cursor-opencode-provider 0.1.5 → 0.2.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.
@@ -0,0 +1,23 @@
1
+ export type InteractionQueryWireInfo = {
2
+ id?: number;
3
+ variantField?: number;
4
+ variantName?: string;
5
+ };
6
+ export type HandledInteraction = {
7
+ id: number;
8
+ variantField: number;
9
+ variantName: string;
10
+ outcome: "rejected" | "acknowledged" | "failed";
11
+ reply: Uint8Array;
12
+ };
13
+ export declare class UnsupportedInteractionQueryError extends Error {
14
+ constructor(info: InteractionQueryWireInfo);
15
+ }
16
+ /**
17
+ * Inspect the raw wrapper as well as the decoded object. protobufjs drops
18
+ * fields introduced by newer Cursor schemas; raw inspection lets us fail fast
19
+ * instead of accidentally restoring the heartbeat-only deadlock.
20
+ */
21
+ export declare function inspectInteractionQueryWire(agentServerPayload: Uint8Array): InteractionQueryWireInfo;
22
+ /** Build the immediate typed response required by Cursor's Run RPC. */
23
+ export declare function handleInteractionQuery(query: Record<string, unknown>, agentServerPayload: Uint8Array): HandledInteraction;
@@ -0,0 +1,136 @@
1
+ import { encodeMessage } from "./messages.js";
2
+ import { readAllFields } from "./struct.js";
3
+ const HEADLESS_REASON = "This Cursor interaction requires UI approval and is not available through the OpenCode provider.";
4
+ const variantNames = {
5
+ 2: "web_search_request_query",
6
+ 3: "ask_question_interaction_query",
7
+ 4: "switch_mode_request_query",
8
+ 7: "create_plan_request_query",
9
+ 8: "setup_vm_environment_args",
10
+ 9: "web_fetch_request_query",
11
+ 10: "pr_management_request_query",
12
+ 11: "mcp_auth_request_query",
13
+ 12: "generate_image_request_query",
14
+ 13: "replace_env_args",
15
+ 14: "connect_scm_request_query",
16
+ };
17
+ export class UnsupportedInteractionQueryError extends Error {
18
+ constructor(info) {
19
+ const variant = info.variantField === undefined
20
+ ? "missing variant"
21
+ : `unsupported variant field #${info.variantField}`;
22
+ const id = info.id === undefined ? "missing id" : `id=${info.id}`;
23
+ super(`Cursor interaction query cannot be handled (${id}, ${variant})`);
24
+ this.name = "UnsupportedInteractionQueryError";
25
+ }
26
+ }
27
+ /**
28
+ * Inspect the raw wrapper as well as the decoded object. protobufjs drops
29
+ * fields introduced by newer Cursor schemas; raw inspection lets us fail fast
30
+ * instead of accidentally restoring the heartbeat-only deadlock.
31
+ */
32
+ export function inspectInteractionQueryWire(agentServerPayload) {
33
+ const queryBytes = readAllFields(agentServerPayload)
34
+ .find((field) => field.fn === 7 && field.wt === 2)?.bytes;
35
+ if (!queryBytes)
36
+ return {};
37
+ // InteractionQuery.id is a proto3 uint32. Cursor commonly uses id=0, whose
38
+ // default scalar value is omitted from the wire; absence therefore means
39
+ // zero, not a malformed/missing correlation id.
40
+ let id = 0;
41
+ let variantField;
42
+ for (const field of readAllFields(queryBytes)) {
43
+ if (field.fn === 1 && field.wt === 0)
44
+ id = field.varint;
45
+ else if (field.wt === 2 && variantField === undefined)
46
+ variantField = field.fn;
47
+ }
48
+ return {
49
+ id,
50
+ variantField,
51
+ variantName: variantField === undefined ? undefined : variantNames[variantField],
52
+ };
53
+ }
54
+ /** Build the immediate typed response required by Cursor's Run RPC. */
55
+ export function handleInteractionQuery(query, agentServerPayload) {
56
+ const info = inspectInteractionQueryWire(agentServerPayload);
57
+ if (info.id === undefined || info.variantField === undefined || !info.variantName) {
58
+ throw new UnsupportedInteractionQueryError(info);
59
+ }
60
+ if (typeof query.id === "number" && query.id !== info.id) {
61
+ throw new Error(`Cursor interaction query id mismatch: decoded=${query.id} wire=${info.id}`);
62
+ }
63
+ let response;
64
+ let outcome;
65
+ switch (info.variantField) {
66
+ case 2:
67
+ response = { web_search_request_response: { rejected: { reason: HEADLESS_REASON } } };
68
+ outcome = "rejected";
69
+ break;
70
+ case 3:
71
+ response = {
72
+ ask_question_interaction_response: {
73
+ result: { rejected: { reason: "Questions must be asked through OpenCode's question tool." } },
74
+ },
75
+ };
76
+ outcome = "rejected";
77
+ break;
78
+ case 4:
79
+ response = { switch_mode_request_response: { rejected: { reason: HEADLESS_REASON } } };
80
+ outcome = "rejected";
81
+ break;
82
+ case 7:
83
+ // Cursor CLI's headless fallback acknowledges plan creation without a
84
+ // client-side URI; the plan remains in conversation state/checkpoints.
85
+ response = { create_plan_request_response: { result: { success: {}, plan_uri: "" } } };
86
+ outcome = "acknowledged";
87
+ break;
88
+ case 8:
89
+ // OpenCode owns the local environment; there is no Cursor VM to create.
90
+ response = { setup_vm_environment_result: { success: {} } };
91
+ outcome = "acknowledged";
92
+ break;
93
+ case 9:
94
+ response = { web_fetch_request_response: { rejected: { reason: HEADLESS_REASON } } };
95
+ outcome = "rejected";
96
+ break;
97
+ case 10:
98
+ response = { pr_management_result: { rejected: { reason: HEADLESS_REASON } } };
99
+ outcome = "rejected";
100
+ break;
101
+ case 11:
102
+ response = { mcp_auth_request_response: { rejected: { reason: HEADLESS_REASON } } };
103
+ outcome = "rejected";
104
+ break;
105
+ case 12:
106
+ response = { generate_image_request_response: { rejected: { reason: HEADLESS_REASON } } };
107
+ outcome = "rejected";
108
+ break;
109
+ case 13:
110
+ response = {
111
+ replace_env_result: {
112
+ failure: {
113
+ error_message: "Environment replacement is not supported by the OpenCode provider.",
114
+ setup_logs: "",
115
+ },
116
+ },
117
+ };
118
+ outcome = "failed";
119
+ break;
120
+ case 14:
121
+ response = { connect_scm_request_response: { rejected: { reason: HEADLESS_REASON } } };
122
+ outcome = "rejected";
123
+ break;
124
+ default:
125
+ throw new UnsupportedInteractionQueryError(info);
126
+ }
127
+ return {
128
+ id: info.id,
129
+ variantField: info.variantField,
130
+ variantName: info.variantName,
131
+ outcome,
132
+ reply: encodeMessage("AgentClientMessage", {
133
+ interaction_response: { id: info.id, ...response },
134
+ }),
135
+ };
136
+ }