@playwo/opencode-cursor-oauth 0.0.0-dev.2c48be2f48c9 → 0.0.0-dev.9a9acda06a80
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.
|
@@ -38,6 +38,8 @@ async function collectFullResponse(payload, accessToken, modelId, convKey, metad
|
|
|
38
38
|
pendingExecs: [],
|
|
39
39
|
outputTokens: 0,
|
|
40
40
|
totalTokens: 0,
|
|
41
|
+
interactionToolArgsText: new Map(),
|
|
42
|
+
emittedToolCallIds: new Set(),
|
|
41
43
|
};
|
|
42
44
|
const tagFilter = createThinkingTagFilter();
|
|
43
45
|
bridge.onData(createConnectFrameParser((messageBytes) => {
|
|
@@ -58,7 +60,7 @@ async function collectFullResponse(payload, accessToken, modelId, convKey, metad
|
|
|
58
60
|
},
|
|
59
61
|
});
|
|
60
62
|
scheduleBridgeEnd(bridge);
|
|
61
|
-
}, (checkpointBytes) => updateConversationCheckpoint(convKey, checkpointBytes), (info) => {
|
|
63
|
+
}, (checkpointBytes) => updateConversationCheckpoint(convKey, checkpointBytes), () => scheduleBridgeEnd(bridge), (info) => {
|
|
62
64
|
endStreamError = new Error(`Cursor requested unsupported exec type: ${info.execCase}`);
|
|
63
65
|
logPluginError("Closing non-streaming Cursor bridge after unsupported exec", {
|
|
64
66
|
modelId,
|
|
@@ -27,6 +27,8 @@ function createBridgeStreamResponse(bridge, heartbeatTimer, blobStore, mcpTools,
|
|
|
27
27
|
pendingExecs: [],
|
|
28
28
|
outputTokens: 0,
|
|
29
29
|
totalTokens: 0,
|
|
30
|
+
interactionToolArgsText: new Map(),
|
|
31
|
+
emittedToolCallIds: new Set(),
|
|
30
32
|
};
|
|
31
33
|
const tagFilter = createThinkingTagFilter();
|
|
32
34
|
let assistantText = metadata.assistantSeedText ?? "";
|
|
@@ -138,7 +140,7 @@ function createBridgeStreamResponse(bridge, heartbeatTimer, blobStore, mcpTools,
|
|
|
138
140
|
sendSSE(makeChunk({}, "tool_calls"));
|
|
139
141
|
sendDone();
|
|
140
142
|
closeController();
|
|
141
|
-
}, (checkpointBytes) => updateConversationCheckpoint(convKey, checkpointBytes), (info) => {
|
|
143
|
+
}, (checkpointBytes) => updateConversationCheckpoint(convKey, checkpointBytes), () => scheduleBridgeEnd(bridge), (info) => {
|
|
142
144
|
endStreamError = new Error(`Cursor requested unsupported exec type: ${info.execCase}`);
|
|
143
145
|
logPluginError("Closing Cursor bridge after unsupported exec", {
|
|
144
146
|
modelId,
|
|
@@ -34,4 +34,4 @@ export declare function computeUsage(state: StreamState): {
|
|
|
34
34
|
completion_tokens: number;
|
|
35
35
|
total_tokens: number;
|
|
36
36
|
};
|
|
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, onUnhandledExec?: (info: UnhandledExecInfo) => void): void;
|
|
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;
|
|
@@ -128,10 +128,10 @@ export function computeUsage(state) {
|
|
|
128
128
|
const prompt_tokens = Math.max(0, total_tokens - completion_tokens);
|
|
129
129
|
return { prompt_tokens, completion_tokens, total_tokens };
|
|
130
130
|
}
|
|
131
|
-
export function processServerMessage(msg, blobStore, mcpTools, sendFrame, state, onText, onMcpExec, onCheckpoint, onUnhandledExec) {
|
|
131
|
+
export function processServerMessage(msg, blobStore, mcpTools, sendFrame, state, onText, onMcpExec, onCheckpoint, onTurnEnded, onUnhandledExec) {
|
|
132
132
|
const msgCase = msg.message.case;
|
|
133
133
|
if (msgCase === "interactionUpdate") {
|
|
134
|
-
handleInteractionUpdate(msg.message.value, state, onText);
|
|
134
|
+
handleInteractionUpdate(msg.message.value, state, onText, onMcpExec, onTurnEnded);
|
|
135
135
|
}
|
|
136
136
|
else if (msgCase === "kvServerMessage") {
|
|
137
137
|
handleKvMessage(msg.message.value, blobStore, sendFrame);
|
|
@@ -149,7 +149,7 @@ export function processServerMessage(msg, blobStore, mcpTools, sendFrame, state,
|
|
|
149
149
|
}
|
|
150
150
|
}
|
|
151
151
|
}
|
|
152
|
-
function handleInteractionUpdate(update, state, onText) {
|
|
152
|
+
function handleInteractionUpdate(update, state, onText, onMcpExec, onTurnEnded) {
|
|
153
153
|
const updateCase = update.message?.case;
|
|
154
154
|
if (updateCase === "textDelta") {
|
|
155
155
|
const delta = update.message.value.text || "";
|
|
@@ -164,10 +164,57 @@ function handleInteractionUpdate(update, state, onText) {
|
|
|
164
164
|
else if (updateCase === "tokenDelta") {
|
|
165
165
|
state.outputTokens += update.message.value.tokens ?? 0;
|
|
166
166
|
}
|
|
167
|
+
else if (updateCase === "partialToolCall") {
|
|
168
|
+
const partial = update.message.value;
|
|
169
|
+
if (partial.callId && partial.argsTextDelta) {
|
|
170
|
+
state.interactionToolArgsText.set(partial.callId, partial.argsTextDelta);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
else if (updateCase === "toolCallCompleted") {
|
|
174
|
+
const exec = decodeInteractionToolCall(update.message.value, state);
|
|
175
|
+
if (exec)
|
|
176
|
+
onMcpExec(exec);
|
|
177
|
+
}
|
|
178
|
+
else if (updateCase === "turnEnded") {
|
|
179
|
+
onTurnEnded?.();
|
|
180
|
+
}
|
|
167
181
|
// toolCallStarted, partialToolCall, toolCallDelta, toolCallCompleted
|
|
168
182
|
// are intentionally ignored. MCP tool calls flow through the exec
|
|
169
183
|
// message path (mcpArgs → mcpResult), not interaction updates.
|
|
170
184
|
}
|
|
185
|
+
function decodeInteractionToolCall(update, state) {
|
|
186
|
+
const callId = update.callId ?? "";
|
|
187
|
+
const toolCase = update.toolCall?.tool?.case;
|
|
188
|
+
if (toolCase !== "mcpToolCall")
|
|
189
|
+
return null;
|
|
190
|
+
const mcpArgs = update.toolCall?.tool?.value?.args;
|
|
191
|
+
if (!mcpArgs)
|
|
192
|
+
return null;
|
|
193
|
+
const toolCallId = mcpArgs.toolCallId || callId || crypto.randomUUID();
|
|
194
|
+
if (state.emittedToolCallIds.has(toolCallId))
|
|
195
|
+
return null;
|
|
196
|
+
const decodedMap = decodeMcpArgsMap(mcpArgs.args ?? {});
|
|
197
|
+
const partialArgsText = callId
|
|
198
|
+
? state.interactionToolArgsText.get(callId)?.trim()
|
|
199
|
+
: undefined;
|
|
200
|
+
let decodedArgs = "{}";
|
|
201
|
+
if (Object.keys(decodedMap).length > 0) {
|
|
202
|
+
decodedArgs = JSON.stringify(decodedMap);
|
|
203
|
+
}
|
|
204
|
+
else if (partialArgsText) {
|
|
205
|
+
decodedArgs = partialArgsText;
|
|
206
|
+
}
|
|
207
|
+
state.emittedToolCallIds.add(toolCallId);
|
|
208
|
+
if (callId)
|
|
209
|
+
state.interactionToolArgsText.delete(callId);
|
|
210
|
+
return {
|
|
211
|
+
execId: callId || toolCallId,
|
|
212
|
+
execMsgId: 0,
|
|
213
|
+
toolCallId,
|
|
214
|
+
toolName: mcpArgs.toolName || mcpArgs.name || "unknown_mcp_tool",
|
|
215
|
+
decodedArgs,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
171
218
|
/** Send a KV client response back to Cursor. */
|
|
172
219
|
function sendKvResponse(kvMsg, messageCase, value, sendFrame) {
|
|
173
220
|
const response = create(KvClientMessageSchema, {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@playwo/opencode-cursor-oauth",
|
|
3
|
-
"version": "0.0.0-dev.
|
|
3
|
+
"version": "0.0.0-dev.9a9acda06a80",
|
|
4
4
|
"description": "OpenCode plugin that connects Cursor's API to OpenCode via OAuth, model discovery, and a local OpenAI-compatible proxy.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|