Skip to content

Commit c91595d

Browse files
committed
fix: normalize claude system events and refresh tests
1 parent fdeef51 commit c91595d

14 files changed

Lines changed: 99 additions & 25 deletions

CLAUDE.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ Universal schema guidance:
3434
- Do not make breaking changes to API endpoints.
3535
- When changing API routes, ensure the HTTP/SSE test suite has full coverage of every route.
3636
- When agent schema changes, ensure API tests cover the new schema and event shapes end-to-end.
37+
- Never use synthetic data or mocked responses in tests.
38+
- Never manually write agent types; always use generated types in `resources/agent-schemas/`. If types are broken, fix the generated types.
3739

3840
### CLI ⇄ HTTP endpoint map (keep in sync)
3941

README.md

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,18 @@
33
Universal API for running Claude Code, Codex, OpenCode, and Amp inside sandboxes.
44

55
- **Any coding agent**: Universal API to interact with all agents with full feature coverage
6-
- **Server Mode**: Run as HTTP server from any sandbox provider or as TypeScript & Python SDK
6+
- **Server, stdin/stdout, or SDK mode**: Run as an HTTP server, CLI using stdin/stdout, or with the SDK
77
- **Universal session schema**: Universal schema to store agent transcripts
88
- **Supports your sandbox provider**: Daytona, E2B, Vercel Sandboxes, and more
99
- **Lightweight, portable Rust binary**: Install anywhere with 1 curl command
1010
- **OpenAPI spec**: Versioned API schema tracked in `sdks/openapi/openapi.json`
1111

12+
Coming soon:
13+
14+
- **Vercel AI SDK Compatibility**: Works with existing AI SDK tooling, like `useChat`
15+
- **Auto-configure MCP & Skills**: Auto-load MCP servers & skills for your agents
16+
- **Process & logs manager**: Manage processes, logs, and ports for your agents to run background processes
17+
1218
## Agent Support
1319

1420
| Feature | [Claude Code](https://docs.anthropic.com/en/docs/agents-and-tools/claude-code/overview) | [Codex](https://github.com/openai/codex) | [OpenCode](https://github.com/opencode-ai/opencode) | [Amp](https://ampcode.com) |
@@ -62,13 +68,22 @@ Features out of scope:
6268
## FAQ
6369

6470
**Why not use PTY?**
65-
PTY-based approaches require parsing terminal escape sequences and dealing with interactive prompts. The agents we support all have machine-readable output modes (JSONL, HTTP APIs) that provide structured events, making integration more reliable.
71+
72+
PTY-based approaches require parsing terminal escape sequences and dealing with interactive prompts.
73+
74+
The agents we support all have machine-readable output modes (JSONL, HTTP APIs) that provide structured events, making integration more reliable.
6675

6776
**Why not use features that already exist on sandbox provider APIs?**
68-
Sandbox providers focus on infrastructure (containers, VMs, networking). This project focuses specifically on coding agent orchestration—session management, HITL (human-in-the-loop) flows, and universal event schemas. These concerns are complementary.
77+
78+
Sandbox providers focus on infrastructure (containers, VMs, networking).
79+
80+
This project focuses specifically on coding agent orchestration: session management, HITL (human-in-the-loop) flows, and universal event schemas. These concerns are complementary.
6981

7082
**Does it support [platform]?**
7183
The server is a single Rust binary that runs anywhere with a curl install. If your platform can run Linux binaries (Docker, VMs, etc.), it works. See the deployment guides for E2B, Daytona, Vercel Sandboxes, and Docker.
7284

7385
**Can I use this with my personal API keys?**
7486
Yes. Use `sandbox-agent credentials extract-env` to extract API keys from your local agent configs (Claude Code, Codex, OpenCode, Amp) and pass them to the sandbox environment.
87+
88+
**Why rust?**
89+
TODO

ROADMAP.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
## soon
22

3+
- implement stdin/stdout
4+
- switch sdk to use sdtin/stdout for embedded mdoe
35
- discuss actor arch in readme + give example
46
- skillfile
57
- specifically include the release checklist

pnpm-lock.yaml

Lines changed: 17 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

server/packages/agent-management/src/agents.rs

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -112,11 +112,13 @@ impl AgentManager {
112112

113113
pub fn install(&self, agent: AgentId, options: InstallOptions) -> Result<InstallResult, AgentError> {
114114
let install_path = self.binary_path(agent);
115-
if install_path.exists() && !options.reinstall {
116-
return Ok(InstallResult {
117-
path: install_path,
118-
version: self.version(agent).unwrap_or(None),
119-
});
115+
if !options.reinstall {
116+
if let Ok(existing_path) = self.resolve_binary(agent) {
117+
return Ok(InstallResult {
118+
path: existing_path,
119+
version: self.version(agent).unwrap_or(None),
120+
});
121+
}
120122
}
121123

122124
fs::create_dir_all(&self.install_dir)?;
@@ -135,7 +137,9 @@ impl AgentManager {
135137
}
136138

137139
pub fn is_installed(&self, agent: AgentId) -> bool {
138-
self.binary_path(agent).exists() || find_in_path(agent.binary_name()).is_some()
140+
self.binary_path(agent).exists()
141+
|| find_in_path(agent.binary_name()).is_some()
142+
|| default_install_dir().join(agent.binary_name()).exists()
139143
}
140144

141145
pub fn binary_path(&self, agent: AgentId) -> PathBuf {
@@ -368,6 +372,10 @@ impl AgentManager {
368372
if let Some(path) = find_in_path(agent.binary_name()) {
369373
return Ok(path);
370374
}
375+
let fallback = default_install_dir().join(agent.binary_name());
376+
if fallback.exists() {
377+
return Ok(fallback);
378+
}
371379
Err(AgentError::BinaryNotFound { agent })
372380
}
373381
}
@@ -780,6 +788,12 @@ fn find_in_path(binary_name: &str) -> Option<PathBuf> {
780788
None
781789
}
782790

791+
fn default_install_dir() -> PathBuf {
792+
dirs::data_dir()
793+
.map(|dir| dir.join("sandbox-agent").join("bin"))
794+
.unwrap_or_else(|| PathBuf::from(".").join(".sandbox-agent").join("bin"))
795+
}
796+
783797
fn download_bytes(url: &Url) -> Result<Vec<u8>, AgentError> {
784798
let client = Client::builder().build()?;
785799
let mut response = client.get(url.clone()).send()?;

server/packages/sandbox-agent/tests/http_sse_snapshots.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use tower_http::cors::CorsLayer;
1919
const PROMPT: &str = "Reply with exactly the single word OK.";
2020
const PERMISSION_PROMPT: &str = "List files in the current directory using available tools.";
2121
const QUESTION_PROMPT: &str =
22-
"Ask the user a multiple-choice question with options yes/no using any built-in AskUserQuestion tool, then wait.";
22+
"Use the AskUserQuestion tool to ask exactly one yes/no question, then wait for a reply. Do not answer yourself.";
2323

2424
struct TestApp {
2525
app: Router,
@@ -1022,7 +1022,7 @@ async fn approval_flow_snapshots() {
10221022
}
10231023

10241024
let question_reply_session = format!("question-reply-{}", config.agent.as_str());
1025-
create_session(&app.app, config.agent, &question_reply_session, test_permission_mode(config.agent)).await;
1025+
create_session(&app.app, config.agent, &question_reply_session, "plan").await;
10261026
let status = send_status(
10271027
&app.app,
10281028
Method::POST,
@@ -1083,7 +1083,7 @@ async fn approval_flow_snapshots() {
10831083
}
10841084

10851085
let question_reject_session = format!("question-reject-{}", config.agent.as_str());
1086-
create_session(&app.app, config.agent, &question_reject_session, test_permission_mode(config.agent)).await;
1086+
create_session(&app.app, config.agent, &question_reject_session, "plan").await;
10871087
let status = send_status(
10881088
&app.app,
10891089
Method::POST,

server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__approval_flow_snapshots@permission_events_claude.snap

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
---
22
source: server/packages/sandbox-agent/tests/http_sse_snapshots.rs
3-
assertion_line: 978
43
expression: normalize_events(&permission_events)
54
---
65
- agent: claude
@@ -9,8 +8,10 @@ expression: normalize_events(&permission_events)
98
started:
109
message: session.created
1110
- agent: claude
12-
kind: unknown
11+
kind: started
1312
seq: 2
13+
started:
14+
message: system.init
1415
- agent: claude
1516
kind: message
1617
message:

server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__approval_flow_snapshots@question_reject_events_claude.snap

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
---
22
source: server/packages/sandbox-agent/tests/http_sse_snapshots.rs
3-
assertion_line: 1100
43
expression: normalize_events(&reject_events)
54
---
65
- agent: claude
@@ -9,8 +8,10 @@ expression: normalize_events(&reject_events)
98
started:
109
message: session.created
1110
- agent: claude
12-
kind: unknown
11+
kind: started
1312
seq: 2
13+
started:
14+
message: system.init
1415
- agent: claude
1516
kind: message
1617
message:

server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__approval_flow_snapshots@question_reply_events_claude.snap

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@ expression: normalize_events(&question_events)
88
started:
99
message: session.created
1010
- agent: claude
11-
kind: unknown
11+
kind: started
1212
seq: 2
13+
started:
14+
message: system.init
1315
- agent: claude
1416
kind: message
1517
message:

server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__run_concurrency_snapshot@concurrency_events_claude.snap

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
---
22
source: server/packages/sandbox-agent/tests/http_sse_snapshots.rs
3-
assertion_line: 1232
43
expression: snapshot
54
---
65
session_a:
@@ -10,8 +9,10 @@ session_a:
109
started:
1110
message: session.created
1211
- agent: claude
13-
kind: unknown
12+
kind: started
1413
seq: 2
14+
started:
15+
message: system.init
1516
- agent: claude
1617
kind: message
1718
message:
@@ -27,8 +28,10 @@ session_b:
2728
started:
2829
message: session.created
2930
- agent: claude
30-
kind: unknown
31+
kind: started
3132
seq: 2
33+
started:
34+
message: system.init
3235
- agent: claude
3336
kind: message
3437
message:

0 commit comments

Comments
 (0)