opencode-cursor-proxy 1.0.1
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.
- package/LICENSE +21 -0
- package/README.md +139 -0
- package/README.zh-CN.md +136 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/api/agent-service.d.ts +136 -0
- package/dist/lib/api/agent-service.js +938 -0
- package/dist/lib/api/agent-service.js.map +1 -0
- package/dist/lib/api/ai-service.d.ts +26 -0
- package/dist/lib/api/ai-service.js +38 -0
- package/dist/lib/api/ai-service.js.map +1 -0
- package/dist/lib/api/cursor-client.d.ts +119 -0
- package/dist/lib/api/cursor-client.js +511 -0
- package/dist/lib/api/cursor-client.js.map +1 -0
- package/dist/lib/api/cursor-models.d.ts +13 -0
- package/dist/lib/api/cursor-models.js +34 -0
- package/dist/lib/api/cursor-models.js.map +1 -0
- package/dist/lib/api/openai-compat.d.ts +10 -0
- package/dist/lib/api/openai-compat.js +262 -0
- package/dist/lib/api/openai-compat.js.map +1 -0
- package/dist/lib/api/proto/agent-messages.d.ts +25 -0
- package/dist/lib/api/proto/agent-messages.js +132 -0
- package/dist/lib/api/proto/agent-messages.js.map +1 -0
- package/dist/lib/api/proto/bidi.d.ts +17 -0
- package/dist/lib/api/proto/bidi.js +24 -0
- package/dist/lib/api/proto/bidi.js.map +1 -0
- package/dist/lib/api/proto/decoding.d.ts +19 -0
- package/dist/lib/api/proto/decoding.js +118 -0
- package/dist/lib/api/proto/decoding.js.map +1 -0
- package/dist/lib/api/proto/encoding.d.ts +64 -0
- package/dist/lib/api/proto/encoding.js +180 -0
- package/dist/lib/api/proto/encoding.js.map +1 -0
- package/dist/lib/api/proto/exec.d.ts +12 -0
- package/dist/lib/api/proto/exec.js +383 -0
- package/dist/lib/api/proto/exec.js.map +1 -0
- package/dist/lib/api/proto/index.d.ts +13 -0
- package/dist/lib/api/proto/index.js +10 -0
- package/dist/lib/api/proto/index.js.map +1 -0
- package/dist/lib/api/proto/interaction.d.ts +15 -0
- package/dist/lib/api/proto/interaction.js +99 -0
- package/dist/lib/api/proto/interaction.js.map +1 -0
- package/dist/lib/api/proto/kv.d.ts +52 -0
- package/dist/lib/api/proto/kv.js +156 -0
- package/dist/lib/api/proto/kv.js.map +1 -0
- package/dist/lib/api/proto/tool-calls.d.ts +9 -0
- package/dist/lib/api/proto/tool-calls.js +144 -0
- package/dist/lib/api/proto/tool-calls.js.map +1 -0
- package/dist/lib/api/proto/types.d.ts +201 -0
- package/dist/lib/api/proto/types.js +10 -0
- package/dist/lib/api/proto/types.js.map +1 -0
- package/dist/lib/auth/helpers.d.ts +40 -0
- package/dist/lib/auth/helpers.js +103 -0
- package/dist/lib/auth/helpers.js.map +1 -0
- package/dist/lib/auth/index.d.ts +7 -0
- package/dist/lib/auth/index.js +10 -0
- package/dist/lib/auth/index.js.map +1 -0
- package/dist/lib/auth/login.d.ts +55 -0
- package/dist/lib/auth/login.js +184 -0
- package/dist/lib/auth/login.js.map +1 -0
- package/dist/lib/config.d.ts +153 -0
- package/dist/lib/config.js +182 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/openai-compat/handler.d.ts +40 -0
- package/dist/lib/openai-compat/handler.js +808 -0
- package/dist/lib/openai-compat/handler.js.map +1 -0
- package/dist/lib/openai-compat/index.d.ts +9 -0
- package/dist/lib/openai-compat/index.js +13 -0
- package/dist/lib/openai-compat/index.js.map +1 -0
- package/dist/lib/openai-compat/types.d.ts +127 -0
- package/dist/lib/openai-compat/types.js +6 -0
- package/dist/lib/openai-compat/types.js.map +1 -0
- package/dist/lib/openai-compat/utils.d.ts +143 -0
- package/dist/lib/openai-compat/utils.js +348 -0
- package/dist/lib/openai-compat/utils.js.map +1 -0
- package/dist/lib/session-reuse.d.ts +88 -0
- package/dist/lib/session-reuse.js +198 -0
- package/dist/lib/session-reuse.js.map +1 -0
- package/dist/lib/storage.d.ts +55 -0
- package/dist/lib/storage.js +159 -0
- package/dist/lib/storage.js.map +1 -0
- package/dist/lib/utils/cache.d.ts +131 -0
- package/dist/lib/utils/cache.js +297 -0
- package/dist/lib/utils/cache.js.map +1 -0
- package/dist/lib/utils/fetch.d.ts +84 -0
- package/dist/lib/utils/fetch.js +261 -0
- package/dist/lib/utils/fetch.js.map +1 -0
- package/dist/lib/utils/index.d.ts +13 -0
- package/dist/lib/utils/index.js +22 -0
- package/dist/lib/utils/index.js.map +1 -0
- package/dist/lib/utils/jwt.d.ts +40 -0
- package/dist/lib/utils/jwt.js +102 -0
- package/dist/lib/utils/jwt.js.map +1 -0
- package/dist/lib/utils/logger.d.ts +107 -0
- package/dist/lib/utils/logger.js +227 -0
- package/dist/lib/utils/logger.js.map +1 -0
- package/dist/lib/utils/model-resolver.d.ts +49 -0
- package/dist/lib/utils/model-resolver.js +503 -0
- package/dist/lib/utils/model-resolver.js.map +1 -0
- package/dist/lib/utils/request-pool.d.ts +38 -0
- package/dist/lib/utils/request-pool.js +105 -0
- package/dist/lib/utils/request-pool.js.map +1 -0
- package/dist/lib/utils/request-transformer.d.ts +87 -0
- package/dist/lib/utils/request-transformer.js +154 -0
- package/dist/lib/utils/request-transformer.js.map +1 -0
- package/dist/lib/utils/tokenizer.d.ts +14 -0
- package/dist/lib/utils/tokenizer.js +76 -0
- package/dist/lib/utils/tokenizer.js.map +1 -0
- package/dist/plugin/index.d.ts +8 -0
- package/dist/plugin/index.js +9 -0
- package/dist/plugin/index.js.map +1 -0
- package/dist/plugin/plugin.d.ts +21 -0
- package/dist/plugin/plugin.js +309 -0
- package/dist/plugin/plugin.js.map +1 -0
- package/dist/plugin/types.d.ts +120 -0
- package/dist/plugin/types.js +7 -0
- package/dist/plugin/types.js.map +1 -0
- package/dist/server.d.ts +15 -0
- package/dist/server.js +95 -0
- package/dist/server.js.map +1 -0
- package/package.json +79 -0
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenAI-Compatible API Utilities
|
|
3
|
+
*
|
|
4
|
+
* Shared utility functions for OpenAI API compatibility layer.
|
|
5
|
+
*/
|
|
6
|
+
import { randomUUID } from "node:crypto";
|
|
7
|
+
/**
|
|
8
|
+
* Generate a unique completion ID
|
|
9
|
+
*/
|
|
10
|
+
export function generateCompletionId() {
|
|
11
|
+
return `chatcmpl-${randomUUID().replace(/-/g, "").slice(0, 24)}`;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Check if content contains multimodal (image) parts
|
|
15
|
+
*/
|
|
16
|
+
export function hasMultimodalContent(content) {
|
|
17
|
+
if (content === null || typeof content === "string") {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
return content.some((part) => part.type === "image_url");
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Process multimodal content into separate text and image parts
|
|
24
|
+
*
|
|
25
|
+
* Handles:
|
|
26
|
+
* - Plain string content
|
|
27
|
+
* - Array of text/image parts
|
|
28
|
+
* - Base64 data URLs
|
|
29
|
+
* - Regular image URLs
|
|
30
|
+
*/
|
|
31
|
+
export function processMultimodalContent(content) {
|
|
32
|
+
if (content === null) {
|
|
33
|
+
return { text: "", images: [], hasImages: false };
|
|
34
|
+
}
|
|
35
|
+
if (typeof content === "string") {
|
|
36
|
+
return { text: content, images: [], hasImages: false };
|
|
37
|
+
}
|
|
38
|
+
const texts = [];
|
|
39
|
+
const images = [];
|
|
40
|
+
for (const part of content) {
|
|
41
|
+
if (part.type === "text") {
|
|
42
|
+
texts.push(part.text);
|
|
43
|
+
}
|
|
44
|
+
else if (part.type === "image_url") {
|
|
45
|
+
const imagePart = part;
|
|
46
|
+
const url = imagePart.image_url.url;
|
|
47
|
+
const isBase64 = url.startsWith("data:");
|
|
48
|
+
let mimeType;
|
|
49
|
+
if (isBase64) {
|
|
50
|
+
// Extract MIME type from data URL: data:image/png;base64,...
|
|
51
|
+
const match = url.match(/^data:([^;]+);/);
|
|
52
|
+
mimeType = match?.[1];
|
|
53
|
+
}
|
|
54
|
+
images.push({
|
|
55
|
+
url,
|
|
56
|
+
detail: imagePart.image_url.detail,
|
|
57
|
+
isBase64,
|
|
58
|
+
mimeType,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
text: texts.join("\n"),
|
|
64
|
+
images,
|
|
65
|
+
hasImages: images.length > 0,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Extract only text content from potentially multimodal content
|
|
70
|
+
*/
|
|
71
|
+
function extractTextContent(content) {
|
|
72
|
+
if (content === null)
|
|
73
|
+
return "";
|
|
74
|
+
if (typeof content === "string")
|
|
75
|
+
return content;
|
|
76
|
+
return content
|
|
77
|
+
.filter((part) => part.type === "text")
|
|
78
|
+
.map(part => part.text)
|
|
79
|
+
.join("\n");
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Format image references for text-based prompts
|
|
83
|
+
* When images can't be directly passed, include a description
|
|
84
|
+
*/
|
|
85
|
+
export function formatImageReferences(images) {
|
|
86
|
+
if (images.length === 0)
|
|
87
|
+
return "";
|
|
88
|
+
const refs = images.map((img, i) => {
|
|
89
|
+
const source = img.isBase64
|
|
90
|
+
? `[embedded ${img.mimeType || "image"}]`
|
|
91
|
+
: `[image: ${img.url}]`;
|
|
92
|
+
const detail = img.detail ? ` (detail: ${img.detail})` : "";
|
|
93
|
+
return `Image ${i + 1}: ${source}${detail}`;
|
|
94
|
+
});
|
|
95
|
+
return refs.join("\n");
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Convert OpenAI messages array to a prompt string for Cursor.
|
|
99
|
+
* Handles the full message history including:
|
|
100
|
+
* - system messages (prepended)
|
|
101
|
+
* - user messages (with optional multimodal content)
|
|
102
|
+
* - assistant messages (including those with tool_calls)
|
|
103
|
+
* - tool result messages (role: "tool")
|
|
104
|
+
*
|
|
105
|
+
* For multi-turn conversations with tool calls, this formats the conversation
|
|
106
|
+
* so the model can see what tools were called and their results.
|
|
107
|
+
*
|
|
108
|
+
* For multimodal content:
|
|
109
|
+
* - Extracts text content for the prompt
|
|
110
|
+
* - Collects images separately for models that support vision
|
|
111
|
+
* - Optionally adds image references for non-vision models
|
|
112
|
+
*/
|
|
113
|
+
export function messagesToPrompt(messages, options = {}) {
|
|
114
|
+
const result = messagesToPromptWithImages(messages, options);
|
|
115
|
+
return result.prompt;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Convert OpenAI messages to prompt with image extraction
|
|
119
|
+
* Returns both the prompt and any extracted images for vision models
|
|
120
|
+
*/
|
|
121
|
+
export function messagesToPromptWithImages(messages, options = {}) {
|
|
122
|
+
const { supportsVision = false, includeImageReferences = true } = options;
|
|
123
|
+
const parts = [];
|
|
124
|
+
const allImages = [];
|
|
125
|
+
// Extract system messages to prepend
|
|
126
|
+
const systemMessages = messages.filter(m => m.role === "system");
|
|
127
|
+
if (systemMessages.length > 0) {
|
|
128
|
+
parts.push(systemMessages.map(m => extractTextContent(m.content)).join("\n"));
|
|
129
|
+
}
|
|
130
|
+
// Process non-system messages in order
|
|
131
|
+
const conversationMessages = messages.filter(m => m.role !== "system");
|
|
132
|
+
// Check if this is a continuation with tool results
|
|
133
|
+
const hasToolResults = conversationMessages.some(m => m.role === "tool");
|
|
134
|
+
// Format the full conversation history
|
|
135
|
+
for (const msg of conversationMessages) {
|
|
136
|
+
if (msg.role === "user") {
|
|
137
|
+
// Process potentially multimodal user messages
|
|
138
|
+
const multimodal = processMultimodalContent(msg.content);
|
|
139
|
+
let userContent = multimodal.text;
|
|
140
|
+
if (multimodal.hasImages) {
|
|
141
|
+
// Collect images for vision-capable models
|
|
142
|
+
allImages.push(...multimodal.images);
|
|
143
|
+
// Add image references for non-vision models or as context
|
|
144
|
+
if (!supportsVision && includeImageReferences) {
|
|
145
|
+
const imageRefs = formatImageReferences(multimodal.images);
|
|
146
|
+
if (imageRefs) {
|
|
147
|
+
userContent = `${userContent}\n${imageRefs}`;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
parts.push(`User: ${userContent}`);
|
|
152
|
+
}
|
|
153
|
+
else if (msg.role === "assistant") {
|
|
154
|
+
if (msg.tool_calls && msg.tool_calls.length > 0) {
|
|
155
|
+
// Assistant made tool calls - show what was called
|
|
156
|
+
const toolCallsDesc = msg.tool_calls.map(tc => `[Called tool: ${tc.function.name}(${tc.function.arguments})]`).join("\n");
|
|
157
|
+
const textContent = extractTextContent(msg.content);
|
|
158
|
+
if (textContent) {
|
|
159
|
+
parts.push(`Assistant: ${textContent}\n${toolCallsDesc}`);
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
parts.push(`Assistant: ${toolCallsDesc}`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
const textContent = extractTextContent(msg.content);
|
|
167
|
+
if (textContent) {
|
|
168
|
+
parts.push(`Assistant: ${textContent}`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
else if (msg.role === "tool") {
|
|
173
|
+
// Tool result - show the result with the tool call ID for context
|
|
174
|
+
parts.push(`[Tool result for ${msg.tool_call_id}]: ${extractTextContent(msg.content)}`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
// Add instruction for the model to continue if there are tool results
|
|
178
|
+
if (hasToolResults) {
|
|
179
|
+
parts.push("\nBased on the tool results above, please continue your response:");
|
|
180
|
+
}
|
|
181
|
+
return {
|
|
182
|
+
prompt: parts.join("\n\n"),
|
|
183
|
+
images: allImages,
|
|
184
|
+
hasImages: allImages.length > 0,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Map exec request to OpenAI tool call format
|
|
189
|
+
*
|
|
190
|
+
* Mapping rules:
|
|
191
|
+
* - shell → bash: Execute shell commands
|
|
192
|
+
* - read → read: Read file contents
|
|
193
|
+
* - ls → list: List directory contents
|
|
194
|
+
* - grep (with pattern) → grep: Search file contents
|
|
195
|
+
* - grep (with glob) → glob: Search files by pattern
|
|
196
|
+
* - write → write: Write/create files (supports text and binary)
|
|
197
|
+
* - mcp → original tool name: MCP tool passthrough
|
|
198
|
+
* - request_context → null: Internal use only, not exposed
|
|
199
|
+
*/
|
|
200
|
+
export function mapExecRequestToTool(execReq) {
|
|
201
|
+
switch (execReq.type) {
|
|
202
|
+
case "shell": {
|
|
203
|
+
const toolArgs = { command: execReq.command };
|
|
204
|
+
if (execReq.cwd)
|
|
205
|
+
toolArgs.cwd = execReq.cwd;
|
|
206
|
+
return { toolName: "bash", toolArgs };
|
|
207
|
+
}
|
|
208
|
+
case "read":
|
|
209
|
+
return { toolName: "read", toolArgs: { filePath: execReq.path } };
|
|
210
|
+
case "ls":
|
|
211
|
+
return { toolName: "list", toolArgs: { path: execReq.path } };
|
|
212
|
+
case "grep": {
|
|
213
|
+
// Prefer glob pattern over regex pattern when both exist
|
|
214
|
+
// glob is used for file path matching (e.g., **/*.ts)
|
|
215
|
+
// pattern is used for content searching (e.g., "function foo")
|
|
216
|
+
if (execReq.glob) {
|
|
217
|
+
return { toolName: "glob", toolArgs: { pattern: execReq.glob, path: execReq.path } };
|
|
218
|
+
}
|
|
219
|
+
return { toolName: "grep", toolArgs: { pattern: execReq.pattern, path: execReq.path } };
|
|
220
|
+
}
|
|
221
|
+
case "write": {
|
|
222
|
+
// Handle both text and binary content
|
|
223
|
+
// Binary content (fileBytes) takes precedence if present
|
|
224
|
+
const content = execReq.fileBytes && execReq.fileBytes.length > 0
|
|
225
|
+
? Buffer.from(execReq.fileBytes).toString("base64")
|
|
226
|
+
: execReq.fileText;
|
|
227
|
+
const toolArgs = { filePath: execReq.path, content };
|
|
228
|
+
// Flag to indicate base64 encoding for binary files
|
|
229
|
+
if (execReq.fileBytes && execReq.fileBytes.length > 0) {
|
|
230
|
+
toolArgs.encoding = "base64";
|
|
231
|
+
}
|
|
232
|
+
return { toolName: "write", toolArgs };
|
|
233
|
+
}
|
|
234
|
+
case "mcp":
|
|
235
|
+
return {
|
|
236
|
+
toolName: execReq.toolName,
|
|
237
|
+
toolArgs: (execReq.args ?? {})
|
|
238
|
+
};
|
|
239
|
+
case "request_context":
|
|
240
|
+
// Internal tool, not exposed to OpenCode
|
|
241
|
+
return { toolName: null, toolArgs: null };
|
|
242
|
+
default:
|
|
243
|
+
// Unknown type - return null to skip
|
|
244
|
+
return { toolName: null, toolArgs: null };
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Create an error response in OpenAI format
|
|
249
|
+
*/
|
|
250
|
+
export function createErrorResponse(message, type = "invalid_request_error", status = 400) {
|
|
251
|
+
return new Response(JSON.stringify({
|
|
252
|
+
error: {
|
|
253
|
+
message,
|
|
254
|
+
type,
|
|
255
|
+
param: null,
|
|
256
|
+
code: null,
|
|
257
|
+
},
|
|
258
|
+
}), {
|
|
259
|
+
status,
|
|
260
|
+
headers: {
|
|
261
|
+
"Content-Type": "application/json",
|
|
262
|
+
"Access-Control-Allow-Origin": "*",
|
|
263
|
+
},
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Create an SSE chunk string from data
|
|
268
|
+
*/
|
|
269
|
+
export function createSSEChunk(data) {
|
|
270
|
+
return `data: ${JSON.stringify(data)}\n\n`;
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Create an SSE done signal
|
|
274
|
+
*/
|
|
275
|
+
export function createSSEDone() {
|
|
276
|
+
return "data: [DONE]\n\n";
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Create a streaming SSE response
|
|
280
|
+
*/
|
|
281
|
+
export function makeStreamResponse(readable) {
|
|
282
|
+
return new Response(readable, {
|
|
283
|
+
headers: {
|
|
284
|
+
"Content-Type": "text/event-stream",
|
|
285
|
+
"Cache-Control": "no-cache",
|
|
286
|
+
"Connection": "keep-alive",
|
|
287
|
+
"Access-Control-Allow-Origin": "*",
|
|
288
|
+
},
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Create CORS preflight response
|
|
293
|
+
*/
|
|
294
|
+
export function handleCORS() {
|
|
295
|
+
return new Response(null, {
|
|
296
|
+
status: 204,
|
|
297
|
+
headers: {
|
|
298
|
+
"Access-Control-Allow-Origin": "*",
|
|
299
|
+
"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
|
|
300
|
+
"Access-Control-Allow-Headers": "Content-Type, Authorization",
|
|
301
|
+
"Access-Control-Max-Age": "86400",
|
|
302
|
+
},
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Determine model owner based on model name
|
|
307
|
+
*/
|
|
308
|
+
export function getModelOwner(displayName) {
|
|
309
|
+
const lowerName = displayName.toLowerCase();
|
|
310
|
+
if (lowerName.includes("claude") || lowerName.includes("opus") || lowerName.includes("sonnet")) {
|
|
311
|
+
return "anthropic";
|
|
312
|
+
}
|
|
313
|
+
if (lowerName.includes("gpt")) {
|
|
314
|
+
return "openai";
|
|
315
|
+
}
|
|
316
|
+
if (lowerName.includes("gemini")) {
|
|
317
|
+
return "google";
|
|
318
|
+
}
|
|
319
|
+
if (lowerName.includes("grok")) {
|
|
320
|
+
return "xai";
|
|
321
|
+
}
|
|
322
|
+
return "cursor";
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* Create an OpenAI stream chunk
|
|
326
|
+
*/
|
|
327
|
+
export function createStreamChunk(completionId, model, created, delta, finishReason = null) {
|
|
328
|
+
return {
|
|
329
|
+
id: completionId,
|
|
330
|
+
object: "chat.completion.chunk",
|
|
331
|
+
created,
|
|
332
|
+
model,
|
|
333
|
+
choices: [{
|
|
334
|
+
index: 0,
|
|
335
|
+
delta,
|
|
336
|
+
finish_reason: finishReason,
|
|
337
|
+
}],
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Generate a tool call ID from completion ID and index
|
|
342
|
+
*/
|
|
343
|
+
export function generateToolCallId(completionId, index) {
|
|
344
|
+
// completionId format is "chatcmpl-{uuid}", so skip the "chatcmpl-" prefix (9 chars)
|
|
345
|
+
// This ensures unique IDs across different requests
|
|
346
|
+
return `call_${completionId.slice(9, 17)}_${index}`;
|
|
347
|
+
}
|
|
348
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../../src/lib/openai-compat/utils.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session Reuse Utilities
|
|
3
|
+
*
|
|
4
|
+
* ARCHITECTURAL NOTE:
|
|
5
|
+
* True session reuse (keeping a single bidirectional stream open across multiple
|
|
6
|
+
* OpenAI API requests) is not possible due to a fundamental mismatch:
|
|
7
|
+
*
|
|
8
|
+
* - OpenAI API: Request/response model. Must close HTTP response to return tool_calls
|
|
9
|
+
* to the client, then receive a new HTTP request with tool results.
|
|
10
|
+
*
|
|
11
|
+
* - Cursor's bidirectional streaming: Keeps a single stream open. Tool execution
|
|
12
|
+
* happens locally while the stream stays open. Results are sent via bidiAppend,
|
|
13
|
+
* and the server continues generating automatically.
|
|
14
|
+
*
|
|
15
|
+
* Our workaround: When tool results come back in a new request, we close any
|
|
16
|
+
* existing session and start completely fresh. The messagesToPrompt() function
|
|
17
|
+
* formats the full conversation history including prior tool calls and results,
|
|
18
|
+
* so the server has full context even though we're starting a new stream.
|
|
19
|
+
*
|
|
20
|
+
* The session infrastructure (SessionLike, pendingExecs, etc.) is retained for:
|
|
21
|
+
* 1. Potential future improvements if we find a way to bridge the gap
|
|
22
|
+
* 2. Internal read handling during edit flows (single-request scope)
|
|
23
|
+
*/
|
|
24
|
+
import type { ExecRequest, McpExecRequest } from "./api/agent-service";
|
|
25
|
+
export interface OpenAIToolCallLite {
|
|
26
|
+
id?: string;
|
|
27
|
+
function?: {
|
|
28
|
+
name?: string;
|
|
29
|
+
arguments?: string;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export interface OpenAIMessageLite {
|
|
33
|
+
role: "system" | "user" | "assistant" | "tool";
|
|
34
|
+
content: unknown;
|
|
35
|
+
tool_calls?: OpenAIToolCallLite[];
|
|
36
|
+
tool_call_id?: string;
|
|
37
|
+
}
|
|
38
|
+
export interface SessionClient {
|
|
39
|
+
sendToolResult: (execRequest: McpExecRequest & {
|
|
40
|
+
type: "mcp";
|
|
41
|
+
}, result: {
|
|
42
|
+
success?: {
|
|
43
|
+
content: string;
|
|
44
|
+
isError?: boolean;
|
|
45
|
+
};
|
|
46
|
+
error?: string;
|
|
47
|
+
}) => Promise<void>;
|
|
48
|
+
sendShellResult: (id: number, execId: string | undefined, command: string, cwd: string, stdout: string, stderr: string, exitCode: number, executionTimeMs?: number) => Promise<void>;
|
|
49
|
+
sendReadResult: (id: number, execId: string | undefined, content: string, path: string, totalLines?: number, fileSize?: bigint, truncated?: boolean) => Promise<void>;
|
|
50
|
+
sendLsResult: (id: number, execId: string | undefined, filesString: string) => Promise<void>;
|
|
51
|
+
sendGrepResult: (id: number, execId: string | undefined, pattern: string, path: string, files: string[]) => Promise<void>;
|
|
52
|
+
sendWriteResult: (id: number, execId: string | undefined, result: {
|
|
53
|
+
success?: {
|
|
54
|
+
path: string;
|
|
55
|
+
linesCreated: number;
|
|
56
|
+
fileSize: number;
|
|
57
|
+
fileContentAfterWrite?: string;
|
|
58
|
+
};
|
|
59
|
+
error?: {
|
|
60
|
+
path: string;
|
|
61
|
+
error: string;
|
|
62
|
+
};
|
|
63
|
+
}) => Promise<void>;
|
|
64
|
+
sendResumeAction?: () => Promise<void>;
|
|
65
|
+
}
|
|
66
|
+
export interface SessionLike {
|
|
67
|
+
id: string;
|
|
68
|
+
iterator: AsyncIterator<unknown>;
|
|
69
|
+
pendingExecs: Map<string, ExecRequest>;
|
|
70
|
+
createdAt: number;
|
|
71
|
+
lastActivity: number;
|
|
72
|
+
state: "running" | "waiting_tool";
|
|
73
|
+
client: SessionClient;
|
|
74
|
+
}
|
|
75
|
+
export declare function createSessionId(): string;
|
|
76
|
+
export declare function makeToolCallId(sessionId: string, callBase: string): string;
|
|
77
|
+
export declare function parseSessionIdFromToolCallId(toolCallId: string | null | undefined): string | null;
|
|
78
|
+
export declare function findSessionIdInMessages(messages: OpenAIMessageLite[]): string | null;
|
|
79
|
+
export declare function collectToolMessages(messages: OpenAIMessageLite[]): OpenAIMessageLite[];
|
|
80
|
+
export declare function extractMessageContent(message: OpenAIMessageLite): string;
|
|
81
|
+
export declare function selectCallBase(execReq: ExecRequest): string;
|
|
82
|
+
export { mapExecRequestToTool } from "./openai-compat/utils";
|
|
83
|
+
export declare function safeParseJson(input: string): Record<string, unknown> | null;
|
|
84
|
+
export declare function sendToolResultsToCursor(session: SessionLike, toolMessages: OpenAIMessageLite[]): Promise<boolean>;
|
|
85
|
+
export declare function cleanupExpiredSessions(sessionMap: Map<string, {
|
|
86
|
+
iterator?: AsyncIterator<unknown>;
|
|
87
|
+
lastActivity: number;
|
|
88
|
+
}>, timeoutMs: number, now?: number): Promise<void>;
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session Reuse Utilities
|
|
3
|
+
*
|
|
4
|
+
* ARCHITECTURAL NOTE:
|
|
5
|
+
* True session reuse (keeping a single bidirectional stream open across multiple
|
|
6
|
+
* OpenAI API requests) is not possible due to a fundamental mismatch:
|
|
7
|
+
*
|
|
8
|
+
* - OpenAI API: Request/response model. Must close HTTP response to return tool_calls
|
|
9
|
+
* to the client, then receive a new HTTP request with tool results.
|
|
10
|
+
*
|
|
11
|
+
* - Cursor's bidirectional streaming: Keeps a single stream open. Tool execution
|
|
12
|
+
* happens locally while the stream stays open. Results are sent via bidiAppend,
|
|
13
|
+
* and the server continues generating automatically.
|
|
14
|
+
*
|
|
15
|
+
* Our workaround: When tool results come back in a new request, we close any
|
|
16
|
+
* existing session and start completely fresh. The messagesToPrompt() function
|
|
17
|
+
* formats the full conversation history including prior tool calls and results,
|
|
18
|
+
* so the server has full context even though we're starting a new stream.
|
|
19
|
+
*
|
|
20
|
+
* The session infrastructure (SessionLike, pendingExecs, etc.) is retained for:
|
|
21
|
+
* 1. Potential future improvements if we find a way to bridge the gap
|
|
22
|
+
* 2. Internal read handling during edit flows (single-request scope)
|
|
23
|
+
*/
|
|
24
|
+
import crypto from "node:crypto";
|
|
25
|
+
export function createSessionId() {
|
|
26
|
+
return crypto.randomUUID().replace(/-/g, "").slice(0, 12);
|
|
27
|
+
}
|
|
28
|
+
export function makeToolCallId(sessionId, callBase) {
|
|
29
|
+
return `sess_${sessionId}__call_${callBase}`;
|
|
30
|
+
}
|
|
31
|
+
export function parseSessionIdFromToolCallId(toolCallId) {
|
|
32
|
+
if (!toolCallId)
|
|
33
|
+
return null;
|
|
34
|
+
const match = toolCallId.match(/^sess_([a-zA-Z0-9]+)__call_/);
|
|
35
|
+
if (!match)
|
|
36
|
+
return null;
|
|
37
|
+
return match[1] ?? null;
|
|
38
|
+
}
|
|
39
|
+
export function findSessionIdInMessages(messages) {
|
|
40
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
41
|
+
const msg = messages[i];
|
|
42
|
+
if (!msg)
|
|
43
|
+
continue;
|
|
44
|
+
if (msg.role === "tool" && msg.tool_call_id) {
|
|
45
|
+
const sessionId = parseSessionIdFromToolCallId(msg.tool_call_id);
|
|
46
|
+
if (sessionId)
|
|
47
|
+
return sessionId;
|
|
48
|
+
}
|
|
49
|
+
if (msg.role === "assistant" && Array.isArray(msg.tool_calls)) {
|
|
50
|
+
for (const tc of msg.tool_calls) {
|
|
51
|
+
const sessionId = parseSessionIdFromToolCallId(tc?.id);
|
|
52
|
+
if (sessionId)
|
|
53
|
+
return sessionId;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
export function collectToolMessages(messages) {
|
|
60
|
+
return messages.filter((m) => m?.role === "tool" && !!m.tool_call_id);
|
|
61
|
+
}
|
|
62
|
+
export function extractMessageContent(message) {
|
|
63
|
+
if (typeof message.content === "string")
|
|
64
|
+
return message.content;
|
|
65
|
+
if (Array.isArray(message.content)) {
|
|
66
|
+
return message.content
|
|
67
|
+
.map((part) => {
|
|
68
|
+
if (typeof part === "string")
|
|
69
|
+
return part;
|
|
70
|
+
if (part && typeof part === "object" && "text" in part) {
|
|
71
|
+
const text = part.text;
|
|
72
|
+
return typeof text === "string" ? text : "";
|
|
73
|
+
}
|
|
74
|
+
return "";
|
|
75
|
+
})
|
|
76
|
+
.filter(Boolean)
|
|
77
|
+
.join("");
|
|
78
|
+
}
|
|
79
|
+
return "";
|
|
80
|
+
}
|
|
81
|
+
export function selectCallBase(execReq) {
|
|
82
|
+
const raw = execReq.type === "mcp"
|
|
83
|
+
? execReq.toolCallId
|
|
84
|
+
: execReq.execId ?? String(execReq.id ?? crypto.randomUUID());
|
|
85
|
+
const cleaned = raw.replace(/[^a-zA-Z0-9]/g, "");
|
|
86
|
+
return cleaned.slice(0, 32) || crypto.randomUUID().replace(/-/g, "").slice(0, 12);
|
|
87
|
+
}
|
|
88
|
+
// Re-export from utils.ts to maintain backward compatibility
|
|
89
|
+
// The canonical implementation is in openai-compat/utils.ts
|
|
90
|
+
export { mapExecRequestToTool } from "./openai-compat/utils";
|
|
91
|
+
export function safeParseJson(input) {
|
|
92
|
+
try {
|
|
93
|
+
return JSON.parse(input);
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
export async function sendToolResultsToCursor(session, toolMessages) {
|
|
100
|
+
let processedAny = false;
|
|
101
|
+
for (const message of toolMessages) {
|
|
102
|
+
if (!message.tool_call_id)
|
|
103
|
+
continue;
|
|
104
|
+
console.log(`[Session ${session.id}] Looking up tool_call_id=${message.tool_call_id}, available keys=[${Array.from(session.pendingExecs.keys()).join(", ")}]`);
|
|
105
|
+
const execReq = session.pendingExecs.get(message.tool_call_id);
|
|
106
|
+
if (!execReq) {
|
|
107
|
+
console.warn(`[Session ${session.id}] Tool result for unknown tool_call_id ${message.tool_call_id}; ignoring`);
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
const content = extractMessageContent(message);
|
|
111
|
+
try {
|
|
112
|
+
if (execReq.type === "mcp") {
|
|
113
|
+
await session.client.sendToolResult(execReq, {
|
|
114
|
+
success: { content, isError: false },
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
else if (execReq.type === "shell") {
|
|
118
|
+
const parsed = safeParseJson(content);
|
|
119
|
+
const stdout = parsed && typeof parsed.stdout === "string" ? parsed.stdout : content;
|
|
120
|
+
const stderr = parsed && typeof parsed.stderr === "string" ? parsed.stderr : "";
|
|
121
|
+
const exitCode = parsed && typeof parsed.exitCode === "number" ? parsed.exitCode : 0;
|
|
122
|
+
const executionTimeMs = parsed && typeof parsed.executionTimeMs === "number" ? parsed.executionTimeMs : undefined;
|
|
123
|
+
await session.client.sendShellResult(execReq.id, execReq.execId, execReq.command, execReq.cwd || process.cwd(), stdout, stderr, exitCode, executionTimeMs);
|
|
124
|
+
}
|
|
125
|
+
else if (execReq.type === "read") {
|
|
126
|
+
await session.client.sendReadResult(execReq.id, execReq.execId, content, execReq.path, content.split("\n").length, BigInt(content.length), false);
|
|
127
|
+
}
|
|
128
|
+
else if (execReq.type === "ls") {
|
|
129
|
+
await session.client.sendLsResult(execReq.id, execReq.execId, content);
|
|
130
|
+
}
|
|
131
|
+
else if (execReq.type === "grep") {
|
|
132
|
+
const files = content.trim().split("\n").filter(Boolean);
|
|
133
|
+
const pattern = execReq.glob ?? execReq.pattern ?? "";
|
|
134
|
+
const path = execReq.path ?? process.cwd();
|
|
135
|
+
await session.client.sendGrepResult(execReq.id, execReq.execId, pattern, path, files);
|
|
136
|
+
}
|
|
137
|
+
else if (execReq.type === "write") {
|
|
138
|
+
const parsed = safeParseJson(content);
|
|
139
|
+
const parsedError = parsed?.error;
|
|
140
|
+
if (typeof parsedError === "string" && parsedError.length > 0) {
|
|
141
|
+
await session.client.sendWriteResult(execReq.id, execReq.execId, {
|
|
142
|
+
error: { path: execReq.path, error: parsedError },
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
const linesCreatedValue = parsed?.linesCreated;
|
|
147
|
+
const fileSizeValue = parsed?.fileSize;
|
|
148
|
+
const fileContentAfterWriteValue = parsed?.fileContentAfterWrite;
|
|
149
|
+
const linesCreated = typeof linesCreatedValue === "number" ? linesCreatedValue : content.split("\n").length;
|
|
150
|
+
const fileSize = typeof fileSizeValue === "number" ? fileSizeValue : content.length;
|
|
151
|
+
const fileContentAfterWrite = typeof fileContentAfterWriteValue === "string" ? fileContentAfterWriteValue : undefined;
|
|
152
|
+
await session.client.sendWriteResult(execReq.id, execReq.execId, {
|
|
153
|
+
success: {
|
|
154
|
+
path: execReq.path,
|
|
155
|
+
linesCreated,
|
|
156
|
+
fileSize,
|
|
157
|
+
fileContentAfterWrite,
|
|
158
|
+
},
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
catch (err) {
|
|
167
|
+
console.error(`[Session ${session.id}] Failed to send tool result for ${message.tool_call_id}:`, err);
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
170
|
+
session.pendingExecs.delete(message.tool_call_id);
|
|
171
|
+
processedAny = true;
|
|
172
|
+
}
|
|
173
|
+
if (processedAny) {
|
|
174
|
+
session.state = "running";
|
|
175
|
+
session.lastActivity = Date.now();
|
|
176
|
+
console.log(`[Session ${session.id}] processedAny=true, tool results sent. Waiting for server to continue...`);
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
console.log(`[Session ${session.id}] processedAny=false, no matching tool results found`);
|
|
180
|
+
}
|
|
181
|
+
return processedAny;
|
|
182
|
+
}
|
|
183
|
+
export async function cleanupExpiredSessions(sessionMap, timeoutMs, now = Date.now()) {
|
|
184
|
+
for (const [sessionId, session] of sessionMap) {
|
|
185
|
+
if (now - session.lastActivity > timeoutMs) {
|
|
186
|
+
try {
|
|
187
|
+
await session.iterator?.return?.();
|
|
188
|
+
}
|
|
189
|
+
catch (err) {
|
|
190
|
+
console.warn(`[Session ${sessionId}] Failed to close expired iterator:`, err);
|
|
191
|
+
}
|
|
192
|
+
finally {
|
|
193
|
+
sessionMap.delete(sessionId);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
//# sourceMappingURL=session-reuse.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-reuse.js","sourceRoot":"","sources":["../../src/lib/session-reuse.ts"],"names":[],"mappings":"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"}
|