@playwo/opencode-cursor-oauth 0.0.0-dev.4258a6733133 → 0.0.0-dev.4463bb589222

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,5 +1,4 @@
1
1
  import { type CursorBaseRequestOptions } from "./headers";
2
- export declare function encodeBidiAppendRequest(dataHex: string, requestId: string, appendSeqno: number): Uint8Array;
3
2
  export interface CursorSession {
4
3
  write: (data: Uint8Array) => void;
5
4
  end: () => void;
@@ -8,6 +7,6 @@ export interface CursorSession {
8
7
  readonly alive: boolean;
9
8
  }
10
9
  export interface CreateCursorSessionOptions extends CursorBaseRequestOptions {
11
- requestId: string;
10
+ initialRequestBytes: Uint8Array;
12
11
  }
13
12
  export declare function createCursorSession(options: CreateCursorSessionOptions): Promise<CursorSession>;
@@ -1,149 +1,164 @@
1
- import { create, toBinary } from "@bufbuild/protobuf";
2
- import { BidiRequestIdSchema } from "../proto/agent_pb";
3
- import { CURSOR_API_URL } from "./config";
4
- import { concatBytes, encodeProtoMessageField, encodeProtoStringField, encodeProtoVarintField, frameConnectMessage, toFetchBody, } from "./connect-framing";
5
- import { buildCursorHeaders } from "./headers";
6
- import { errorDetails, logPluginError, logPluginWarn } from "../logger";
7
- export function encodeBidiAppendRequest(dataHex, requestId, appendSeqno) {
8
- const requestIdBytes = toBinary(BidiRequestIdSchema, create(BidiRequestIdSchema, { requestId }));
9
- return concatBytes([
10
- encodeProtoStringField(1, dataHex),
11
- encodeProtoMessageField(2, requestIdBytes),
12
- encodeProtoVarintField(3, appendSeqno),
13
- ]);
14
- }
1
+ import { connect as connectHttp2, } from "node:http2";
2
+ import { CURSOR_API_URL, CURSOR_CONNECT_PROTOCOL_VERSION } from "./config";
3
+ import { frameConnectMessage } from "./connect-framing";
4
+ import { buildCursorHeaderValues, } from "./headers";
5
+ import { errorDetails, logPluginError } from "../logger";
6
+ const CURSOR_BIDI_RUN_PATH = "/agent.v1.AgentService/Run";
15
7
  export async function createCursorSession(options) {
16
- const response = await fetch(new URL("/agent.v1.AgentService/RunSSE", options.url ?? CURSOR_API_URL), {
17
- method: "POST",
18
- headers: buildCursorHeaders(options, "application/connect+proto", {
19
- accept: "text/event-stream",
20
- "connect-protocol-version": "1",
21
- }),
22
- body: toFetchBody(frameConnectMessage(toBinary(BidiRequestIdSchema, create(BidiRequestIdSchema, { requestId: options.requestId })))),
23
- });
24
- if (!response.ok || !response.body) {
25
- const errorBody = await response.text().catch(() => "");
26
- logPluginError("Cursor RunSSE request failed", {
27
- requestId: options.requestId,
28
- status: response.status,
29
- responseBody: errorBody,
30
- });
31
- throw new Error(`RunSSE failed: ${response.status}${errorBody ? ` ${errorBody}` : ""}`);
8
+ if (options.initialRequestBytes.length === 0) {
9
+ throw new Error("Cursor sessions require an initial request message");
32
10
  }
33
- const cbs = {
34
- data: null,
35
- close: null,
36
- };
37
- const abortController = new AbortController();
38
- const reader = response.body.getReader();
39
- let appendSeqno = 0;
40
- let alive = true;
41
- let closeCode = 0;
42
- let writeChain = Promise.resolve();
43
- const pendingChunks = [];
44
- const finish = (code) => {
45
- if (!alive)
46
- return;
47
- alive = false;
48
- closeCode = code;
49
- cbs.close?.(code);
50
- };
51
- const append = async (data) => {
52
- const requestBody = encodeBidiAppendRequest(Buffer.from(data).toString("hex"), options.requestId, appendSeqno++);
53
- const appendResponse = await fetch(new URL("/aiserver.v1.BidiService/BidiAppend", options.url ?? CURSOR_API_URL), {
54
- method: "POST",
55
- headers: buildCursorHeaders(options, "application/proto"),
56
- body: toFetchBody(requestBody),
57
- signal: abortController.signal,
58
- });
59
- if (!appendResponse.ok) {
60
- const errorBody = await appendResponse.text().catch(() => "");
61
- logPluginError("Cursor BidiAppend request failed", {
62
- requestId: options.requestId,
63
- appendSeqno: appendSeqno - 1,
64
- status: appendResponse.status,
65
- responseBody: errorBody,
66
- });
67
- throw new Error(`BidiAppend failed: ${appendResponse.status}${errorBody ? ` ${errorBody}` : ""}`);
68
- }
69
- await appendResponse.arrayBuffer().catch(() => undefined);
70
- };
71
- (async () => {
72
- try {
73
- while (true) {
74
- const { done, value } = await reader.read();
75
- if (done) {
76
- finish(0);
77
- break;
78
- }
79
- if (value && value.length > 0) {
80
- const chunk = Buffer.from(value);
81
- if (cbs.data) {
82
- cbs.data(chunk);
83
- }
84
- else {
85
- pendingChunks.push(chunk);
86
- }
87
- }
11
+ const target = new URL(CURSOR_BIDI_RUN_PATH, options.url ?? CURSOR_API_URL);
12
+ const authority = `${target.protocol}//${target.host}`;
13
+ const requestId = crypto.randomUUID();
14
+ return new Promise((resolve, reject) => {
15
+ const cbs = {
16
+ data: null,
17
+ close: null,
18
+ };
19
+ let session;
20
+ let stream;
21
+ let alive = true;
22
+ let closeCode = 0;
23
+ let opened = false;
24
+ let settled = false;
25
+ let statusCode = 0;
26
+ const pendingChunks = [];
27
+ const errorChunks = [];
28
+ const closeTransport = () => {
29
+ try {
30
+ stream?.close();
88
31
  }
89
- }
90
- catch (error) {
91
- logPluginWarn("Cursor stream reader closed with error", {
92
- requestId: options.requestId,
32
+ catch { }
33
+ try {
34
+ session?.close();
35
+ }
36
+ catch { }
37
+ };
38
+ const finish = (code) => {
39
+ if (!alive)
40
+ return;
41
+ alive = false;
42
+ closeCode = code;
43
+ cbs.close?.(code);
44
+ closeTransport();
45
+ };
46
+ const rejectOpen = (error) => {
47
+ if (settled)
48
+ return;
49
+ settled = true;
50
+ alive = false;
51
+ closeTransport();
52
+ reject(error);
53
+ };
54
+ const resolveOpen = (sessionHandle) => {
55
+ if (settled)
56
+ return;
57
+ settled = true;
58
+ opened = true;
59
+ resolve(sessionHandle);
60
+ };
61
+ const handleTransportError = (message, error) => {
62
+ logPluginError(message, {
63
+ requestId,
64
+ url: target.toString(),
93
65
  ...errorDetails(error),
94
66
  });
95
- finish(alive ? 1 : closeCode);
96
- }
97
- })();
98
- return {
99
- get alive() {
100
- return alive;
101
- },
102
- write(data) {
103
- if (!alive)
67
+ if (!opened) {
68
+ rejectOpen(new Error(error instanceof Error ? error.message : String(error ?? message)));
104
69
  return;
105
- writeChain = writeChain
106
- .then(() => append(data))
107
- .catch((error) => {
108
- logPluginError("Cursor stream append failed", {
109
- requestId: options.requestId,
110
- ...errorDetails(error),
111
- });
70
+ }
71
+ finish(1);
72
+ };
73
+ const sessionHandle = {
74
+ get alive() {
75
+ return alive;
76
+ },
77
+ write(data) {
78
+ if (!alive || !stream)
79
+ return;
112
80
  try {
113
- abortController.abort();
81
+ stream.write(frameConnectMessage(data));
114
82
  }
115
- catch { }
116
- try {
117
- reader.cancel();
83
+ catch (error) {
84
+ handleTransportError("Cursor HTTP/2 write failed", error);
85
+ }
86
+ },
87
+ end() {
88
+ finish(0);
89
+ },
90
+ onData(cb) {
91
+ cbs.data = cb;
92
+ while (pendingChunks.length > 0) {
93
+ cb(pendingChunks.shift());
94
+ }
95
+ },
96
+ onClose(cb) {
97
+ if (!alive) {
98
+ queueMicrotask(() => cb(closeCode));
118
99
  }
119
- catch { }
120
- finish(1);
100
+ else {
101
+ cbs.close = cb;
102
+ }
103
+ },
104
+ };
105
+ try {
106
+ session = connectHttp2(authority);
107
+ session.once("error", (error) => {
108
+ handleTransportError("Cursor HTTP/2 session failed", error);
121
109
  });
122
- },
123
- end() {
124
- try {
125
- abortController.abort();
126
- }
127
- catch { }
128
- try {
129
- reader.cancel();
130
- }
131
- catch { }
132
- finish(0);
133
- },
134
- onData(cb) {
135
- cbs.data = cb;
136
- while (pendingChunks.length > 0) {
137
- cb(pendingChunks.shift());
138
- }
139
- },
140
- onClose(cb) {
141
- if (!alive) {
142
- queueMicrotask(() => cb(closeCode));
143
- }
144
- else {
145
- cbs.close = cb;
146
- }
147
- },
148
- };
110
+ const headers = {
111
+ ":method": "POST",
112
+ ":path": `${target.pathname}${target.search}`,
113
+ ...buildCursorHeaderValues(options, "application/connect+proto", {
114
+ accept: "application/connect+proto",
115
+ "connect-protocol-version": CURSOR_CONNECT_PROTOCOL_VERSION,
116
+ }),
117
+ };
118
+ stream = session.request(headers);
119
+ stream.once("response", (responseHeaders) => {
120
+ const statusHeader = responseHeaders[":status"];
121
+ statusCode =
122
+ typeof statusHeader === "number"
123
+ ? statusHeader
124
+ : Number(statusHeader ?? 0);
125
+ if (statusCode >= 200 && statusCode < 300) {
126
+ resolveOpen(sessionHandle);
127
+ }
128
+ });
129
+ stream.on("data", (chunk) => {
130
+ const buffer = Buffer.from(chunk);
131
+ if (!opened && statusCode >= 400) {
132
+ errorChunks.push(buffer);
133
+ return;
134
+ }
135
+ if (cbs.data) {
136
+ cbs.data(buffer);
137
+ }
138
+ else {
139
+ pendingChunks.push(buffer);
140
+ }
141
+ });
142
+ stream.once("end", () => {
143
+ if (!opened) {
144
+ const errorBody = Buffer.concat(errorChunks).toString("utf8").trim();
145
+ logPluginError("Cursor HTTP/2 Run request failed", {
146
+ requestId,
147
+ status: statusCode,
148
+ responseBody: errorBody,
149
+ });
150
+ rejectOpen(new Error(`Run failed: ${statusCode || 1}${errorBody ? ` ${errorBody}` : ""}`));
151
+ return;
152
+ }
153
+ finish(statusCode >= 200 && statusCode < 300 ? 0 : statusCode || 1);
154
+ });
155
+ stream.once("error", (error) => {
156
+ handleTransportError("Cursor HTTP/2 stream failed", error);
157
+ });
158
+ stream.write(frameConnectMessage(options.initialRequestBytes));
159
+ }
160
+ catch (error) {
161
+ handleTransportError("Cursor HTTP/2 transport setup failed", error);
162
+ }
163
+ });
149
164
  }
@@ -1,5 +1,5 @@
1
1
  export { CURSOR_API_URL, CURSOR_CLIENT_VERSION, CURSOR_CONNECT_PROTOCOL_VERSION, CONNECT_END_STREAM_FLAG, } from "./config";
2
2
  export { concatBytes, decodeConnectUnaryBody, encodeProtoMessageField, encodeProtoStringField, encodeProtoVarintField, encodeVarint, frameConnectMessage, toFetchBody, } from "./connect-framing";
3
3
  export { buildCursorHeaders, buildCursorHeaderValues, type CursorBaseRequestOptions, } from "./headers";
4
- export { createCursorSession, encodeBidiAppendRequest, type CreateCursorSessionOptions, type CursorSession, } from "./bidi-session";
4
+ export { createCursorSession, type CreateCursorSessionOptions, type CursorSession, } from "./bidi-session";
5
5
  export { callCursorUnaryRpc, type CursorUnaryRpcOptions } from "./unary-rpc";
@@ -1,5 +1,5 @@
1
1
  export { CURSOR_API_URL, CURSOR_CLIENT_VERSION, CURSOR_CONNECT_PROTOCOL_VERSION, CONNECT_END_STREAM_FLAG, } from "./config";
2
2
  export { concatBytes, decodeConnectUnaryBody, encodeProtoMessageField, encodeProtoStringField, encodeProtoVarintField, encodeVarint, frameConnectMessage, toFetchBody, } from "./connect-framing";
3
3
  export { buildCursorHeaders, buildCursorHeaderValues, } from "./headers";
4
- export { createCursorSession, encodeBidiAppendRequest, } from "./bidi-session";
4
+ export { createCursorSession, } from "./bidi-session";
5
5
  export { callCursorUnaryRpc } from "./unary-rpc";
@@ -4,7 +4,6 @@ export interface CursorUnaryRpcOptions {
4
4
  requestBody: Uint8Array;
5
5
  url?: string;
6
6
  timeoutMs?: number;
7
- transport?: "auto" | "fetch" | "http2";
8
7
  }
9
8
  export declare function callCursorUnaryRpc(options: CursorUnaryRpcOptions): Promise<{
10
9
  body: Uint8Array;
@@ -1,67 +1,10 @@
1
1
  import { connect as connectHttp2, } from "node:http2";
2
2
  import { CURSOR_API_URL, CURSOR_CONNECT_PROTOCOL_VERSION } from "./config";
3
- import { toFetchBody } from "./connect-framing";
4
- import { buildCursorHeaders, buildCursorHeaderValues } from "./headers";
3
+ import { buildCursorHeaderValues } from "./headers";
5
4
  import { errorDetails, logPluginError } from "../logger";
6
5
  export async function callCursorUnaryRpc(options) {
7
6
  const target = new URL(options.rpcPath, options.url ?? CURSOR_API_URL);
8
- const transport = options.transport ?? "auto";
9
- if (transport === "http2" ||
10
- (transport === "auto" && target.protocol === "https:")) {
11
- const http2Result = await callCursorUnaryRpcOverHttp2(options, target);
12
- if (transport === "http2" ||
13
- http2Result.timedOut ||
14
- http2Result.exitCode !== 1) {
15
- return http2Result;
16
- }
17
- }
18
- return callCursorUnaryRpcOverFetch(options, target);
19
- }
20
- async function callCursorUnaryRpcOverFetch(options, target) {
21
- let timedOut = false;
22
- const timeoutMs = options.timeoutMs ?? 5_000;
23
- const controller = new AbortController();
24
- const timeout = timeoutMs > 0
25
- ? setTimeout(() => {
26
- timedOut = true;
27
- controller.abort();
28
- }, timeoutMs)
29
- : undefined;
30
- try {
31
- const response = await fetch(target, {
32
- method: "POST",
33
- headers: buildCursorHeaders(options, "application/proto", {
34
- accept: "application/proto, application/json",
35
- "connect-protocol-version": CURSOR_CONNECT_PROTOCOL_VERSION,
36
- "connect-timeout-ms": String(timeoutMs),
37
- }),
38
- body: toFetchBody(options.requestBody),
39
- signal: controller.signal,
40
- });
41
- const body = new Uint8Array(await response.arrayBuffer());
42
- return {
43
- body,
44
- exitCode: response.ok ? 0 : response.status,
45
- timedOut,
46
- };
47
- }
48
- catch {
49
- logPluginError("Cursor unary fetch transport failed", {
50
- rpcPath: options.rpcPath,
51
- url: target.toString(),
52
- timeoutMs,
53
- timedOut,
54
- });
55
- return {
56
- body: new Uint8Array(),
57
- exitCode: timedOut ? 124 : 1,
58
- timedOut,
59
- };
60
- }
61
- finally {
62
- if (timeout)
63
- clearTimeout(timeout);
64
- }
7
+ return callCursorUnaryRpcOverHttp2(options, target);
65
8
  }
66
9
  async function callCursorUnaryRpcOverHttp2(options, target) {
67
10
  const timeoutMs = options.timeoutMs ?? 5_000;
@@ -2,12 +2,10 @@ import { createCursorSession } from "../cursor/bidi-session";
2
2
  import { makeHeartbeatBytes } from "./stream-dispatch";
3
3
  const HEARTBEAT_INTERVAL_MS = 5_000;
4
4
  export async function startBridge(accessToken, requestBytes) {
5
- const requestId = crypto.randomUUID();
6
5
  const bridge = await createCursorSession({
7
6
  accessToken,
8
- requestId,
7
+ initialRequestBytes: requestBytes,
9
8
  });
10
- bridge.write(requestBytes);
11
9
  const heartbeatTimer = setInterval(() => bridge.write(makeHeartbeatBytes()), HEARTBEAT_INTERVAL_MS);
12
10
  return { bridge, heartbeatTimer };
13
11
  }
@@ -1,5 +1,5 @@
1
1
  import { create, toBinary } from "@bufbuild/protobuf";
2
- import { AgentClientMessageSchema, ClientHeartbeatSchema, ConversationStateStructureSchema, BackgroundShellSpawnResultSchema, DeleteResultSchema, DeleteRejectedSchema, DiagnosticsResultSchema, ExecClientMessageSchema, FetchErrorSchema, FetchResultSchema, GetBlobResultSchema, GrepErrorSchema, GrepResultSchema, KvClientMessageSchema, LsRejectedSchema, LsResultSchema, McpResultSchema, ReadRejectedSchema, ReadResultSchema, RequestContextResultSchema, RequestContextSchema, RequestContextSuccessSchema, SetBlobResultSchema, ShellRejectedSchema, ShellResultSchema, WriteRejectedSchema, WriteResultSchema, WriteShellStdinErrorSchema, WriteShellStdinResultSchema, } from "../proto/agent_pb";
2
+ import { AgentClientMessageSchema, AskQuestionInteractionResponseSchema, AskQuestionRejectedSchema, AskQuestionResultSchema, ClientHeartbeatSchema, ConversationStateStructureSchema, BackgroundShellSpawnResultSchema, CreatePlanErrorSchema, CreatePlanRequestResponseSchema, CreatePlanResultSchema, DeleteResultSchema, DeleteRejectedSchema, DiagnosticsResultSchema, ExecClientMessageSchema, ExaFetchRequestResponseSchema, ExaFetchRequestResponse_RejectedSchema, ExaSearchRequestResponseSchema, ExaSearchRequestResponse_RejectedSchema, FetchErrorSchema, FetchResultSchema, GetBlobResultSchema, GrepErrorSchema, GrepResultSchema, InteractionResponseSchema, KvClientMessageSchema, LsRejectedSchema, LsResultSchema, McpResultSchema, ReadRejectedSchema, ReadResultSchema, RequestContextResultSchema, RequestContextSchema, RequestContextSuccessSchema, SetBlobResultSchema, ShellRejectedSchema, ShellResultSchema, SwitchModeRequestResponseSchema, SwitchModeRequestResponse_RejectedSchema, WebSearchRequestResponseSchema, WebSearchRequestResponse_RejectedSchema, WriteRejectedSchema, WriteResultSchema, WriteShellStdinErrorSchema, WriteShellStdinResultSchema, } from "../proto/agent_pb";
3
3
  import { CONNECT_END_STREAM_FLAG } from "../cursor/config";
4
4
  import { logPluginError, logPluginWarn } from "../logger";
5
5
  import { decodeMcpArgsMap } from "../openai/tools";
@@ -146,10 +146,7 @@ export function processServerMessage(msg, blobStore, mcpTools, sendFrame, state,
146
146
  });
147
147
  }
148
148
  else if (msgCase === "interactionQuery") {
149
- onUnsupportedMessage?.({
150
- category: "interactionQuery",
151
- caseName: msg.message.value.query.case ?? "undefined",
152
- });
149
+ handleInteractionQuery(msg.message.value, sendFrame, onUnsupportedMessage);
153
150
  }
154
151
  else if (msgCase === "conversationCheckpointUpdate") {
155
152
  const stateStructure = msg.message.value;
@@ -252,6 +249,90 @@ function decodeInteractionToolCall(update, state) {
252
249
  decodedArgs,
253
250
  };
254
251
  }
252
+ function handleInteractionQuery(query, sendFrame, onUnsupportedMessage) {
253
+ const queryCase = query.query.case;
254
+ if (queryCase === "webSearchRequestQuery") {
255
+ const response = create(WebSearchRequestResponseSchema, {
256
+ result: {
257
+ case: "rejected",
258
+ value: create(WebSearchRequestResponse_RejectedSchema, {
259
+ reason: "Native Cursor web search is not available in this environment. Use the provided MCP tool `websearch` instead.",
260
+ }),
261
+ },
262
+ });
263
+ sendInteractionResponse(query.id, "webSearchRequestResponse", response, sendFrame);
264
+ return;
265
+ }
266
+ if (queryCase === "askQuestionInteractionQuery") {
267
+ const response = create(AskQuestionInteractionResponseSchema, {
268
+ result: create(AskQuestionResultSchema, {
269
+ result: {
270
+ case: "rejected",
271
+ value: create(AskQuestionRejectedSchema, {
272
+ reason: "Native Cursor question prompts are not available in this environment. Use the provided MCP tool `question` instead.",
273
+ }),
274
+ },
275
+ }),
276
+ });
277
+ sendInteractionResponse(query.id, "askQuestionInteractionResponse", response, sendFrame);
278
+ return;
279
+ }
280
+ if (queryCase === "switchModeRequestQuery") {
281
+ const response = create(SwitchModeRequestResponseSchema, {
282
+ result: {
283
+ case: "rejected",
284
+ value: create(SwitchModeRequestResponse_RejectedSchema, {
285
+ reason: "Cursor mode switching is not available in this environment. Continue using the current agent and the provided MCP tools.",
286
+ }),
287
+ },
288
+ });
289
+ sendInteractionResponse(query.id, "switchModeRequestResponse", response, sendFrame);
290
+ return;
291
+ }
292
+ if (queryCase === "exaSearchRequestQuery") {
293
+ const response = create(ExaSearchRequestResponseSchema, {
294
+ result: {
295
+ case: "rejected",
296
+ value: create(ExaSearchRequestResponse_RejectedSchema, {
297
+ reason: "Native Cursor Exa search is not available in this environment. Use the provided MCP tool `websearch` instead.",
298
+ }),
299
+ },
300
+ });
301
+ sendInteractionResponse(query.id, "exaSearchRequestResponse", response, sendFrame);
302
+ return;
303
+ }
304
+ if (queryCase === "exaFetchRequestQuery") {
305
+ const response = create(ExaFetchRequestResponseSchema, {
306
+ result: {
307
+ case: "rejected",
308
+ value: create(ExaFetchRequestResponse_RejectedSchema, {
309
+ reason: "Native Cursor Exa fetch is not available in this environment. Use the provided MCP tools `websearch` and `webfetch` instead.",
310
+ }),
311
+ },
312
+ });
313
+ sendInteractionResponse(query.id, "exaFetchRequestResponse", response, sendFrame);
314
+ return;
315
+ }
316
+ if (queryCase === "createPlanRequestQuery") {
317
+ const response = create(CreatePlanRequestResponseSchema, {
318
+ result: create(CreatePlanResultSchema, {
319
+ planUri: "",
320
+ result: {
321
+ case: "error",
322
+ value: create(CreatePlanErrorSchema, {
323
+ error: "Native Cursor plan creation is not available in this environment. Use the provided MCP planning tools instead.",
324
+ }),
325
+ },
326
+ }),
327
+ });
328
+ sendInteractionResponse(query.id, "createPlanRequestResponse", response, sendFrame);
329
+ return;
330
+ }
331
+ onUnsupportedMessage?.({
332
+ category: "interactionQuery",
333
+ caseName: queryCase ?? "undefined",
334
+ });
335
+ }
255
336
  /** Send a KV client response back to Cursor. */
256
337
  function sendKvResponse(kvMsg, messageCase, value, sendFrame) {
257
338
  const response = create(KvClientMessageSchema, {
@@ -263,6 +344,16 @@ function sendKvResponse(kvMsg, messageCase, value, sendFrame) {
263
344
  });
264
345
  sendFrame(toBinary(AgentClientMessageSchema, clientMsg));
265
346
  }
347
+ function sendInteractionResponse(queryId, messageCase, value, sendFrame) {
348
+ const response = create(InteractionResponseSchema, {
349
+ id: queryId,
350
+ result: { case: messageCase, value: value },
351
+ });
352
+ const clientMessage = create(AgentClientMessageSchema, {
353
+ message: { case: "interactionResponse", value: response },
354
+ });
355
+ sendFrame(toBinary(AgentClientMessageSchema, clientMessage));
356
+ }
266
357
  function handleKvMessage(kvMsg, blobStore, sendFrame) {
267
358
  const kvCase = kvMsg.message.case;
268
359
  if (kvCase === "getBlobArgs") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@playwo/opencode-cursor-oauth",
3
- "version": "0.0.0-dev.4258a6733133",
3
+ "version": "0.0.0-dev.4463bb589222",
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",