import * as Effect from "effect/Effect";import type * as EffectAcpSchema from "effect-acp/schema";import { describe, expect, it } from "vite-plus/test"; import { applyCursorAcpModelSelection, buildCursorAcpSpawnInput } from "./CursorAcpSupport.ts"; const parameterizedGpt54ConfigOptions: ReadonlyArray<EffectAcpSchema.SessionConfigOption> = [ { id: "model", name: "Model", category: "model", type: "select", currentValue: "gpt-5.4-medium-fast", options: [{ value: "gpt-5.4-medium-fast", name: "GPT-5.4" }], }, { id: "reasoning", name: "Reasoning", category: "thought_level", type: "select", currentValue: "medium", options: [ { value: "low", name: "Low" }, { value: "medium", name: "Medium" }, { value: "high", name: "High" }, { value: "extra-high", name: "Extra High" }, ], }, { id: "context", name: "Context", category: "model_config", type: "select", currentValue: "272k", options: [ { value: "272k", name: "272K" }, { value: "1m", name: "1M" }, ], }, { id: "fast", name: "Fast", category: "model_config", type: "select", currentValue: "false", options: [ { value: "false", name: "Off" }, { value: "true", name: "Fast" }, ], },]; describe("buildCursorAcpSpawnInput", () => { it("builds the default Cursor ACP command", () => { expect(buildCursorAcpSpawnInput(undefined, "/tmp/project")).toEqual({ command: "cursor-agent", args: ["acp"], cwd: "/tmp/project", }); }); it("includes the configured api endpoint when present", () => { expect( buildCursorAcpSpawnInput( { binaryPath: "/usr/local/bin/agent", apiEndpoint: "http://localhost:3000", }, "/tmp/project", ), ).toEqual({ command: "/usr/local/bin/agent", args: ["-e", "http://localhost:3000", "acp"], cwd: "/tmp/project", }); }); it("forces approval in full-access mode", () => { expect(buildCursorAcpSpawnInput(undefined, "/tmp/project", undefined, "full-access")).toEqual({ command: "cursor-agent", args: ["--force", "acp"], cwd: "/tmp/project", }); }); it("uses Cursor auto-review in auto mode", () => { expect(buildCursorAcpSpawnInput(undefined, "/tmp/project", undefined, "auto")).toEqual({ command: "cursor-agent", args: ["--auto-review", "acp"], cwd: "/tmp/project", }); }); it.each(["approval-required", "auto-accept-edits"] as const)( "does not relax approval in %s mode", (runtimeMode) => { expect(buildCursorAcpSpawnInput(undefined, "/tmp/project", undefined, runtimeMode)).toEqual({ command: "cursor-agent", args: ["acp"], cwd: "/tmp/project", }); }, );}); describe("applyCursorAcpModelSelection", () => { it("sets the base model before applying separate config options", async () => { const calls: Array< | { readonly type: "model"; readonly value: string } | { readonly type: "config"; readonly configId: string; readonly value: string | boolean } > = []; const runtime = { getConfigOptions: Effect.succeed(parameterizedGpt54ConfigOptions), setModel: (value: string) => Effect.sync(() => { calls.push({ type: "model", value }); }), setConfigOption: (configId: string, value: string | boolean) => Effect.sync(() => { calls.push({ type: "config", configId, value }); }), }; await Effect.runPromise( applyCursorAcpModelSelection({ runtime, model: "gpt-5.4-medium-fast[reasoning=medium,context=272k]", selections: [ { id: "reasoning", value: "xhigh" }, { id: "contextWindow", value: "1m" }, { id: "fastMode", value: true }, ], mapError: ({ step, configId, cause }) => step === "set-config-option" ? `failed to set config option ${configId}: ${cause.message}` : `failed to set model: ${cause.message}`, }), ); expect(calls).toEqual([ { type: "model", value: "gpt-5.4-medium-fast" }, { type: "config", configId: "reasoning", value: "extra-high" }, { type: "config", configId: "context", value: "1m" }, { type: "config", configId: "fast", value: "true" }, ]); });});
import * as Effect from "effect/Effect";
import type * as EffectAcpSchema from "effect-acp/schema";
import { describe, expect, it } from "vite-plus/test";
import { applyCursorAcpModelSelection, buildCursorAcpSpawnInput } from "./CursorAcpSupport.ts";
const parameterizedGpt54ConfigOptions: ReadonlyArray<EffectAcpSchema.SessionConfigOption> = [
{
id: "model",
name: "Model",
category: "model",
type: "select",
currentValue: "gpt-5.4-medium-fast",
options: [{ value: "gpt-5.4-medium-fast", name: "GPT-5.4" }],
},
{
id: "reasoning",
name: "Reasoning",
category: "thought_level",
type: "select",
currentValue: "medium",
options: [
{ value: "low", name: "Low" },
{ value: "medium", name: "Medium" },
{ value: "high", name: "High" },
{ value: "extra-high", name: "Extra High" },
],
},
{
id: "context",
name: "Context",
category: "model_config",
type: "select",
currentValue: "272k",
options: [
{ value: "272k", name: "272K" },
{ value: "1m", name: "1M" },
],
},
{
id: "fast",
name: "Fast",
category: "model_config",
type: "select",
currentValue: "false",
options: [
{ value: "false", name: "Off" },
{ value: "true", name: "Fast" },
],
},
];
describe("buildCursorAcpSpawnInput", () => {
it("builds the default Cursor ACP command", () => {
expect(buildCursorAcpSpawnInput(undefined, "/tmp/project")).toEqual({
command: "cursor-agent",
args: ["acp"],
cwd: "/tmp/project",
});
});
it("includes the configured api endpoint when present", () => {
expect(
buildCursorAcpSpawnInput(
{
binaryPath: "/usr/local/bin/agent",
apiEndpoint: "http://localhost:3000",
},
"/tmp/project",
),
).toEqual({
command: "/usr/local/bin/agent",
args: ["-e", "http://localhost:3000", "acp"],
cwd: "/tmp/project",
});
});
it("forces approval in full-access mode", () => {
expect(buildCursorAcpSpawnInput(undefined, "/tmp/project", undefined, "full-access")).toEqual({
command: "cursor-agent",
args: ["--force", "acp"],
cwd: "/tmp/project",
});
});
it("uses Cursor auto-review in auto mode", () => {
expect(buildCursorAcpSpawnInput(undefined, "/tmp/project", undefined, "auto")).toEqual({
command: "cursor-agent",
args: ["--auto-review", "acp"],
cwd: "/tmp/project",
});
});
it.each(["approval-required", "auto-accept-edits"] as const)(
"does not relax approval in %s mode",
(runtimeMode) => {
expect(buildCursorAcpSpawnInput(undefined, "/tmp/project", undefined, runtimeMode)).toEqual({
command: "cursor-agent",
args: ["acp"],
cwd: "/tmp/project",
});
},
);
});
describe("applyCursorAcpModelSelection", () => {
it("sets the base model before applying separate config options", async () => {
const calls: Array<
| { readonly type: "model"; readonly value: string }
| { readonly type: "config"; readonly configId: string; readonly value: string | boolean }
> = [];
const runtime = {
getConfigOptions: Effect.succeed(parameterizedGpt54ConfigOptions),
setModel: (value: string) =>
Effect.sync(() => {
calls.push({ type: "model", value });
}),
setConfigOption: (configId: string, value: string | boolean) =>
Effect.sync(() => {
calls.push({ type: "config", configId, value });
}),
};
await Effect.runPromise(
applyCursorAcpModelSelection({
runtime,
model: "gpt-5.4-medium-fast[reasoning=medium,context=272k]",
selections: [
{ id: "reasoning", value: "xhigh" },
{ id: "contextWindow", value: "1m" },
{ id: "fastMode", value: true },
],
mapError: ({ step, configId, cause }) =>
step === "set-config-option"
? `failed to set config option ${configId}: ${cause.message}`
: `failed to set model: ${cause.message}`,
}),
);
expect(calls).toEqual([
{ type: "model", value: "gpt-5.4-medium-fast" },
{ type: "config", configId: "reasoning", value: "extra-high" },
{ type: "config", configId: "context", value: "1m" },
{ type: "config", configId: "fast", value: "true" },
]);
});
});