Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 23 additions & 15 deletions apps/host-daemon/src/provider-cli-health.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ const CLAUDE_CODE_DEFINITION: ProviderCliDefinition = {
const CURSOR_DEFINITION: ProviderCliDefinition = {
key: "cursor",
displayName: "Cursor",
executableName: "agent",
executableName: "cursor-agent",
npmPackageName: null,
minimumSupportedVersion: null,
installCommand: {
Expand All @@ -225,8 +225,8 @@ const CURSOR_DEFINITION: ProviderCliDefinition = {
},
updateCommand: {
commandKind: "exec",
displayCommand: "agent update",
command: "agent",
displayCommand: "cursor-agent update",
command: "cursor-agent",
args: ["update"],
},
};
Expand Down Expand Up @@ -280,8 +280,12 @@ function installMissingClaudeCommands(
function installMissingCursorCommands(
runner: FakeProviderCliCommandRunner,
): void {
runner.setExit("which", ["agent"], 1, "agent not found");
runner.setSpawnError("agent", ["--version"], "spawn agent ENOENT");
runner.setExit("which", ["cursor-agent"], 1, "cursor-agent not found");
runner.setSpawnError(
"cursor-agent",
["--version"],
"spawn cursor-agent ENOENT",
);
runner.setSuccess("npm", ["prefix", "-g"], "/usr/local\n");
}

Expand Down Expand Up @@ -342,8 +346,12 @@ function installCurrentClaudeCommands(
function installCurrentCursorCommands(
runner: FakeProviderCliCommandRunner,
): void {
runner.setSuccess("which", ["agent"], "/Users/me/.local/bin/agent\n");
runner.setSuccess("agent", ["--version"], "agent 1.2.3\n");
runner.setSuccess(
"which",
["cursor-agent"],
"/Users/me/.local/bin/cursor-agent\n",
);
runner.setSuccess("cursor-agent", ["--version"], "cursor-agent 1.2.3\n");
runner.setSuccess("npm", ["prefix", "-g"], "/usr/local\n");
}

Expand Down Expand Up @@ -442,7 +450,7 @@ describe("provider CLI health", () => {

expect(status).toEqual({
displayName: "Cursor",
executableName: "agent",
executableName: "cursor-agent",
executablePath: null,
installed: false,
installSource: "notInstalled",
Expand Down Expand Up @@ -568,7 +576,7 @@ describe("provider CLI health", () => {
expect(runner.commandLines()).toContain(
"npm view @anthropic-ai/claude-code version",
);
expect(runner.commandLines()).toContain("which agent");
expect(runner.commandLines()).toContain("which cursor-agent");
expect(runner.commandLines()).not.toContain("npm view cursor version");
});

Expand Down Expand Up @@ -607,14 +615,14 @@ describe("provider CLI health", () => {
]);
});

it("checks Cursor installation using its agent executable", async () => {
it("checks Cursor installation using its namespaced executable", async () => {
const runner = new FakeProviderCliCommandRunner();
runner.setExit("which", ["agent"], 1, "agent not found");
runner.setExit("which", ["cursor-agent"], 1, "cursor-agent not found");

await expect(
isProviderCliInstalled("cursor", { runner }),
).resolves.toBe(false);
expect(runner.commandLines()).toEqual(["which agent"]);
await expect(isProviderCliInstalled("cursor", { runner })).resolves.toBe(
false,
);
expect(runner.commandLines()).toEqual(["which cursor-agent"]);
});

it("streams failed npm installs without hiding the exit status", async () => {
Expand Down
6 changes: 3 additions & 3 deletions apps/host-daemon/src/provider-cli-health.ts
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ const PROVIDER_CLI_DEFINITIONS = {
cursor: {
key: "cursor",
displayName: "Cursor",
executableName: "agent",
executableName: "cursor-agent",
npmPackageName: null,
minimumSupportedVersion: null,
installCommand: {
Expand All @@ -301,8 +301,8 @@ const PROVIDER_CLI_DEFINITIONS = {
},
updateCommand: {
commandKind: "exec",
displayCommand: "agent update",
command: "agent",
displayCommand: "cursor-agent update",
command: "cursor-agent",
args: ["update"],
},
},
Expand Down
2 changes: 1 addition & 1 deletion apps/server/src/services/system/onboarding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ const PLAN_CAPABLE_BY_PROVIDER_ID = new Map<string, PlanCapableAgentConfig>([
"acp-cursor",
{
cliKey: "cursor",
loginCommand: "agent login",
loginCommand: "cursor-agent login",
},
],
]);
Expand Down
14 changes: 10 additions & 4 deletions packages/agent-runtime/src/acp/adapter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ function createAdapter(): AcpProviderAdapter {
});
}

const CURSOR_LIST_COMMAND = { command: "agent", args: ["--list-models"] };
const CURSOR_LIST_COMMAND = {
command: "cursor-agent",
args: ["--list-models"],
};

const THREAD_CONTEXT = { threadId: "thread-1" };

Expand Down Expand Up @@ -115,7 +118,7 @@ describe("acp adapter command plans", () => {
params: {
threadId: "thread-1",
cwd: "/workspace",
agent: { command: "agent", args: ["acp"] },
agent: { command: "cursor-agent", args: ["acp"] },
permissionMode: "accept-edits",
permissionEscalation: "ask",
workspaceWriteRoots: ["/workspace", "/extra-root"],
Expand Down Expand Up @@ -393,7 +396,7 @@ describe("acp adapter model cli", () => {
});
expect(plan).toMatchObject({
params: {
agent: { command: "agent", args: ["acp"] },
agent: { command: "cursor-agent", args: ["acp"] },
modelSelection: {
listCommand: CURSOR_LIST_COMMAND,
selectFlag: "--model",
Expand Down Expand Up @@ -530,7 +533,10 @@ describe("acp adapter model cli", () => {
});
const params = (plan as { params: Record<string, unknown> }).params;
expect("modelSelection" in params).toBe(false);
expect(params.agent).toEqual({ command: "agent", args: ["acp"] });
expect(params.agent).toEqual({
command: "cursor-agent",
args: ["acp"],
});
});
});

Expand Down
3 changes: 2 additions & 1 deletion packages/agent-runtime/src/acp/bridge/model-catalog.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
/**
* Agent CLI model catalog.
*
* Cursor's `agent --list-models` prints one `id - Display Name` line per model,
* Cursor's `cursor-agent --list-models` prints one
* `id - Display Name` line per model,
* OpenCode's `opencode models` prints one bare id per line, and Grok's
* `grok models` prints a bulleted list. These ids can encode reasoning effort:
* `gpt-5.3-codex-low`, bare `gpt-5.3-codex` for medium, `gpt-5.5-extra-high`
Expand Down
10 changes: 6 additions & 4 deletions packages/agent-runtime/src/acp/profiles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,13 @@ export const ACP_AGENT_PROFILES: readonly BuiltInAcpAgentProfile[] = [
{
providerId: "acp-cursor",
displayName: "Cursor",
// Cursor CLI installs its agent binary as `agent` (cursor.com/docs/cli);
// `cursor` is the editor's shell launcher and does not speak ACP.
agentCommand: { command: "agent", args: ["acp"] },
// Cursor installs both `cursor-agent` and the generic `agent` alias. Use
// the namespaced executable so another provider's `agent` binary earlier
// on PATH cannot silently replace Cursor and collapse model discovery to
// the synthetic fallback.
agentCommand: { command: "cursor-agent", args: ["acp"] },
// Global flags must precede the `acp` subcommand, matching the documented
// `agent --api-key ... acp` form.
// `cursor-agent --api-key ... acp` form.
modelCli: {
listArgs: ["--list-models"],
selectFlag: "--model",
Expand Down
2 changes: 1 addition & 1 deletion packages/agent-runtime/src/provider-registry.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ describe("provider registry", () => {
kind: "request",
method: "thread/start",
params: {
agent: { command: "agent", args: ["acp"] },
agent: { command: "cursor-agent", args: ["acp"] },
},
});
});
Expand Down
2 changes: 1 addition & 1 deletion packages/bb-app/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ bb uses whichever providers you have configured. Common providers:
| -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `codex` | Install the [Codex CLI](https://developers.openai.com/codex/cli). Then run `codex login` or configure credentials per the Codex docs. |
| `claude-code` | Install [Claude Code](https://docs.anthropic.com/en/docs/claude-code) and authenticate per its docs. |
| `cursor` | Install [Cursor's agent CLI](https://cursor.com/cli) (`agent`) and authenticate per Cursor's docs. |
| `cursor` | Install [Cursor's agent CLI](https://cursor.com/cli) (`cursor-agent`) and authenticate per Cursor's docs. |
| `pi` | See the [Pi coding agent docs](https://github.com/earendil-works/pi/tree/main/packages/coding-agent). BB includes a pinned Pi runtime, so it does not require an installed Pi executable. |
| `opencode` | Install [opencode](https://opencode.ai/) and authenticate per its docs. |
| `grok` | Install [Grok Build](https://docs.x.ai/build/overview) and authenticate with `grok login` or `XAI_API_KEY`. |
Expand Down
2 changes: 1 addition & 1 deletion packages/host-daemon-contract/src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import {
providerCliStatusResponseSchema,
} from "./local.js";

export const HOST_DAEMON_PROTOCOL_VERSION = 87 as const;
export const HOST_DAEMON_PROTOCOL_VERSION = 88 as const;

export {
BRANCH_LIST_LIMIT_MAX,
Expand Down
13 changes: 6 additions & 7 deletions packages/host-daemon-contract/test/contract.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1039,13 +1039,12 @@ describe("host-daemon local schemas", () => {
});

describe("host-daemon command schemas", () => {
// Version 87 keeps the provider thread id across an environment directory
// switch. The server now sends `turn.submit` with that id where it sent
// `thread.start` before, and the daemon must release the old runtime owner
// first. A daemon without that release would own the same provider session
// twice, so an older daemon must update instead of connecting.
it("uses protocol version 87 for moved-thread session handoff", () => {
expect(HOST_DAEMON_PROTOCOL_VERSION).toBe(87);
// Version 88 switches the built-in Cursor profile from the generic `agent`
// executable to `cursor-agent`. Older daemons retain their local built-in
// profile, so they must update before connecting to avoid launching another
// provider's `agent` binary and losing Cursor model discovery.
it("uses protocol version 88 for the namespaced Cursor executable", () => {
expect(HOST_DAEMON_PROTOCOL_VERSION).toBe(88);
});

it("binds Plan cancellation to a required turn id and typed result", () => {
Expand Down
Loading