From fea3f846960609ce388d42c89da11fb1092206ef Mon Sep 17 00:00:00 2001 From: "paseo-bot[bot]" <266920839+paseo-bot[bot]@users.noreply.github.com> Date: Sun, 18 Oct 2026 05:31:37 +0200 Subject: [PATCH] test(cursor): reproduce stale Fast controls for drafted and switched models Preserve a real Cursor CLI reproduction for #5279 without changing production behavior. Draft feature discovery follows the saved CLI model, and a live model switch retains the previous feature list. Co-authored-by: gengjiawen --- .../cursor-fast-5279.real.e2e.test.ts | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 packages/server/src/server/agent/providers/cursor-fast-5279.real.e2e.test.ts diff --git a/packages/server/src/server/agent/providers/cursor-fast-5279.real.e2e.test.ts b/packages/server/src/server/agent/providers/cursor-fast-5279.real.e2e.test.ts new file mode 100644 index 0000000000..8db05848e6 --- /dev/null +++ b/packages/server/src/server/agent/providers/cursor-fast-5279.real.e2e.test.ts @@ -0,0 +1,67 @@ +import { mkdir, mkdtemp, rm } from "node:fs/promises"; +import path from "node:path"; +import { expect, test } from "vitest"; + +import { createTestLogger } from "../../../test-utils/test-logger.js"; +import { CursorACPAgentClient } from "./cursor-acp-agent.js"; + +// Diagnosis-only reproduction for #5279. Requires the installed Cursor CLI; +// sends no prompts and keeps Cursor's model preferences inside this checkout. +test("Cursor Fast availability follows the drafted and active model", async () => { + const fixtures = path.resolve(".diagnose-5279"); + await mkdir(fixtures, { recursive: true }); + const root = await mkdtemp(path.join(fixtures, "cursor-")); + const client = new CursorACPAgentClient({ + logger: createTestLogger(), + command: ["cursor-agent", "acp"], + env: { + CURSOR_CONFIG_DIR: path.join(root, "config"), + CURSOR_DATA_DIR: path.join(root, "data"), + }, + }); + const hasFast = (features: { id: string }[] | undefined) => + features?.some((feature) => feature.id === "fast") ?? false; + + try { + const catalog = await client.fetchCatalog({ scope: "workspace", cwd: root, force: true }); + expect(catalog.models.some((model) => model.id === "composer-2.5")).toBe(true); + expect(catalog.models.some((model) => model.id === "kimi-k3")).toBe(true); + + const composer = await client.listFeatures({ + provider: "acp", + cwd: root, + model: "composer-2.5", + }); + console.log("Default saved model: Composer draft offers Fast =", hasFast(composer)); + expect.soft(hasFast(composer), "Composer draft must offer Fast").toBe(true); + + // A real model change persists Composer only in the isolated Cursor config. + const seed = await client.createSession({ + provider: "acp", + cwd: root, + model: "composer-2.5", + }); + await seed.close(); + + for (const model of ["composer-2.5", "kimi-k3"]) { + const features = await client.listFeatures({ provider: "acp", cwd: root, model }); + console.log("Composer saved model:", model, "draft offers Fast =", hasFast(features)); + expect.soft(hasFast(features), `Draft ${model}`).toBe(model === "composer-2.5"); + } + + const session = await client.createSession({ provider: "acp", cwd: root }); + try { + expect(hasFast(session.features)).toBe(true); + await session.setModel!("kimi-k3"); + console.log("After Composer -> Kimi: session offers Fast =", hasFast(session.features)); + expect.soft(hasFast(session.features), "Kimi session must hide Fast").toBe(false); + await expect(session.setFeature!("fast", "true")).rejects.toThrow( + "Unknown model config option: fast", + ); + } finally { + await session.close(); + } + } finally { + await rm(root, { recursive: true, force: true }); + } +}, 120_000);