diff --git a/README.md b/README.md index 0127a0a..b47789e 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@

Run Coding Agents in Sandboxes. Control Them Over HTTP.

- A server that runs inside your sandbox. Your app connects remotely to control Claude Code, Codex, OpenCode, or Amp — streaming events, handling permissions, managing sessions. + A server that runs inside your sandbox. Your app connects remotely to control Claude Code, Codex, OpenCode, Cursor, or Amp — streaming events, handling permissions, managing sessions.

@@ -24,13 +24,13 @@ Sandbox Agent solves three problems: 1. **Coding agents need sandboxes** — You can't let AI execute arbitrary code on your production servers. Coding agents need isolated environments, but existing SDKs assume local execution. Sandbox Agent is a server that runs inside the sandbox and exposes HTTP/SSE. -2. **Every coding agent is different** — Claude Code, Codex, OpenCode, and Amp each have proprietary APIs, event formats, and behaviors. Swapping agents means rewriting your integration. Sandbox Agent provides one HTTP API — write your code once, swap agents with a config change. +2. **Every coding agent is different** — Claude Code, Codex, OpenCode, Cursor, and Amp each have proprietary APIs, event formats, and behaviors. Swapping agents means rewriting your integration. Sandbox Agent provides one HTTP API — write your code once, swap agents with a config change. 3. **Sessions are ephemeral** — Agent transcripts live in the sandbox. When the process ends, you lose everything. Sandbox Agent streams events in a universal schema to your storage. Persist to Postgres, ClickHouse, or [Rivet](https://rivet.dev). Replay later, audit everything. ## Features -- **Universal Agent API**: Single interface to control Claude Code, Codex, OpenCode, and Amp with full feature coverage +- **Universal Agent API**: Single interface to control Claude Code, Codex, OpenCode, Cursor, and Amp with full feature coverage - **Streaming Events**: Real-time SSE stream of everything the agent does — tool calls, permission requests, file edits, and more - **Universal Session Schema**: [Standardized schema](https://sandboxagent.dev/docs/session-transcript-schema) that normalizes all agent event formats for storage and replay - **Human-in-the-Loop**: Approve or deny tool executions and answer agent questions remotely over HTTP @@ -234,7 +234,7 @@ No, they're complementary. AI SDK is for building chat interfaces and calling LL

Which coding agents are supported? -Claude Code, Codex, OpenCode, and Amp. The SDK normalizes their APIs so you can swap between them without changing your code. +Claude Code, Codex, OpenCode, Cursor, and Amp. The SDK normalizes their APIs so you can swap between them without changing your code.
diff --git a/server/packages/agent-management/src/agents.rs b/server/packages/agent-management/src/agents.rs index a2438f9..ffdd09e 100644 --- a/server/packages/agent-management/src/agents.rs +++ b/server/packages/agent-management/src/agents.rs @@ -21,6 +21,7 @@ pub enum AgentId { Codex, Opencode, Amp, + Cursor, Mock, } @@ -31,6 +32,7 @@ impl AgentId { AgentId::Codex => "codex", AgentId::Opencode => "opencode", AgentId::Amp => "amp", + AgentId::Cursor => "cursor", AgentId::Mock => "mock", } } @@ -41,6 +43,7 @@ impl AgentId { AgentId::Codex => "codex", AgentId::Opencode => "opencode", AgentId::Amp => "amp", + AgentId::Cursor => "cursor-agent", AgentId::Mock => "mock", } } @@ -51,6 +54,7 @@ impl AgentId { "codex" => Some(AgentId::Codex), "opencode" => Some(AgentId::Opencode), "amp" => Some(AgentId::Amp), + "cursor" => Some(AgentId::Cursor), "mock" => Some(AgentId::Mock), _ => None, } @@ -151,6 +155,7 @@ impl AgentManager { install_opencode(&install_path, self.platform, options.version.as_deref())? } AgentId::Amp => install_amp(&install_path, self.platform, options.version.as_deref())?, + AgentId::Cursor => install_cursor(&install_path, self.platform, options.version.as_deref())?, AgentId::Mock => { if !install_path.exists() { fs::write(&install_path, b"mock")?; @@ -268,6 +273,18 @@ impl AgentManager { } command.arg(&options.prompt); } + AgentId::Cursor => { + // cursor-agent typically runs as HTTP server on localhost:32123 + // For CLI usage similar to opencode + command.arg("run").arg("--format").arg("json"); + if let Some(model) = options.model.as_deref() { + command.arg("-m").arg(model); + } + if let Some(session_id) = options.session_id.as_deref() { + command.arg("-s").arg(session_id); + } + command.arg(&options.prompt); + } AgentId::Amp => { let output = spawn_amp(&path, &working_dir, &options)?; let stdout = String::from_utf8_lossy(&output.stdout).to_string(); @@ -1199,6 +1216,30 @@ fn find_in_path(binary_name: &str) -> Option { None } +fn install_cursor(path: &Path, platform: Platform, _version: Option<&str>) -> Result<(), AgentError> { + // Note: cursor-agent binary URL needs to be verified + // Cursor Pro includes cursor-agent, typically installed via: curl -fsS https://cursor.com/install | bash + // For sandbox-agent, we need standalone cursor-agent binary + // TODO: Determine correct download URL for cursor-agent releases + + let platform_segment = match platform { + Platform::LinuxX64 | Platform::LinuxX64Musl => "linux-x64", + Platform::LinuxArm64 => "linux-arm64", + Platform::MacosArm64 => "darwin-arm64", + Platform::MacosX64 => "darwin-x64", + }; + + // Placeholder URL - needs to be updated with actual cursor-agent release URL + let url = Url::parse(&format!( + "https://cursor.com/api/v1/releases/latest/download/cursor-agent-{platform_segment}", + platform_segment = platform_segment + ))?; + + let bytes = download_bytes(&url)?; + write_executable(path, &bytes)?; + Ok(()) +} + fn download_bytes(url: &Url) -> Result, AgentError> { let client = Client::builder().build()?; let mut response = client.get(url.clone()).send()?;