pi-cursor-sdk 0.1.62 → 0.2.0
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/CHANGELOG.md +23 -0
- package/README.md +5 -5
- package/docs/cursor-live-smoke-checklist.md +4 -4
- package/docs/cursor-model-ux-spec.md +5 -3
- package/docs/cursor-native-tool-visual-audit.md +1 -1
- package/docs/cursor-testing-lessons.md +1 -1
- package/docs/platform-smoke.md +3 -3
- package/node_modules/fast-uri/index.js +37 -1
- package/node_modules/fast-uri/package.json +1 -1
- package/node_modules/fast-uri/test/security.test.js +136 -0
- package/node_modules/hono/dist/cjs/context.js +28 -13
- package/node_modules/hono/dist/cjs/helper/proxy/index.js +4 -0
- package/node_modules/hono/dist/cjs/helper/streaming/sse.js +1 -1
- package/node_modules/hono/dist/cjs/hono-base.js +3 -2
- package/node_modules/hono/dist/cjs/jsx/base.js +27 -23
- package/node_modules/hono/dist/cjs/jsx/hooks/index.js +16 -13
- package/node_modules/hono/dist/cjs/middleware/cache/index.js +103 -8
- package/node_modules/hono/dist/cjs/middleware/compress/index.js +5 -0
- package/node_modules/hono/dist/cjs/middleware/cors/index.js +2 -2
- package/node_modules/hono/dist/cjs/middleware/etag/index.js +1 -1
- package/node_modules/hono/dist/cjs/middleware/jwk/jwk.js +9 -4
- package/node_modules/hono/dist/cjs/middleware/jwt/jwt.js +9 -4
- package/node_modules/hono/dist/cjs/middleware/language/language.js +10 -6
- package/node_modules/hono/dist/cjs/middleware/method-not-allowed/index.js +90 -0
- package/node_modules/hono/dist/cjs/middleware/secure-headers/secure-headers.js +10 -4
- package/node_modules/hono/dist/cjs/request.js +7 -9
- package/node_modules/hono/dist/cjs/router/reg-exp-router/node.js +55 -56
- package/node_modules/hono/dist/cjs/router/reg-exp-router/router.js +64 -85
- package/node_modules/hono/dist/cjs/router/reg-exp-router/trie.js +13 -5
- package/node_modules/hono/dist/cjs/router.js +1 -1
- package/node_modules/hono/dist/cjs/utils/accept.js +1 -1
- package/node_modules/hono/dist/cjs/utils/cookie.js +3 -2
- package/node_modules/hono/dist/cjs/utils/url.js +8 -8
- package/node_modules/hono/dist/context.js +28 -13
- package/node_modules/hono/dist/helper/proxy/index.js +4 -0
- package/node_modules/hono/dist/helper/streaming/sse.js +1 -1
- package/node_modules/hono/dist/hono-base.js +3 -2
- package/node_modules/hono/dist/jsx/base.js +27 -23
- package/node_modules/hono/dist/jsx/hooks/index.js +16 -13
- package/node_modules/hono/dist/middleware/cache/index.js +103 -8
- package/node_modules/hono/dist/middleware/compress/index.js +5 -0
- package/node_modules/hono/dist/middleware/cors/index.js +2 -2
- package/node_modules/hono/dist/middleware/etag/index.js +1 -1
- package/node_modules/hono/dist/middleware/jwk/jwk.js +9 -4
- package/node_modules/hono/dist/middleware/jwt/jwt.js +9 -4
- package/node_modules/hono/dist/middleware/language/language.js +10 -6
- package/node_modules/hono/dist/middleware/method-not-allowed/index.js +68 -0
- package/node_modules/hono/dist/middleware/secure-headers/secure-headers.js +10 -4
- package/node_modules/hono/dist/request.js +8 -10
- package/node_modules/hono/dist/router/reg-exp-router/node.js +55 -56
- package/node_modules/hono/dist/router/reg-exp-router/router.js +64 -85
- package/node_modules/hono/dist/router/reg-exp-router/trie.js +13 -5
- package/node_modules/hono/dist/router.js +1 -1
- package/node_modules/hono/dist/types/adapter/aws-lambda/types.d.ts +9 -0
- package/node_modules/hono/dist/types/client/types.d.ts +1 -1
- package/node_modules/hono/dist/types/hono-base.d.ts +4 -3
- package/node_modules/hono/dist/types/jsx/base.d.ts +4 -2
- package/node_modules/hono/dist/types/jsx/dom/index.d.ts +5 -5
- package/node_modules/hono/dist/types/jsx/dom/intrinsic-element/components.d.ts +2 -2
- package/node_modules/hono/dist/types/jsx/dom/server.d.ts +5 -5
- package/node_modules/hono/dist/types/jsx/hooks/index.d.ts +8 -6
- package/node_modules/hono/dist/types/jsx/index.d.ts +5 -5
- package/node_modules/hono/dist/types/middleware/cache/index.d.ts +6 -4
- package/node_modules/hono/dist/types/middleware/cors/index.d.ts +1 -1
- package/node_modules/hono/dist/types/middleware/jsx-renderer/index.d.ts +2 -2
- package/node_modules/hono/dist/types/middleware/jwk/jwk.d.ts +2 -0
- package/node_modules/hono/dist/types/middleware/jwt/jwt.d.ts +2 -0
- package/node_modules/hono/dist/types/middleware/method-not-allowed/index.d.ts +49 -0
- package/node_modules/hono/dist/types/router/reg-exp-router/node.d.ts +1 -1
- package/node_modules/hono/dist/types/router/reg-exp-router/trie.d.ts +2 -1
- package/node_modules/hono/dist/types/router.d.ts +1 -1
- package/node_modules/hono/dist/types/utils/headers.d.ts +2 -2
- package/node_modules/hono/dist/types/utils/url.d.ts +1 -0
- package/node_modules/hono/dist/utils/accept.js +1 -1
- package/node_modules/hono/dist/utils/cookie.js +4 -3
- package/node_modules/hono/dist/utils/url.js +6 -7
- package/node_modules/hono/package.json +10 -3
- package/node_modules/ip-address/README.md +146 -126
- package/node_modules/ip-address/dist/common.d.ts +29 -0
- package/node_modules/ip-address/dist/common.js +44 -5
- package/node_modules/ip-address/dist/common.js.map +1 -1
- package/node_modules/ip-address/dist/ipv4.d.ts +8 -1
- package/node_modules/ip-address/dist/ipv4.js +24 -20
- package/node_modules/ip-address/dist/ipv4.js.map +1 -1
- package/node_modules/ip-address/dist/ipv6.d.ts +68 -3
- package/node_modules/ip-address/dist/ipv6.js +180 -53
- package/node_modules/ip-address/dist/ipv6.js.map +1 -1
- package/node_modules/ip-address/dist/v4/constants.js +5 -1
- package/node_modules/ip-address/dist/v4/constants.js.map +1 -1
- package/node_modules/ip-address/dist/v6/constants.js +3 -2
- package/node_modules/ip-address/dist/v6/constants.js.map +1 -1
- package/node_modules/ip-address/package.json +6 -4
- package/package.json +5 -5
- package/scripts/platform-smoke/live-suite-runner.mjs +15 -39
- package/scripts/platform-smoke/platform-build-windows.ps1 +1 -1
- package/scripts/platform-smoke/scenarios.mjs +1 -1
- package/scripts/platform-smoke.mjs +5 -5
- package/scripts/refresh-cursor-model-snapshots.mjs +16 -7
- package/shared/cursor-model-selection-identities.d.mts +21 -0
- package/shared/cursor-model-selection-identities.mjs +95 -0
- package/src/bundled-context-windows.ts +18 -12
- package/src/context.ts +1 -1
- package/src/cursor-context-tools.ts +1 -1
- package/src/cursor-display-only-trace.ts +1 -1
- package/src/cursor-fallback-models.generated.ts +875 -361
- package/src/cursor-live-run-accounting.ts +1 -1
- package/src/cursor-live-run-coordinator.ts +1 -1
- package/src/cursor-native-replay-routing.ts +1 -1
- package/src/cursor-partial-content-emitter.ts +1 -1
- package/src/cursor-pi-tool-bridge-mcp.ts +1 -1
- package/src/cursor-pi-tool-bridge-run.ts +1 -1
- package/src/cursor-pi-tool-bridge-types.ts +1 -1
- package/src/cursor-provider-lazy.ts +1 -1
- package/src/cursor-provider-live-run-drain.ts +1 -1
- package/src/cursor-provider-overflow.ts +1 -1
- package/src/cursor-provider-run-finalizer.ts +1 -1
- package/src/cursor-provider-turn-coordinator.ts +1 -1
- package/src/cursor-provider-turn-lifecycle-emitter.ts +1 -1
- package/src/cursor-provider-turn-prepare.ts +1 -1
- package/src/cursor-provider-turn-types.ts +1 -1
- package/src/cursor-provider.ts +1 -1
- package/src/cursor-run-final-text.ts +1 -1
- package/src/cursor-sdk-event-debug.ts +1 -1
- package/src/cursor-session-agent.ts +1 -1
- package/src/cursor-session-send-policy.ts +1 -1
- package/src/cursor-usage-accounting.ts +1 -1
- package/src/model-discovery.ts +32 -89
- package/node_modules/cross-spawn/node_modules/which/CHANGELOG.md +0 -166
|
@@ -19,7 +19,6 @@ import { getScenario, renderPrompt } from "./scenarios.mjs";
|
|
|
19
19
|
|
|
20
20
|
const DEFAULT_MODEL = "cursor/composer-2-5";
|
|
21
21
|
const DEFAULT_WAIT_MS = 240_000;
|
|
22
|
-
const READY_WAIT_MS = 45_000;
|
|
23
22
|
const SESSION_JSONL_WAIT_MS = 60_000;
|
|
24
23
|
const COLS = 150;
|
|
25
24
|
const ROWS = 45;
|
|
@@ -242,25 +241,10 @@ function stripANSI(text) {
|
|
|
242
241
|
return text.replace(/\x1b\[[0-9;?]*[ -/]*[@-~]/g, "").replace(/\x1b\][^\x07]*(?:\x07|\x1b\\)/g, "");
|
|
243
242
|
}
|
|
244
243
|
|
|
245
|
-
function
|
|
246
|
-
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
function shellQuote(value) {
|
|
250
|
-
return `'${String(value).replace(/'/g, `'\\''`)}'`;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
function ptySpawnCommand(piCli, args) {
|
|
254
|
-
if (process.platform !== "win32") {
|
|
255
|
-
return {
|
|
256
|
-
file: "/bin/bash",
|
|
257
|
-
args: ["-lc", ["exec", shellQuote(piCli), ...args.map(shellQuote)].join(" ")],
|
|
258
|
-
};
|
|
259
|
-
}
|
|
260
|
-
return {
|
|
261
|
-
file: "powershell.exe",
|
|
262
|
-
args: ["-NoLogo", "-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", ["&", psQuote(piCli), ...args.map(psQuote)].join(" ")],
|
|
263
|
-
};
|
|
244
|
+
function ptySpawnCommand(args) {
|
|
245
|
+
const cliEntry = resolve(process.cwd(), "node_modules", "@earendil-works", "pi-coding-agent", "dist", "cli.js");
|
|
246
|
+
if (!existsSync(cliEntry)) throw new Error(`Pi CLI entry not found: ${cliEntry}`);
|
|
247
|
+
return { file: process.execPath, args: [cliEntry, ...args] };
|
|
264
248
|
}
|
|
265
249
|
|
|
266
250
|
async function waitForSessionJsonl(sessionDir, finalMarker, startedAt, events) {
|
|
@@ -285,7 +269,7 @@ async function waitForSessionJsonl(sessionDir, finalMarker, startedAt, events) {
|
|
|
285
269
|
return files;
|
|
286
270
|
}
|
|
287
271
|
|
|
288
|
-
async function runPtyPi({ artifactDir,
|
|
272
|
+
async function runPtyPi({ artifactDir, ptyCommand, env, cwd, sessionDir, finalMarker, waitMs, abortMode, scenario }) {
|
|
289
273
|
let pty;
|
|
290
274
|
try {
|
|
291
275
|
pty = await import("node-pty");
|
|
@@ -297,8 +281,7 @@ async function runPtyPi({ artifactDir, piCli, piArgs, env, cwd, sessionDir, prom
|
|
|
297
281
|
let plain = "";
|
|
298
282
|
const events = [];
|
|
299
283
|
const startedAt = Date.now();
|
|
300
|
-
const { file, args } =
|
|
301
|
-
writeFileSync(join(artifactDir, "pty-spawn-command.json"), JSON.stringify({ file, args, cwd, fileExists: existsSync(file), cwdExists: existsSync(cwd) }, null, 2));
|
|
284
|
+
const { file, args } = ptyCommand;
|
|
302
285
|
let child;
|
|
303
286
|
try {
|
|
304
287
|
child = pty.spawn(file, args, {
|
|
@@ -322,15 +305,7 @@ async function runPtyPi({ artifactDir, piCli, piArgs, env, cwd, sessionDir, prom
|
|
|
322
305
|
events.push({ type: "exit", elapsedMs: Date.now() - startedAt, code: event.exitCode, signal: event.signal });
|
|
323
306
|
});
|
|
324
307
|
|
|
325
|
-
const
|
|
326
|
-
while (!/(?:composer-2-5|escape interrupt|ctrl\+c\/ctrl\+d)/i.test(plain) && Date.now() - readyStartedAt < READY_WAIT_MS) {
|
|
327
|
-
await delay(500);
|
|
328
|
-
}
|
|
329
|
-
events.push({ type: "ready_wait_finished", elapsedMs: Date.now() - startedAt, ready: /(?:composer-2-5|escape interrupt|ctrl\+c\/ctrl\+d)/i.test(plain) });
|
|
330
|
-
child.write(`\x1b[200~${prompt}\x1b[201~\r`);
|
|
331
|
-
events.push({ type: "prompt_sent", elapsedMs: Date.now() - startedAt, bytes: prompt.length });
|
|
332
|
-
await delay(1_000);
|
|
333
|
-
const responseStartOffset = plain.length;
|
|
308
|
+
const responseStartOffset = 0;
|
|
334
309
|
events.push({ type: "response_watch_started", elapsedMs: Date.now() - startedAt, offset: responseStartOffset });
|
|
335
310
|
|
|
336
311
|
let observed = false;
|
|
@@ -572,6 +547,7 @@ async function main() {
|
|
|
572
547
|
const suiteEnv = {
|
|
573
548
|
...process.env,
|
|
574
549
|
...scenario.env,
|
|
550
|
+
PI_OFFLINE: "1",
|
|
575
551
|
...(args.suite === "cursor-abort-cleanup" ? { PLATFORM_ABORT_MARKER: "SHOULD_NOT_PRINT" } : {}),
|
|
576
552
|
PI_CODING_AGENT_DIR: agentDir,
|
|
577
553
|
PI_CURSOR_SDK_EVENT_DEBUG_DIR: debugDir,
|
|
@@ -581,21 +557,21 @@ async function main() {
|
|
|
581
557
|
if (args.suite === "cursor-abort-cleanup") writeProcessSnapshot(logDir, "process-before", platform);
|
|
582
558
|
const prompt = renderPrompt(scenario, platform);
|
|
583
559
|
writeFileSync(join(artifactDir, "prompt.txt"), prompt);
|
|
584
|
-
const piArgs = ["--approve", "--cursor-no-fast", "--cursor-mode", "agent", "--model", args.model, "--session-dir", sessionDir, "--session-id", `platform-${args.suite}-${Date.now()}
|
|
560
|
+
const piArgs = ["--approve", "--cursor-no-fast", "--cursor-mode", "agent", "--model", args.model, "--session-dir", sessionDir, "--session-id", `platform-${args.suite}-${Date.now()}`, prompt];
|
|
561
|
+
const ptyCommand = ptySpawnCommand(piArgs);
|
|
585
562
|
writeFileSync(join(artifactDir, "pi-command.json"), JSON.stringify({
|
|
586
|
-
|
|
587
|
-
args: piArgs,
|
|
563
|
+
...ptyCommand,
|
|
588
564
|
cwd: workspaceDir,
|
|
589
|
-
|
|
565
|
+
fileExists: existsSync(ptyCommand.file),
|
|
566
|
+
cwdExists: existsSync(workspaceDir),
|
|
567
|
+
env: Object.fromEntries(Object.entries(suiteEnv).filter(([key]) => key.startsWith("PI_CURSOR_") || key === "PI_CODING_AGENT_DIR" || key === "PI_OFFLINE" || key === "TERM")),
|
|
590
568
|
}, null, 2));
|
|
591
569
|
const ptyResult = await runPtyPi({
|
|
592
570
|
artifactDir,
|
|
593
|
-
|
|
594
|
-
piArgs,
|
|
571
|
+
ptyCommand,
|
|
595
572
|
env: suiteEnv,
|
|
596
573
|
cwd: workspaceDir,
|
|
597
574
|
sessionDir,
|
|
598
|
-
prompt,
|
|
599
575
|
finalMarker: scenario.finalMarker,
|
|
600
576
|
waitMs: args.waitMs,
|
|
601
577
|
abortMode: args.suite === "cursor-abort-cleanup",
|
|
@@ -62,7 +62,7 @@ Write-SectionFile "NPM_CI_STDOUT" $NpmCiOut
|
|
|
62
62
|
Write-SectionFile "NPM_CI_STDERR" $NpmCiErr
|
|
63
63
|
|
|
64
64
|
Write-Output "=== check:platform-smoke ==="
|
|
65
|
-
& npm.cmd run check:platform-smoke 1> $CheckPlatformSmokeOut 2> $CheckPlatformSmokeErr
|
|
65
|
+
& npm.cmd run check:platform-smoke -- --testTimeout=15000 1> $CheckPlatformSmokeOut 2> $CheckPlatformSmokeErr
|
|
66
66
|
$CHECK_PLATFORM_SMOKE_EXIT = Exit-CodeFromLastCommand
|
|
67
67
|
Write-Output "PLATFORM_CHECK_PLATFORM_SMOKE_EXIT=$CHECK_PLATFORM_SMOKE_EXIT"
|
|
68
68
|
Write-SectionFile "CHECK_PLATFORM_SMOKE_STDOUT" $CheckPlatformSmokeOut
|
|
@@ -108,7 +108,7 @@ NATIVE_MATRIX_OK package=<name> grep=<yes/no> find=<yes/no> list=<yes/no> shell=
|
|
|
108
108
|
PI_CURSOR_EXPOSE_BUILTIN_TOOLS: "0",
|
|
109
109
|
PI_CURSOR_SDK_EVENT_DEBUG: "1",
|
|
110
110
|
},
|
|
111
|
-
promptTemplate: "
|
|
111
|
+
promptTemplate: "Join HTTP1, LIVE, and OK with underscores and reply with only the result.",
|
|
112
112
|
finalMarker: "HTTP1_LIVE_OK",
|
|
113
113
|
requiredCards: ["http1-status"],
|
|
114
114
|
},
|
|
@@ -28,7 +28,7 @@ function printHelp() {
|
|
|
28
28
|
|
|
29
29
|
Commands:
|
|
30
30
|
doctor Run all preflight checks (no Cursor tokens)
|
|
31
|
-
run --target <names> Run one or more comma-separated targets
|
|
31
|
+
run --target <names> Run one or more comma-separated targets sequentially
|
|
32
32
|
run --suite <name> Run one suite on all or specified targets
|
|
33
33
|
run --target <n> --suite <n>
|
|
34
34
|
|
|
@@ -242,14 +242,14 @@ async function main() {
|
|
|
242
242
|
}
|
|
243
243
|
|
|
244
244
|
const startedAt = new Date().toISOString();
|
|
245
|
-
const
|
|
245
|
+
const results = [];
|
|
246
|
+
for (const targetName of targets) {
|
|
246
247
|
console.log(`\n=== Target: ${targetName} ===`);
|
|
247
248
|
const result = args.suite
|
|
248
249
|
? await runSuite(targetName, suites[0])
|
|
249
250
|
: await runTarget(targetName, suites);
|
|
250
|
-
|
|
251
|
-
}
|
|
252
|
-
const results = await Promise.all(targetRuns);
|
|
251
|
+
results.push({ targetName, result });
|
|
252
|
+
}
|
|
253
253
|
const finishedAt = new Date().toISOString();
|
|
254
254
|
const latest = writeLatestPlatformSmokeIndex(config, results, {
|
|
255
255
|
startedAt,
|
|
@@ -4,6 +4,7 @@ import { Cursor } from "@cursor/sdk";
|
|
|
4
4
|
import { defaultApiKeyFromEnv, parseArgv } from "./lib/cursor-cli-args.mjs";
|
|
5
5
|
import { scrubSensitiveText } from "../shared/cursor-sensitive-text.mjs";
|
|
6
6
|
import { createScriptFail } from "./lib/cursor-script-fail.mjs";
|
|
7
|
+
import { normalizeCursorContextWindowEntries } from "../shared/cursor-model-selection-identities.mjs";
|
|
7
8
|
|
|
8
9
|
const FALLBACK_MODELS_PATH = "src/cursor-fallback-models.generated.ts";
|
|
9
10
|
const CONTEXT_WINDOWS_PATH = "src/bundled-context-windows.ts";
|
|
@@ -40,7 +41,9 @@ Notes:
|
|
|
40
41
|
- This script prints model IDs/counts only; it never prints API keys.
|
|
41
42
|
- Cursor.models.list() is the source of truth for fallback catalog metadata.
|
|
42
43
|
- Checkpoint-derived context windows are optional input because collecting them
|
|
43
|
-
requires successful local SDK runs; this script does not start agents
|
|
44
|
+
requires successful local SDK runs; this script does not start agents.
|
|
45
|
+
- Context-window keys are limited to current selectable model IDs. Redundant
|
|
46
|
+
:fast/:slow aliases that send the default parameters collapse to one key.`);
|
|
44
47
|
}
|
|
45
48
|
|
|
46
49
|
const fail = createScriptFail("refresh-cursor-snapshots");
|
|
@@ -156,9 +159,10 @@ function hasContextParameter(model) {
|
|
|
156
159
|
}
|
|
157
160
|
|
|
158
161
|
function formatContextWindows(models, checkpointWindows, fallbackContextWindow) {
|
|
159
|
-
const
|
|
160
|
-
const
|
|
161
|
-
|
|
162
|
+
const merged = normalizeCursorContextWindowEntries(models, parseExistingContextWindows(), "existing bundled context windows");
|
|
163
|
+
for (const [modelId, contextWindow] of normalizeCursorContextWindowEntries(models, checkpointWindows, "checkpoint context windows")) {
|
|
164
|
+
merged.set(modelId, contextWindow);
|
|
165
|
+
}
|
|
162
166
|
for (const model of models) {
|
|
163
167
|
if (!hasContextParameter(model) && !merged.has(model.id)) merged.set(model.id, fallbackContextWindow);
|
|
164
168
|
}
|
|
@@ -167,7 +171,7 @@ function formatContextWindows(models, checkpointWindows, fallbackContextWindow)
|
|
|
167
171
|
const date = new Date().toISOString().slice(0, 10);
|
|
168
172
|
const sorted = [...merged.entries()].sort(([a], [b]) => (a === "default" ? -1 : b === "default" ? 1 : a.localeCompare(b)));
|
|
169
173
|
const lines = sorted.map(([modelId, contextWindow]) => `\t${JSON.stringify(modelId)}: ${contextWindow},`);
|
|
170
|
-
return `// Generated from Cursor SDK checkpoint tokenDetails.maxTokens on ${date}.\n// Refresh with: npm run refresh:cursor-snapshots -- --write --context-windows ~/.pi/agent/cursor-sdk-context-windows.json\n//
|
|
174
|
+
return `// Generated from Cursor SDK checkpoint tokenDetails.maxTokens on ${date}.\n// Refresh with: npm run refresh:cursor-snapshots -- --write --context-windows ~/.pi/agent/cursor-sdk-context-windows.json\n// Keys are current selectable model IDs. Equivalent default :fast/:slow selections\n// collapse to one key; stale and ambiguous aliases are omitted. Values are observed\n// or conservative default/non-Max-mode limits and may override a catalog context\n// label when the completed SDK checkpoint reports a different effective limit.\nexport const BUNDLED_CONTEXT_WINDOWS = {\n${lines.join("\n")}\n} as const satisfies Record<string, number>;\n`;
|
|
171
175
|
}
|
|
172
176
|
|
|
173
177
|
const args = parseRefreshArgs(process.argv.slice(2));
|
|
@@ -184,11 +188,16 @@ if (!Array.isArray(rawModels) || rawModels.length === 0) fail("Cursor.models.lis
|
|
|
184
188
|
const models = rawModels.map(sanitizeModelItem).sort((a, b) => a.id.localeCompare(b.id));
|
|
185
189
|
const checkpointWindows = parseContextWindowsFile(args.contextWindowsPath);
|
|
186
190
|
const fallbackSource = formatFallbackModels(models, sdkVersion);
|
|
187
|
-
|
|
191
|
+
let contextWindowSource;
|
|
192
|
+
try {
|
|
193
|
+
contextWindowSource = args.contextWindowsPath ? formatContextWindows(models, checkpointWindows, args.fallbackContextWindow) : undefined;
|
|
194
|
+
} catch (error) {
|
|
195
|
+
fail(error instanceof Error ? error.message : String(error));
|
|
196
|
+
}
|
|
188
197
|
const existingContextWindowCount = parseExistingContextWindows().size;
|
|
189
198
|
|
|
190
199
|
console.log(`Fetched ${models.length} Cursor models with @cursor/sdk@${sdkVersion}.`);
|
|
191
|
-
console.log(`Context windows: ${checkpointWindows.size} checkpoint
|
|
200
|
+
console.log(`Context windows: ${checkpointWindows.size} checkpoint input(s), ${existingContextWindowCount} existing bundled entr${existingContextWindowCount === 1 ? "y" : "ies"}.`);
|
|
192
201
|
console.log(`First models: ${models.slice(0, 8).map((model) => model.id).join(", ")}${models.length > 8 ? ", ..." : ""}`);
|
|
193
202
|
|
|
194
203
|
if (args.check) {
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ModelListItem } from "@cursor/sdk";
|
|
2
|
+
|
|
3
|
+
export interface CursorModelSelectionIdentity {
|
|
4
|
+
model: ModelListItem;
|
|
5
|
+
selectionModelId: string;
|
|
6
|
+
context?: string;
|
|
7
|
+
fastOverride?: boolean;
|
|
8
|
+
piModelId: string;
|
|
9
|
+
contextWindowKey: string;
|
|
10
|
+
baseContextWindowKey: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export declare function getCursorModelSelectionIdentities(
|
|
14
|
+
models: readonly ModelListItem[],
|
|
15
|
+
): CursorModelSelectionIdentity[];
|
|
16
|
+
|
|
17
|
+
export declare function normalizeCursorContextWindowEntries(
|
|
18
|
+
models: readonly ModelListItem[],
|
|
19
|
+
entries: ReadonlyMap<string, number>,
|
|
20
|
+
source?: string,
|
|
21
|
+
): Map<string, number>;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
function getParameter(model, id) {
|
|
2
|
+
return (model.parameters ?? []).find((parameter) => parameter.id === id);
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
function getDefaultParam(model, id) {
|
|
6
|
+
const variant = (model.variants ?? []).find((candidate) => candidate.isDefault) ?? model.variants?.[0];
|
|
7
|
+
return variant?.params?.find((param) => param.id === id)?.value.toLowerCase();
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function getAmbiguousAliases(models) {
|
|
11
|
+
const ownersByAlias = new Map();
|
|
12
|
+
for (const model of models) {
|
|
13
|
+
for (const rawAlias of model.aliases ?? []) {
|
|
14
|
+
const alias = rawAlias.trim();
|
|
15
|
+
if (!alias || alias === model.id) continue;
|
|
16
|
+
const owners = ownersByAlias.get(alias) ?? new Set();
|
|
17
|
+
owners.add(model.id);
|
|
18
|
+
ownersByAlias.set(alias, owners);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return new Set([...ownersByAlias].filter(([, owners]) => owners.size > 1).map(([alias]) => alias));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function getSelectableIds(model, reservedIds, ambiguousAliases) {
|
|
25
|
+
const ids = [model.id];
|
|
26
|
+
for (const rawAlias of model.aliases ?? []) {
|
|
27
|
+
const alias = rawAlias.trim();
|
|
28
|
+
if (!alias || alias === model.id || ids.includes(alias) || reservedIds.has(alias) || ambiguousAliases.has(alias)) continue;
|
|
29
|
+
ids.push(alias);
|
|
30
|
+
}
|
|
31
|
+
return ids;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function encodePiModelId(modelId, context, fastOverride) {
|
|
35
|
+
const contextQualified = context ? `${modelId}@${context}` : modelId;
|
|
36
|
+
if (fastOverride === true) return `${contextQualified}:fast`;
|
|
37
|
+
if (fastOverride === false) return `${contextQualified}:slow`;
|
|
38
|
+
return contextQualified;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function getCursorModelSelectionIdentities(models) {
|
|
42
|
+
const sortedModels = [...models].sort((a, b) => a.id.localeCompare(b.id));
|
|
43
|
+
const reservedIds = new Set(models.map((model) => model.id));
|
|
44
|
+
const ambiguousAliases = getAmbiguousAliases(models);
|
|
45
|
+
const usedPiModelIds = new Set();
|
|
46
|
+
const identities = [];
|
|
47
|
+
|
|
48
|
+
for (const model of sortedModels) {
|
|
49
|
+
const contextValues = getParameter(model, "context")?.values?.map(({ value }) => value) ?? [];
|
|
50
|
+
const contexts = contextValues.length > 0 ? contextValues : [undefined];
|
|
51
|
+
const hasFast = getParameter(model, "fast") !== undefined;
|
|
52
|
+
const fastOverrides = hasFast ? [undefined, true, false] : [undefined];
|
|
53
|
+
const defaultFast = getDefaultParam(model, "fast");
|
|
54
|
+
|
|
55
|
+
for (const selectionModelId of getSelectableIds(model, reservedIds, ambiguousAliases)) {
|
|
56
|
+
for (const context of contexts) {
|
|
57
|
+
for (const fastOverride of fastOverrides) {
|
|
58
|
+
const piModelId = encodePiModelId(selectionModelId, context, fastOverride);
|
|
59
|
+
if (usedPiModelIds.has(piModelId)) continue;
|
|
60
|
+
usedPiModelIds.add(piModelId);
|
|
61
|
+
const defaultEquivalent =
|
|
62
|
+
(fastOverride === true && defaultFast === "true") || (fastOverride === false && defaultFast === "false");
|
|
63
|
+
identities.push({
|
|
64
|
+
model,
|
|
65
|
+
selectionModelId,
|
|
66
|
+
...(context ? { context } : {}),
|
|
67
|
+
...(fastOverride !== undefined ? { fastOverride } : {}),
|
|
68
|
+
piModelId,
|
|
69
|
+
contextWindowKey: defaultEquivalent ? encodePiModelId(selectionModelId, context) : piModelId,
|
|
70
|
+
baseContextWindowKey: encodePiModelId(model.id, context, defaultEquivalent ? undefined : fastOverride),
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return identities;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function normalizeCursorContextWindowEntries(models, entries, source = "context windows") {
|
|
81
|
+
const canonicalBySelectableId = new Map(
|
|
82
|
+
getCursorModelSelectionIdentities(models).map(({ piModelId, contextWindowKey }) => [piModelId, contextWindowKey]),
|
|
83
|
+
);
|
|
84
|
+
const normalized = new Map();
|
|
85
|
+
for (const [modelId, contextWindow] of entries) {
|
|
86
|
+
const canonicalId = modelId === "default" ? modelId : canonicalBySelectableId.get(modelId);
|
|
87
|
+
if (!canonicalId) continue;
|
|
88
|
+
const existing = normalized.get(canonicalId);
|
|
89
|
+
if (existing !== undefined && existing !== contextWindow) {
|
|
90
|
+
throw new Error(`${source} assigns conflicting windows to equivalent selection ${canonicalId}: ${existing} and ${contextWindow}`);
|
|
91
|
+
}
|
|
92
|
+
normalized.set(canonicalId, contextWindow);
|
|
93
|
+
}
|
|
94
|
+
return normalized;
|
|
95
|
+
}
|
|
@@ -1,30 +1,36 @@
|
|
|
1
|
-
// Generated from Cursor SDK checkpoint tokenDetails.maxTokens on 2026-
|
|
1
|
+
// Generated from Cursor SDK checkpoint tokenDetails.maxTokens on 2026-08-02.
|
|
2
2
|
// Refresh with: npm run refresh:cursor-snapshots -- --write --context-windows ~/.pi/agent/cursor-sdk-context-windows.json
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
3
|
+
// Keys are current selectable model IDs. Equivalent default :fast/:slow selections
|
|
4
|
+
// collapse to one key; stale and ambiguous aliases are omitted. Values are observed
|
|
5
|
+
// or conservative default/non-Max-mode limits and may override a catalog context
|
|
6
|
+
// label when the completed SDK checkpoint reports a different effective limit.
|
|
7
7
|
export const BUNDLED_CONTEXT_WINDOWS = {
|
|
8
8
|
"default": 200000,
|
|
9
|
+
"claude-fable-5@300k": 300000,
|
|
9
10
|
"claude-haiku-4-5": 200000,
|
|
10
11
|
"claude-opus-4-5": 200000,
|
|
11
|
-
"
|
|
12
|
+
"claude-opus-4-8@1m": 300000,
|
|
12
13
|
"composer-2": 200000,
|
|
13
14
|
"composer-2.5": 200000,
|
|
14
15
|
"gemini-2.5-flash": 200000,
|
|
15
16
|
"gemini-3-flash": 200000,
|
|
16
17
|
"gemini-3.1-pro": 200000,
|
|
18
|
+
"gemini-3.5-flash": 200000,
|
|
19
|
+
"gemini-3.6-flash": 200000,
|
|
20
|
+
"glm-5.2": 200000,
|
|
21
|
+
"gpt-5-5@272k": 272000,
|
|
17
22
|
"gpt-5-mini": 272000,
|
|
18
23
|
"gpt-5.1": 272000,
|
|
19
|
-
"gpt-5.1-codex-max": 272000,
|
|
20
|
-
"gpt-5.1-codex-mini": 272000,
|
|
21
24
|
"gpt-5.2": 272000,
|
|
22
|
-
"gpt-5.2-codex": 272000,
|
|
23
25
|
"gpt-5.3-codex": 272000,
|
|
24
|
-
"gpt-5.3-codex-spark": 128000,
|
|
25
26
|
"gpt-5.4-mini": 272000,
|
|
26
27
|
"gpt-5.4-nano": 272000,
|
|
27
28
|
"gpt-5.5@272k": 272000,
|
|
28
|
-
"
|
|
29
|
-
"
|
|
29
|
+
"gpt-5.6@1m": 272000,
|
|
30
|
+
"grok-4.5": 256000,
|
|
31
|
+
"grok-4.5:slow": 256000,
|
|
32
|
+
"kimi-k2.7-code": 200000,
|
|
33
|
+
"kimi-k3": 200000,
|
|
34
|
+
"opus-4.8@1m": 300000,
|
|
35
|
+
"opus-4.8@300k": 300000,
|
|
30
36
|
} as const satisfies Record<string, number>;
|
package/src/context.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import type { Context, Message, ToolCall } from "@earendil-works/pi-ai
|
|
2
|
+
import type { Context, Message, ToolCall } from "@earendil-works/pi-ai";
|
|
3
3
|
import { convertToLlm } from "@earendil-works/pi-coding-agent";
|
|
4
4
|
import type { AgentModeOption, SDKImage } from "@cursor/sdk";
|
|
5
5
|
import { CURSOR_PI_BRIDGE_PREFERENCE_TEXT } from "./cursor-bridge-contract.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Context } from "@earendil-works/pi-ai
|
|
1
|
+
import type { Context } from "@earendil-works/pi-ai";
|
|
2
2
|
|
|
3
3
|
/** Tool names from the provider context snapshot at stream start (not live pi.getActiveTools()). */
|
|
4
4
|
export function getActiveContextToolNames(context: Context): ReadonlySet<string> | undefined {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AssistantMessage, AssistantMessageEventStream } from "@earendil-works/pi-ai
|
|
1
|
+
import type { AssistantMessage, AssistantMessageEventStream } from "@earendil-works/pi-ai";
|
|
2
2
|
|
|
3
3
|
function withDisplayOnlyThinking(partial: AssistantMessage, thinking: string): AssistantMessage {
|
|
4
4
|
return { ...partial, content: [...partial.content, { type: "thinking", thinking }] };
|