@bifrost-ai/engine-cursor 0.1.1-build.1783540132209
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/dist/index.d.mts +27 -0
- package/dist/index.mjs +619 -0
- package/package.json +37 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { McpServerConfig, SettingSource } from "@cursor/sdk";
|
|
2
|
+
import { Engine, EngineContext, EngineResult } from "@bifrost-ai/engine";
|
|
3
|
+
|
|
4
|
+
//#region src/cursor-engine.d.ts
|
|
5
|
+
type CursorEngineConfig = {
|
|
6
|
+
apiKey?: string;
|
|
7
|
+
model?: string;
|
|
8
|
+
settingSources?: SettingSource[];
|
|
9
|
+
mode?: "agent" | "plan";
|
|
10
|
+
executionTimeoutMs?: number;
|
|
11
|
+
};
|
|
12
|
+
type McpToolkitConstructor = (context: EngineContext) => McpServerConfig;
|
|
13
|
+
declare class CursorEngine implements Engine {
|
|
14
|
+
private readonly config;
|
|
15
|
+
private toolkits;
|
|
16
|
+
constructor(config?: CursorEngineConfig);
|
|
17
|
+
registerToolkit(name: string, toolkit: McpServerConfig | McpToolkitConstructor): void;
|
|
18
|
+
private resolveApiKey;
|
|
19
|
+
private resolveModel;
|
|
20
|
+
private resolveMcpServers;
|
|
21
|
+
private streamRun;
|
|
22
|
+
private withExecutionTimeout;
|
|
23
|
+
private mapFinishedResult;
|
|
24
|
+
execute(context: EngineContext, sessionId?: string): Promise<EngineResult>;
|
|
25
|
+
}
|
|
26
|
+
//#endregion
|
|
27
|
+
export { CursorEngine, type CursorEngineConfig, type McpToolkitConstructor };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,619 @@
|
|
|
1
|
+
import { Agent, CursorAgentError } from "@cursor/sdk";
|
|
2
|
+
import createDebug from "debug";
|
|
3
|
+
//#region src/prompt.ts
|
|
4
|
+
const promptSection = (name, body) => `<${name}>${body}</${name}>`;
|
|
5
|
+
const buildPrompt = (options) => {
|
|
6
|
+
const { agent, instructions } = options;
|
|
7
|
+
return [promptSection("AgentDefinition", agent.promptBody), promptSection("FeatureDefinition", instructions)].join("\n");
|
|
8
|
+
};
|
|
9
|
+
//#endregion
|
|
10
|
+
//#region src/pricing.ts
|
|
11
|
+
const catalog = {
|
|
12
|
+
source: "https://cursor.com/docs/models-and-pricing",
|
|
13
|
+
unit: "usd_per_million_tokens",
|
|
14
|
+
auto: {
|
|
15
|
+
"inputAndCacheWrite": 1.25,
|
|
16
|
+
"output": 6,
|
|
17
|
+
"cacheRead": .25
|
|
18
|
+
},
|
|
19
|
+
models: [
|
|
20
|
+
{
|
|
21
|
+
"name": "Claude 4 Sonnet",
|
|
22
|
+
"ids": ["claude-4-sonnet", "claude-4-sonnet-thinking"],
|
|
23
|
+
"provider": "Anthropic",
|
|
24
|
+
"input": 3,
|
|
25
|
+
"cacheWrite": 3.75,
|
|
26
|
+
"cacheRead": .3,
|
|
27
|
+
"output": 15
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"name": "Claude 4 Sonnet 1M",
|
|
31
|
+
"ids": ["claude-4-sonnet-1m"],
|
|
32
|
+
"provider": "Anthropic",
|
|
33
|
+
"input": 6,
|
|
34
|
+
"cacheWrite": 7.5,
|
|
35
|
+
"cacheRead": .6,
|
|
36
|
+
"output": 22.5
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"name": "Claude 4.5 Haiku",
|
|
40
|
+
"ids": ["claude-4-5-haiku", "claude-4.5-haiku"],
|
|
41
|
+
"provider": "Anthropic",
|
|
42
|
+
"input": 1,
|
|
43
|
+
"cacheWrite": 1.25,
|
|
44
|
+
"cacheRead": .1,
|
|
45
|
+
"output": 5
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"name": "Claude 4.5 Opus",
|
|
49
|
+
"ids": ["claude-4-5-opus", "claude-4.5-opus"],
|
|
50
|
+
"provider": "Anthropic",
|
|
51
|
+
"input": 5,
|
|
52
|
+
"cacheWrite": 6.25,
|
|
53
|
+
"cacheRead": .5,
|
|
54
|
+
"output": 25
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"name": "Claude 4.5 Sonnet",
|
|
58
|
+
"ids": ["claude-4-5-sonnet", "claude-4.5-sonnet"],
|
|
59
|
+
"provider": "Anthropic",
|
|
60
|
+
"input": 3,
|
|
61
|
+
"cacheWrite": 3.75,
|
|
62
|
+
"cacheRead": .3,
|
|
63
|
+
"output": 15
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"name": "Claude 4.6 Opus",
|
|
67
|
+
"ids": ["claude-4-6-opus", "claude-4.6-opus"],
|
|
68
|
+
"provider": "Anthropic",
|
|
69
|
+
"input": 5,
|
|
70
|
+
"cacheWrite": 6.25,
|
|
71
|
+
"cacheRead": .5,
|
|
72
|
+
"output": 25
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"name": "Claude 4.6 Sonnet",
|
|
76
|
+
"ids": ["claude-4-6-sonnet", "claude-4.6-sonnet"],
|
|
77
|
+
"provider": "Anthropic",
|
|
78
|
+
"input": 3,
|
|
79
|
+
"cacheWrite": 3.75,
|
|
80
|
+
"cacheRead": .3,
|
|
81
|
+
"output": 15
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"name": "Claude 4.7 Opus",
|
|
85
|
+
"ids": ["claude-4-7-opus", "claude-4.7-opus"],
|
|
86
|
+
"provider": "Anthropic",
|
|
87
|
+
"input": 5,
|
|
88
|
+
"cacheWrite": 6.25,
|
|
89
|
+
"cacheRead": .5,
|
|
90
|
+
"output": 25
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"name": "Claude Fable 5",
|
|
94
|
+
"ids": ["claude-fable-5"],
|
|
95
|
+
"provider": "Anthropic",
|
|
96
|
+
"input": 10,
|
|
97
|
+
"cacheWrite": 12.5,
|
|
98
|
+
"cacheRead": 1,
|
|
99
|
+
"output": 50
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
"name": "Claude Opus 4.7 (fast mode)",
|
|
103
|
+
"ids": ["claude-opus-4-7-fast", "claude-opus-4.7-fast"],
|
|
104
|
+
"provider": "Anthropic",
|
|
105
|
+
"input": 30,
|
|
106
|
+
"cacheWrite": 37.5,
|
|
107
|
+
"cacheRead": 3,
|
|
108
|
+
"output": 150
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
"name": "Claude Opus 4.8",
|
|
112
|
+
"ids": [
|
|
113
|
+
"claude-opus-4-8",
|
|
114
|
+
"claude-opus-4.8",
|
|
115
|
+
"claude-opus-4-8-fast"
|
|
116
|
+
],
|
|
117
|
+
"provider": "Anthropic",
|
|
118
|
+
"input": 5,
|
|
119
|
+
"cacheWrite": 6.25,
|
|
120
|
+
"cacheRead": .5,
|
|
121
|
+
"output": 25
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
"name": "Claude Sonnet 5",
|
|
125
|
+
"ids": ["claude-sonnet-5"],
|
|
126
|
+
"provider": "Anthropic",
|
|
127
|
+
"input": 3,
|
|
128
|
+
"cacheWrite": 3.75,
|
|
129
|
+
"cacheRead": .3,
|
|
130
|
+
"output": 15
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
"name": "Composer 1",
|
|
134
|
+
"ids": ["composer-1"],
|
|
135
|
+
"provider": "Cursor",
|
|
136
|
+
"input": 1.25,
|
|
137
|
+
"cacheRead": .125,
|
|
138
|
+
"output": 10
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
"name": "Composer 1.5",
|
|
142
|
+
"ids": ["composer-1.5"],
|
|
143
|
+
"provider": "Cursor",
|
|
144
|
+
"input": 3.5,
|
|
145
|
+
"cacheRead": .35,
|
|
146
|
+
"output": 17.5
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
"name": "Composer 2",
|
|
150
|
+
"ids": ["composer-2", "composer-2-fast"],
|
|
151
|
+
"provider": "Cursor",
|
|
152
|
+
"input": .5,
|
|
153
|
+
"cacheRead": .2,
|
|
154
|
+
"output": 2.5
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
"name": "Composer 2.5",
|
|
158
|
+
"ids": ["composer-2.5"],
|
|
159
|
+
"provider": "Cursor",
|
|
160
|
+
"input": .5,
|
|
161
|
+
"cacheRead": .2,
|
|
162
|
+
"output": 2.5
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
"name": "Composer 2.5 (Fast)",
|
|
166
|
+
"ids": ["composer-2.5-fast"],
|
|
167
|
+
"provider": "Cursor",
|
|
168
|
+
"input": 3,
|
|
169
|
+
"cacheRead": .5,
|
|
170
|
+
"output": 15
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
"name": "Gemini 2.5 Flash",
|
|
174
|
+
"ids": ["gemini-2.5-flash"],
|
|
175
|
+
"provider": "Google",
|
|
176
|
+
"input": .3,
|
|
177
|
+
"cacheRead": .03,
|
|
178
|
+
"output": 2.5
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
"name": "Gemini 3 Flash",
|
|
182
|
+
"ids": ["gemini-3-flash"],
|
|
183
|
+
"provider": "Google",
|
|
184
|
+
"input": .5,
|
|
185
|
+
"cacheRead": .05,
|
|
186
|
+
"output": 3
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
"name": "Gemini 3 Pro",
|
|
190
|
+
"ids": ["gemini-3-pro", "gemini-3-pro-image-preview"],
|
|
191
|
+
"provider": "Google",
|
|
192
|
+
"input": 2,
|
|
193
|
+
"cacheRead": .2,
|
|
194
|
+
"output": 12
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
"name": "Gemini 3.1 Pro",
|
|
198
|
+
"ids": ["gemini-3.1-pro"],
|
|
199
|
+
"provider": "Google",
|
|
200
|
+
"input": 2,
|
|
201
|
+
"cacheRead": .2,
|
|
202
|
+
"output": 12
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
"name": "Gemini 3.5 Flash",
|
|
206
|
+
"ids": ["gemini-3.5-flash"],
|
|
207
|
+
"provider": "Google",
|
|
208
|
+
"input": 1.5,
|
|
209
|
+
"cacheRead": .15,
|
|
210
|
+
"output": 9
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
"name": "GLM 5.2",
|
|
214
|
+
"ids": ["glm-5.2"],
|
|
215
|
+
"provider": "Z.ai",
|
|
216
|
+
"input": 1.4,
|
|
217
|
+
"cacheRead": .26,
|
|
218
|
+
"output": 4.4
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
"name": "GPT-5",
|
|
222
|
+
"ids": ["gpt-5", "gpt-5-high"],
|
|
223
|
+
"provider": "OpenAI",
|
|
224
|
+
"input": 1.25,
|
|
225
|
+
"cacheRead": .125,
|
|
226
|
+
"output": 10
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
"name": "GPT-5 Fast",
|
|
230
|
+
"ids": [
|
|
231
|
+
"gpt-5-fast",
|
|
232
|
+
"gpt-5-high-fast",
|
|
233
|
+
"gpt-5-low-fast"
|
|
234
|
+
],
|
|
235
|
+
"provider": "OpenAI",
|
|
236
|
+
"input": 2.5,
|
|
237
|
+
"cacheRead": .25,
|
|
238
|
+
"output": 20
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
"name": "GPT-5 Mini",
|
|
242
|
+
"ids": ["gpt-5-mini"],
|
|
243
|
+
"provider": "OpenAI",
|
|
244
|
+
"input": .25,
|
|
245
|
+
"cacheRead": .025,
|
|
246
|
+
"output": 2
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
"name": "GPT-5-Codex",
|
|
250
|
+
"ids": ["gpt-5-codex"],
|
|
251
|
+
"provider": "OpenAI",
|
|
252
|
+
"input": 1.25,
|
|
253
|
+
"cacheRead": .125,
|
|
254
|
+
"output": 10
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
"name": "GPT-5.1 Codex",
|
|
258
|
+
"ids": ["gpt-5.1-codex"],
|
|
259
|
+
"provider": "OpenAI",
|
|
260
|
+
"input": 1.25,
|
|
261
|
+
"cacheRead": .125,
|
|
262
|
+
"output": 10
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
"name": "GPT-5.1 Codex Max",
|
|
266
|
+
"ids": ["gpt-5.1-codex-max"],
|
|
267
|
+
"provider": "OpenAI",
|
|
268
|
+
"input": 1.25,
|
|
269
|
+
"cacheRead": .125,
|
|
270
|
+
"output": 10
|
|
271
|
+
},
|
|
272
|
+
{
|
|
273
|
+
"name": "GPT-5.1 Codex Mini",
|
|
274
|
+
"ids": ["gpt-5.1-codex-mini"],
|
|
275
|
+
"provider": "OpenAI",
|
|
276
|
+
"input": .25,
|
|
277
|
+
"cacheRead": .025,
|
|
278
|
+
"output": 2
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
"name": "GPT-5.2",
|
|
282
|
+
"ids": ["gpt-5.2", "gpt-5.2-high"],
|
|
283
|
+
"provider": "OpenAI",
|
|
284
|
+
"input": 1.75,
|
|
285
|
+
"cacheRead": .175,
|
|
286
|
+
"output": 14
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
"name": "GPT-5.2 Codex",
|
|
290
|
+
"ids": ["gpt-5.2-codex"],
|
|
291
|
+
"provider": "OpenAI",
|
|
292
|
+
"input": 1.75,
|
|
293
|
+
"cacheRead": .175,
|
|
294
|
+
"output": 14
|
|
295
|
+
},
|
|
296
|
+
{
|
|
297
|
+
"name": "GPT-5.3 Codex",
|
|
298
|
+
"ids": ["gpt-5.3-codex", "gpt-5.3-codex-high"],
|
|
299
|
+
"provider": "OpenAI",
|
|
300
|
+
"input": 1.75,
|
|
301
|
+
"cacheRead": .175,
|
|
302
|
+
"output": 14
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
"name": "GPT-5.4",
|
|
306
|
+
"ids": ["gpt-5.4", "gpt-5.4-fast"],
|
|
307
|
+
"provider": "OpenAI",
|
|
308
|
+
"input": 2.5,
|
|
309
|
+
"cacheRead": .25,
|
|
310
|
+
"output": 15
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
"name": "GPT-5.4 Mini",
|
|
314
|
+
"ids": ["gpt-5.4-mini"],
|
|
315
|
+
"provider": "OpenAI",
|
|
316
|
+
"input": .75,
|
|
317
|
+
"cacheRead": .075,
|
|
318
|
+
"output": 4.5
|
|
319
|
+
},
|
|
320
|
+
{
|
|
321
|
+
"name": "GPT-5.4 Nano",
|
|
322
|
+
"ids": ["gpt-5.4-nano"],
|
|
323
|
+
"provider": "OpenAI",
|
|
324
|
+
"input": .2,
|
|
325
|
+
"cacheRead": .02,
|
|
326
|
+
"output": 1.25
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
"name": "GPT-5.5",
|
|
330
|
+
"ids": ["gpt-5.5", "gpt-5.5-fast"],
|
|
331
|
+
"provider": "OpenAI",
|
|
332
|
+
"input": 5,
|
|
333
|
+
"cacheRead": .5,
|
|
334
|
+
"output": 30
|
|
335
|
+
},
|
|
336
|
+
{
|
|
337
|
+
"name": "Grok 4.20",
|
|
338
|
+
"ids": ["grok-4.20"],
|
|
339
|
+
"provider": "xAI",
|
|
340
|
+
"input": 2,
|
|
341
|
+
"cacheRead": .2,
|
|
342
|
+
"output": 6
|
|
343
|
+
},
|
|
344
|
+
{
|
|
345
|
+
"name": "Grok 4.3",
|
|
346
|
+
"ids": ["grok-4.3"],
|
|
347
|
+
"provider": "xAI",
|
|
348
|
+
"input": 1.25,
|
|
349
|
+
"cacheRead": .2,
|
|
350
|
+
"output": 2.5
|
|
351
|
+
},
|
|
352
|
+
{
|
|
353
|
+
"name": "Grok Build 0.1",
|
|
354
|
+
"ids": ["grok-build-0.1"],
|
|
355
|
+
"provider": "xAI",
|
|
356
|
+
"input": 1,
|
|
357
|
+
"cacheRead": .2,
|
|
358
|
+
"output": 2
|
|
359
|
+
},
|
|
360
|
+
{
|
|
361
|
+
"name": "Kimi K2.7 Code",
|
|
362
|
+
"ids": ["kimi-k2.7-code", "kimi-k2-7-code"],
|
|
363
|
+
"provider": "Moonshot",
|
|
364
|
+
"input": .95,
|
|
365
|
+
"cacheRead": .19,
|
|
366
|
+
"output": 4
|
|
367
|
+
}
|
|
368
|
+
]
|
|
369
|
+
};
|
|
370
|
+
const TOKENS_PER_MILLION = 1e6;
|
|
371
|
+
const normalizeModelId = (modelId) => modelId.trim().toLowerCase().replaceAll("_", "-");
|
|
372
|
+
const ratesById = new Map(catalog.models.flatMap((model) => model.ids.map((id) => [normalizeModelId(id), {
|
|
373
|
+
input: model.input,
|
|
374
|
+
cacheWrite: model.cacheWrite,
|
|
375
|
+
cacheRead: model.cacheRead,
|
|
376
|
+
output: model.output
|
|
377
|
+
}])));
|
|
378
|
+
const resolveModelRates = (modelId) => {
|
|
379
|
+
if (!modelId) return;
|
|
380
|
+
const normalized = normalizeModelId(modelId);
|
|
381
|
+
if (normalized === "auto") return {
|
|
382
|
+
input: catalog.auto.inputAndCacheWrite,
|
|
383
|
+
cacheWrite: catalog.auto.inputAndCacheWrite,
|
|
384
|
+
cacheRead: catalog.auto.cacheRead,
|
|
385
|
+
output: catalog.auto.output
|
|
386
|
+
};
|
|
387
|
+
const exact = ratesById.get(normalized);
|
|
388
|
+
if (exact) return exact;
|
|
389
|
+
let bestMatchId;
|
|
390
|
+
let bestRates;
|
|
391
|
+
for (const [id, rates] of ratesById) if (normalized.startsWith(id)) {
|
|
392
|
+
if (!bestMatchId || id.length > bestMatchId.length) {
|
|
393
|
+
bestMatchId = id;
|
|
394
|
+
bestRates = rates;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
return bestRates;
|
|
398
|
+
};
|
|
399
|
+
const calculateUsageCostUsd = (modelId, usage) => {
|
|
400
|
+
const rates = resolveModelRates(modelId);
|
|
401
|
+
if (!rates) return 0;
|
|
402
|
+
const cacheWriteRate = rates.cacheWrite ?? rates.input;
|
|
403
|
+
const cacheReadRate = rates.cacheRead ?? 0;
|
|
404
|
+
return (usage.inputTokens * rates.input + usage.cacheWriteTokens * cacheWriteRate + usage.cacheReadTokens * cacheReadRate + usage.outputTokens * rates.output) / TOKENS_PER_MILLION;
|
|
405
|
+
};
|
|
406
|
+
//#endregion
|
|
407
|
+
//#region src/stats.ts
|
|
408
|
+
const mapRunResultToStats = (result, numTurns) => {
|
|
409
|
+
const usage = result.usage;
|
|
410
|
+
const tokenUsage = {
|
|
411
|
+
inputTokens: usage?.inputTokens ?? 0,
|
|
412
|
+
outputTokens: usage?.outputTokens ?? 0,
|
|
413
|
+
cacheReadTokens: usage?.cacheReadTokens ?? 0,
|
|
414
|
+
cacheWriteTokens: usage?.cacheWriteTokens ?? 0
|
|
415
|
+
};
|
|
416
|
+
return {
|
|
417
|
+
durationMs: result.durationMs ?? 0,
|
|
418
|
+
inputTokens: tokenUsage.inputTokens,
|
|
419
|
+
outputTokens: tokenUsage.outputTokens,
|
|
420
|
+
cacheReadTokens: tokenUsage.cacheReadTokens,
|
|
421
|
+
cacheCreationTokens: tokenUsage.cacheWriteTokens,
|
|
422
|
+
totalCostUsd: calculateUsageCostUsd(result.modelId, tokenUsage),
|
|
423
|
+
numTurns
|
|
424
|
+
};
|
|
425
|
+
};
|
|
426
|
+
//#endregion
|
|
427
|
+
//#region src/stream-preview.ts
|
|
428
|
+
const formatToolInput = (input) => {
|
|
429
|
+
if (!input || typeof input !== "object") return String(input ?? "");
|
|
430
|
+
return Object.entries(input).slice(0, 3).map(([key, val]) => `${key}=${String(val)}`).join(", ");
|
|
431
|
+
};
|
|
432
|
+
const extractAssistantPreview = (message) => {
|
|
433
|
+
if (message.type !== "assistant") return null;
|
|
434
|
+
const content = message.message.content;
|
|
435
|
+
const parts = [];
|
|
436
|
+
for (const block of content) if (block.type === "text" && block.text) parts.push(block.text.replace(/\n/g, " "));
|
|
437
|
+
else if (block.type === "tool_use" && block.name) {
|
|
438
|
+
const args = formatToolInput(block.input);
|
|
439
|
+
parts.push(`ToolCall(${block.name}${args ? `, ${args}` : ""})`);
|
|
440
|
+
}
|
|
441
|
+
return parts.length > 0 ? parts.join(" | ") : null;
|
|
442
|
+
};
|
|
443
|
+
const getMessagePreview = (message) => {
|
|
444
|
+
if (message.type === "assistant") return extractAssistantPreview(message) ?? "";
|
|
445
|
+
if (message.type === "thinking") return message.text.replace(/\n/g, " ");
|
|
446
|
+
if (message.type === "tool_call") return `ToolCall(${message.name}, ${message.status})`;
|
|
447
|
+
if (message.type === "user") {
|
|
448
|
+
const parts = [];
|
|
449
|
+
for (const block of message.message.content) if (block.type === "text" && block.text) parts.push(block.text.replace(/\n/g, " "));
|
|
450
|
+
return parts.join(" | ");
|
|
451
|
+
}
|
|
452
|
+
return "";
|
|
453
|
+
};
|
|
454
|
+
//#endregion
|
|
455
|
+
//#region src/cursor-engine.ts
|
|
456
|
+
const debug = createDebug("bifrost:engine:cursor");
|
|
457
|
+
const DEFAULT_MODEL = "composer-2.5";
|
|
458
|
+
const DEFAULT_EXECUTION_TIMEOUT_MS = 1800 * 1e3;
|
|
459
|
+
const mcpServerNamePattern = /^mcp__([^_]+(?:_[^_]+)*)__/;
|
|
460
|
+
var ExecutionTimeoutError = class extends Error {
|
|
461
|
+
timeoutMs;
|
|
462
|
+
constructor(timeoutMs) {
|
|
463
|
+
super(`Execution timed out after ${timeoutMs}ms`);
|
|
464
|
+
this.timeoutMs = timeoutMs;
|
|
465
|
+
this.name = "ExecutionTimeoutError";
|
|
466
|
+
}
|
|
467
|
+
};
|
|
468
|
+
var CursorEngine = class {
|
|
469
|
+
config;
|
|
470
|
+
toolkits = /* @__PURE__ */ new Map();
|
|
471
|
+
constructor(config = {}) {
|
|
472
|
+
this.config = config;
|
|
473
|
+
}
|
|
474
|
+
registerToolkit(name, toolkit) {
|
|
475
|
+
this.toolkits.set(name, toolkit);
|
|
476
|
+
}
|
|
477
|
+
resolveApiKey() {
|
|
478
|
+
return this.config.apiKey ?? process.env.CURSOR_API_KEY;
|
|
479
|
+
}
|
|
480
|
+
resolveModel(agentModel) {
|
|
481
|
+
return { id: agentModel ?? this.config.model ?? DEFAULT_MODEL };
|
|
482
|
+
}
|
|
483
|
+
resolveMcpServers(tools, context) {
|
|
484
|
+
const bareToolNames = [...new Set(tools.map((tool) => typeof tool === "string" ? tool.replace(/\(.*\)$/, "") : tool.name))];
|
|
485
|
+
const activeServerNames = new Set(bareToolNames.map((toolName) => mcpServerNamePattern.exec(toolName)?.[1]).filter((serverName) => serverName !== null && serverName !== void 0));
|
|
486
|
+
if (activeServerNames.size === 0) return;
|
|
487
|
+
debug("creating tools workingDir=%s", context.workingDir);
|
|
488
|
+
const mcpServers = {};
|
|
489
|
+
for (const name of activeServerNames) {
|
|
490
|
+
const entry = this.toolkits.get(name);
|
|
491
|
+
if (entry !== void 0) mcpServers[name] = typeof entry === "function" ? entry(context) : entry;
|
|
492
|
+
}
|
|
493
|
+
debug("mcp tools created count=%s", Object.keys(mcpServers).length);
|
|
494
|
+
return Object.keys(mcpServers).length > 0 ? mcpServers : void 0;
|
|
495
|
+
}
|
|
496
|
+
async streamRun(run) {
|
|
497
|
+
let lastMessage = null;
|
|
498
|
+
let numTurns = 0;
|
|
499
|
+
for await (const message of run.stream()) {
|
|
500
|
+
const preview = getMessagePreview(message);
|
|
501
|
+
debug("message type=%s preview=%s", message.type, preview ? `"${preview}..."` : "-");
|
|
502
|
+
if (message.type === "usage") numTurns += 1;
|
|
503
|
+
if (message.type === "assistant") {
|
|
504
|
+
if (preview) lastMessage = preview;
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
return {
|
|
508
|
+
lastMessage,
|
|
509
|
+
numTurns: numTurns > 0 ? numTurns : 1
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
async withExecutionTimeout(run, timeoutMs, operation) {
|
|
513
|
+
let timer;
|
|
514
|
+
try {
|
|
515
|
+
return await Promise.race([operation(), new Promise((_, reject) => {
|
|
516
|
+
timer = setTimeout(() => {
|
|
517
|
+
reject(new ExecutionTimeoutError(timeoutMs));
|
|
518
|
+
}, timeoutMs);
|
|
519
|
+
})]);
|
|
520
|
+
} catch (error) {
|
|
521
|
+
if (error instanceof ExecutionTimeoutError) await run.cancel().catch(() => void 0);
|
|
522
|
+
throw error;
|
|
523
|
+
} finally {
|
|
524
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
mapFinishedResult(result, agentId, lastMessage, numTurns, modelId) {
|
|
528
|
+
const stats = mapRunResultToStats({
|
|
529
|
+
durationMs: result.durationMs,
|
|
530
|
+
usage: result.usage,
|
|
531
|
+
modelId: result.model?.id ?? modelId
|
|
532
|
+
}, numTurns);
|
|
533
|
+
return {
|
|
534
|
+
success: true,
|
|
535
|
+
skipFulfill: false,
|
|
536
|
+
lastMessage: result.result ?? lastMessage ?? "No response from Cursor",
|
|
537
|
+
stats,
|
|
538
|
+
sessionId: agentId
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
async execute(context, sessionId) {
|
|
542
|
+
const { agent, instructions, workingDir } = context;
|
|
543
|
+
const apiKey = this.resolveApiKey();
|
|
544
|
+
if (!apiKey) return {
|
|
545
|
+
success: false,
|
|
546
|
+
skipFulfill: false,
|
|
547
|
+
lastMessage: "Missing Cursor API key (set CURSOR_API_KEY or pass apiKey in config)",
|
|
548
|
+
stats: null
|
|
549
|
+
};
|
|
550
|
+
const prompt = sessionId ? instructions : buildPrompt({
|
|
551
|
+
agent,
|
|
552
|
+
instructions
|
|
553
|
+
});
|
|
554
|
+
const model = this.resolveModel(agent.model);
|
|
555
|
+
const mcpServers = this.resolveMcpServers(agent.tools, context);
|
|
556
|
+
const settingSources = this.config.settingSources ?? [];
|
|
557
|
+
const executionTimeoutMs = this.config.executionTimeoutMs ?? DEFAULT_EXECUTION_TIMEOUT_MS;
|
|
558
|
+
debug("execute workingDir=%s sessionId=%s", workingDir, sessionId ?? "none");
|
|
559
|
+
debug("prompt: %s", prompt);
|
|
560
|
+
try {
|
|
561
|
+
const agentHandle = sessionId ? await this.withExecutionTimeout({ cancel: async () => void 0 }, executionTimeoutMs, () => Agent.resume(sessionId, {
|
|
562
|
+
apiKey,
|
|
563
|
+
model,
|
|
564
|
+
local: {
|
|
565
|
+
cwd: workingDir,
|
|
566
|
+
settingSources
|
|
567
|
+
},
|
|
568
|
+
...mcpServers && { mcpServers }
|
|
569
|
+
})) : await this.withExecutionTimeout({ cancel: async () => void 0 }, executionTimeoutMs, () => Agent.create({
|
|
570
|
+
apiKey,
|
|
571
|
+
model,
|
|
572
|
+
local: {
|
|
573
|
+
cwd: workingDir,
|
|
574
|
+
settingSources
|
|
575
|
+
},
|
|
576
|
+
...mcpServers && { mcpServers },
|
|
577
|
+
...this.config.mode && { mode: this.config.mode }
|
|
578
|
+
}));
|
|
579
|
+
const run = await this.withExecutionTimeout({ cancel: async () => void 0 }, executionTimeoutMs, () => agentHandle.send(prompt));
|
|
580
|
+
debug("run started id=%s requestId=%s", run.id, run.requestId ?? "-");
|
|
581
|
+
const { lastMessage, numTurns } = await this.withExecutionTimeout(run, executionTimeoutMs, () => this.streamRun(run));
|
|
582
|
+
const result = await this.withExecutionTimeout(run, executionTimeoutMs, () => run.wait());
|
|
583
|
+
if (result.status === "error" || result.status === "cancelled") return {
|
|
584
|
+
success: false,
|
|
585
|
+
skipFulfill: false,
|
|
586
|
+
lastMessage: result.result ?? result.status,
|
|
587
|
+
stats: mapRunResultToStats({
|
|
588
|
+
durationMs: result.durationMs,
|
|
589
|
+
usage: result.usage,
|
|
590
|
+
modelId: result.model?.id ?? model.id
|
|
591
|
+
}, numTurns),
|
|
592
|
+
sessionId: agentHandle.agentId
|
|
593
|
+
};
|
|
594
|
+
return this.mapFinishedResult(result, agentHandle.agentId, lastMessage, numTurns, model.id);
|
|
595
|
+
} catch (error) {
|
|
596
|
+
debug("error: %o", error);
|
|
597
|
+
if (error instanceof ExecutionTimeoutError) return {
|
|
598
|
+
success: false,
|
|
599
|
+
skipFulfill: false,
|
|
600
|
+
lastMessage: error.message,
|
|
601
|
+
stats: null
|
|
602
|
+
};
|
|
603
|
+
if (error instanceof CursorAgentError) return {
|
|
604
|
+
success: false,
|
|
605
|
+
skipFulfill: false,
|
|
606
|
+
lastMessage: error.message,
|
|
607
|
+
stats: null
|
|
608
|
+
};
|
|
609
|
+
return {
|
|
610
|
+
success: false,
|
|
611
|
+
skipFulfill: false,
|
|
612
|
+
lastMessage: error instanceof Error ? error.message : String(error),
|
|
613
|
+
stats: null
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
};
|
|
618
|
+
//#endregion
|
|
619
|
+
export { CursorEngine };
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@bifrost-ai/engine-cursor",
|
|
3
|
+
"version": "0.1.1-build.1783540132209",
|
|
4
|
+
"description": "Cursor SDK engine for Bifrost orchestrator v2",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"files": [
|
|
7
|
+
"dist"
|
|
8
|
+
],
|
|
9
|
+
"type": "module",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": "./dist/index.mjs",
|
|
12
|
+
"./package.json": "./package.json"
|
|
13
|
+
},
|
|
14
|
+
"publishConfig": {
|
|
15
|
+
"access": "public"
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@bifrost-ai/engine": "0.1.1-build.1783540132209",
|
|
19
|
+
"@cursor/sdk": "^1.0.22",
|
|
20
|
+
"debug": "^4.4.0"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@types/debug": "^4.1.12",
|
|
24
|
+
"@types/node": "^25.6.2",
|
|
25
|
+
"@typescript/native-preview": "latest",
|
|
26
|
+
"bumpp": "^11.1.0",
|
|
27
|
+
"typescript": "^6.0.3",
|
|
28
|
+
"vite-plus": "^0.2.0",
|
|
29
|
+
"vitest-gwt": "^4.1.0"
|
|
30
|
+
},
|
|
31
|
+
"scripts": {
|
|
32
|
+
"build": "vp pack",
|
|
33
|
+
"dev": "vp pack --watch",
|
|
34
|
+
"test": "vp test",
|
|
35
|
+
"check": "vp check"
|
|
36
|
+
}
|
|
37
|
+
}
|