diff --git a/Cargo.toml b/Cargo.toml index 0fc4dc8..9b5dbde 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ members = ["server/packages/*", "gigacode"] exclude = ["factory/packages/desktop/src-tauri", "foundry/packages/desktop/src-tauri"] [workspace.package] -version = "0.4.2" +version = "0.4.1" edition = "2021" authors = [ "Rivet Gaming, LLC " ] license = "Apache-2.0" @@ -13,13 +13,13 @@ description = "Universal API for automatic coding agents in sandboxes. Supports [workspace.dependencies] # Internal crates -sandbox-agent = { version = "0.4.2", path = "server/packages/sandbox-agent" } -sandbox-agent-error = { version = "0.4.2", path = "server/packages/error" } -sandbox-agent-agent-management = { version = "0.4.2", path = "server/packages/agent-management" } -sandbox-agent-agent-credentials = { version = "0.4.2", path = "server/packages/agent-credentials" } -sandbox-agent-opencode-adapter = { version = "0.4.2", path = "server/packages/opencode-adapter" } -sandbox-agent-opencode-server-manager = { version = "0.4.2", path = "server/packages/opencode-server-manager" } -acp-http-adapter = { version = "0.4.2", path = "server/packages/acp-http-adapter" } +sandbox-agent = { version = "0.4.1", path = "server/packages/sandbox-agent" } +sandbox-agent-error = { version = "0.4.1", path = "server/packages/error" } +sandbox-agent-agent-management = { version = "0.4.1", path = "server/packages/agent-management" } +sandbox-agent-agent-credentials = { version = "0.4.1", path = "server/packages/agent-credentials" } +sandbox-agent-opencode-adapter = { version = "0.4.1", path = "server/packages/opencode-adapter" } +sandbox-agent-opencode-server-manager = { version = "0.4.1", path = "server/packages/opencode-server-manager" } +acp-http-adapter = { version = "0.4.1", path = "server/packages/acp-http-adapter" } # Serialization serde = { version = "1.0", features = ["derive"] } diff --git a/docs/agent-sessions.mdx b/docs/agent-sessions.mdx index 0154537..0f9e2ab 100644 --- a/docs/agent-sessions.mdx +++ b/docs/agent-sessions.mdx @@ -51,108 +51,6 @@ await session.prompt([ unsubscribe(); ``` -### Event types - -Each event's `payload` contains a session update. The `sessionUpdate` field identifies the type. - - - -Streamed text or content from the agent's response. - -```json -{ - "sessionUpdate": "agent_message_chunk", - "content": { "type": "text", "text": "Here's how the repository is structured..." } -} -``` - - - -Internal reasoning from the agent (chain-of-thought / extended thinking). - -```json -{ - "sessionUpdate": "agent_thought_chunk", - "content": { "type": "text", "text": "I should start by looking at the project structure..." } -} -``` - - - -Echo of the user's prompt being processed. - -```json -{ - "sessionUpdate": "user_message_chunk", - "content": { "type": "text", "text": "Summarize the repository structure." } -} -``` - - - -The agent invoked a tool (file edit, terminal command, etc.). - -```json -{ - "sessionUpdate": "tool_call", - "toolCallId": "tc_abc123", - "title": "Read file", - "status": "in_progress", - "rawInput": { "path": "/src/index.ts" } -} -``` - - - -Progress or result update for an in-progress tool call. - -```json -{ - "sessionUpdate": "tool_call_update", - "toolCallId": "tc_abc123", - "status": "completed", - "content": [{ "type": "text", "text": "import express from 'express';\n..." }] -} -``` - - - -The agent's execution plan for the current task. - -```json -{ - "sessionUpdate": "plan", - "entries": [ - { "content": "Read the project structure", "status": "completed" }, - { "content": "Identify main entrypoints", "status": "in_progress" }, - { "content": "Write summary", "status": "pending" } - ] -} -``` - - - -Token usage metrics for the current turn. - -```json -{ - "sessionUpdate": "usage_update" -} -``` - - - -Session metadata changed (e.g. agent-generated title). - -```json -{ - "sessionUpdate": "session_info_update", - "title": "Repository structure analysis" -} -``` - - - ## Fetch persisted event history ```ts diff --git a/docs/architecture.mdx b/docs/architecture.mdx index 61b4689..49012d4 100644 --- a/docs/architecture.mdx +++ b/docs/architecture.mdx @@ -56,7 +56,7 @@ Agents are installed lazily on first use. To avoid the cold-start delay, pre-ins sandbox-agent install-agent --all ``` -The `rivetdev/sandbox-agent:0.4.2-full` Docker image ships with all agents pre-installed. +The `rivetdev/sandbox-agent:0.4.1-full` Docker image ships with all agents pre-installed. ## Production-ready agent orchestration diff --git a/docs/deploy/daytona.mdx b/docs/deploy/daytona.mdx index e546bef..ad9b32d 100644 --- a/docs/deploy/daytona.mdx +++ b/docs/deploy/daytona.mdx @@ -44,7 +44,7 @@ try { } ``` -The `daytona` provider uses the `rivetdev/sandbox-agent:0.4.2-full` image by default and starts the server automatically. +The `daytona` provider uses the `rivetdev/sandbox-agent:0.4.1-full` image by default and starts the server automatically. ## Using snapshots for faster startup diff --git a/docs/deploy/docker.mdx b/docs/deploy/docker.mdx index c5a3432..9be1625 100644 --- a/docs/deploy/docker.mdx +++ b/docs/deploy/docker.mdx @@ -15,11 +15,11 @@ Run the published full image with all supported agents pre-installed: docker run --rm -p 3000:3000 \ -e ANTHROPIC_API_KEY="$ANTHROPIC_API_KEY" \ -e OPENAI_API_KEY="$OPENAI_API_KEY" \ - rivetdev/sandbox-agent:0.4.2-full \ + rivetdev/sandbox-agent:0.4.1-full \ server --no-token --host 0.0.0.0 --port 3000 ``` -The `0.4.2-full` tag pins the exact version. The moving `full` tag is also published for contributors who want the latest full image. +The `0.4.1-full` tag pins the exact version. The moving `full` tag is also published for contributors who want the latest full image. If you also want the desktop API inside the container, install desktop dependencies before starting the server: @@ -52,7 +52,7 @@ const docker = new Docker(); const PORT = 3000; const container = await docker.createContainer({ - Image: "rivetdev/sandbox-agent:0.4.2-full", + Image: "rivetdev/sandbox-agent:0.4.1-full", Cmd: ["server", "--no-token", "--host", "0.0.0.0", "--port", `${PORT}`], Env: [ `ANTHROPIC_API_KEY=${process.env.ANTHROPIC_API_KEY}`, diff --git a/docs/deploy/foundry-self-hosting.mdx b/docs/deploy/foundry-self-hosting.mdx new file mode 100644 index 0000000..8fd43ae --- /dev/null +++ b/docs/deploy/foundry-self-hosting.mdx @@ -0,0 +1,155 @@ +--- +title: "Foundry Self-Hosting" +description: "Environment, credentials, and deployment setup for Sandbox Agent Foundry auth, GitHub, and billing." +--- + +This guide documents the deployment contract for the Foundry product surface: app auth, GitHub onboarding, repository import, and billing. + +It also covers the local-development bootstrap that uses `.env.development` only when `NODE_ENV=development`. + +## Local Development + +For backend local development, the Foundry backend now supports a development-only dotenv bootstrap: + +- It loads `.env.development.local` and `.env.development` +- It does this **only** when `NODE_ENV=development` +- It does **not** load dotenv files in production + +The example file lives at [`/.env.development.example`](https://github.com/rivet-dev/sandbox-agent/blob/main/.env.development.example). + +To use it locally: + +```bash +cp .env.development.example .env.development +``` + +Run the backend with: + +```bash +just foundry-backend-start +``` + +That recipe sets `NODE_ENV=development`, which enables the dotenv loader. + +### Local Defaults + +These values can be safely defaulted for local development: + +- `APP_URL=http://localhost:4173` +- `BETTER_AUTH_URL=http://localhost:7741` +- `BETTER_AUTH_SECRET=sandbox-agent-foundry-development-only-change-me` +- `GITHUB_REDIRECT_URI=http://localhost:7741/v1/auth/callback/github` + +These should be treated as development-only values. + +## Production Environment + +For production or self-hosting, set these as real environment variables in your deployment platform. Do not rely on dotenv file loading. + +### App/Auth + +| Variable | Required | Notes | +|---|---:|---| +| `APP_URL` | Yes | Public frontend origin | +| `BETTER_AUTH_URL` | Yes | Public auth base URL | +| `BETTER_AUTH_SECRET` | Yes | Strong random secret for auth/session signing | + +### GitHub OAuth + +| Variable | Required | Notes | +|---|---:|---| +| `GITHUB_CLIENT_ID` | Yes | GitHub OAuth app client id | +| `GITHUB_CLIENT_SECRET` | Yes | GitHub OAuth app client secret | +| `GITHUB_REDIRECT_URI` | Yes | GitHub OAuth callback URL | + +Use GitHub OAuth for: + +- user sign-in +- user identity +- org selection +- access to the signed-in user’s GitHub context + +## GitHub App + +If your Foundry deployment uses GitHub App-backed organization install and repo import, also configure: + +| Variable | Required | Notes | +|---|---:|---| +| `GITHUB_APP_ID` | Yes | GitHub App id | +| `GITHUB_APP_CLIENT_ID` | Yes | GitHub App client id | +| `GITHUB_APP_CLIENT_SECRET` | Yes | GitHub App client secret | +| `GITHUB_APP_PRIVATE_KEY` | Yes | PEM private key for installation auth | + +For `.env.development` and `.env.development.local`, store `GITHUB_APP_PRIVATE_KEY` as a quoted single-line value with `\n` escapes instead of raw multi-line PEM text. + +Recommended GitHub App permissions: + +- Repository `Metadata: Read` +- Repository `Contents: Read & Write` +- Repository `Pull requests: Read & Write` +- Repository `Checks: Read` +- Repository `Commit statuses: Read` + +Set the webhook URL to `https:///v1/webhooks/github` and generate a webhook secret. Store the secret as `GITHUB_WEBHOOK_SECRET`. + +This is required, not optional. Foundry depends on GitHub App webhook delivery for installation lifecycle changes, repo access changes, and ongoing repo / pull request sync. If the GitHub App is not installed for the workspace, or webhook delivery is misconfigured, Foundry will remain in an install / reconnect state and core GitHub-backed functionality will not work correctly. + +Recommended webhook subscriptions: + +- `installation` +- `installation_repositories` +- `pull_request` +- `pull_request_review` +- `pull_request_review_comment` +- `push` +- `create` +- `delete` +- `check_suite` +- `check_run` +- `status` + +Use the GitHub App for: + +- installation/reconnect state +- org repo import +- repository sync +- PR creation and updates + +Use GitHub OAuth for: + +- who the user is +- which orgs they can choose + +## Stripe + +For live billing, configure: + +| Variable | Required | Notes | +|---|---:|---| +| `STRIPE_SECRET_KEY` | Yes | Server-side Stripe secret key | +| `STRIPE_PUBLISHABLE_KEY` | Yes | Client-side Stripe publishable key | +| `STRIPE_WEBHOOK_SECRET` | Yes | Signing secret for billing webhooks | +| `STRIPE_PRICE_TEAM` | Yes | Stripe price id for the Team plan checkout session | + +Stripe should own: + +- hosted checkout +- billing portal +- subscription status +- invoice history +- webhook-driven state sync + +## Mock Invariant + +Foundry’s mock client path should continue to work end to end even when the real auth/GitHub/Stripe path exists. + +That includes: + +- sign-in +- org selection/import +- settings +- billing UI +- workspace/task/session flow +- seat accrual + +Use mock mode for deterministic UI review and local product development. Use the real env-backed path for integration and self-hosting. diff --git a/docs/docs.json b/docs/docs.json index dbcc407..8b0f858 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -1,6 +1,6 @@ { "$schema": "https://mintlify.com/docs.json", - "theme": "mint", + "theme": "willow", "name": "Sandbox Agent SDK", "appearance": { "default": "dark", @@ -25,6 +25,11 @@ }, "navbar": { "links": [ + { + "label": "Gigacode", + "icon": "terminal", + "href": "https://github.com/rivet-dev/sandbox-agent/tree/main/gigacode" + }, { "label": "Discord", "icon": "discord", @@ -85,10 +90,13 @@ "group": "System", "pages": ["file-system", "processes", "computer-use", "common-software"] }, + { + "group": "Orchestration", + "pages": ["orchestration-architecture", "session-persistence", "observability", "multiplayer", "security"] + }, { "group": "Reference", "pages": [ - "troubleshooting", "architecture", "cli", "inspector", @@ -121,10 +129,5 @@ } ] }, - "__removed": [ - { - "group": "Orchestration", - "pages": ["orchestration-architecture", "session-persistence", "observability", "multiplayer", "security"] - } - ] + "styles": ["/theme.css"] } diff --git a/docs/gigacode.mdx b/docs/gigacode.mdx new file mode 100644 index 0000000..ccc9e39 --- /dev/null +++ b/docs/gigacode.mdx @@ -0,0 +1,6 @@ +--- +title: Gigacode +url: "https://github.com/rivet-dev/sandbox-agent/tree/main/gigacode" +--- + + diff --git a/docs/openapi.json b/docs/openapi.json index 3624707..1541763 100644 --- a/docs/openapi.json +++ b/docs/openapi.json @@ -10,7 +10,7 @@ "license": { "name": "Apache-2.0" }, - "version": "0.4.2" + "version": "0.4.1" }, "servers": [ { diff --git a/docs/pi-support-plan.md b/docs/pi-support-plan.md new file mode 100644 index 0000000..5e207a5 --- /dev/null +++ b/docs/pi-support-plan.md @@ -0,0 +1,210 @@ +# Pi Agent Support Plan (pi-mono) + +## Implementation Status Update + +- Runtime selection now supports two internal modes: + - `PerSession` (default for unknown/non-allowlisted Pi capabilities) + - `Shared` (allowlist-only compatibility path) +- Pi sessions now use per-session process isolation by default, enabling true concurrent Pi sessions in Inspector and API clients. +- Shared Pi server code remains available and is used only when capability checks allow multiplexing. +- Session termination for per-session Pi mode hard-kills the underlying Pi process and clears queued prompts/pending waiters. +- In-session concurrent sends are serialized with an unbounded daemon-side FIFO queue per session. + +## Investigation Summary + +### Pi CLI modes and RPC protocol +- Pi supports multiple modes including interactive, print/JSON output, RPC, and SDK usage. JSON mode outputs a stream of JSON events suitable for parsing, and RPC mode is intended for programmatic control over stdin/stdout. +- RPC mode is started with `pi --mode rpc` and supports options like `--provider`, `--model`, `--no-session`, and `--session-dir`. +- The RPC protocol is newline-delimited JSON over stdin/stdout: + - Commands are JSON objects written to stdin. + - Responses are JSON objects with `type: "response"` and optional `id`. + - Events are JSON objects without `id`. +- `prompt` can include images using `ImageContent` (base64 or URL) alongside text. +- JSON/print mode (`pi -p` or `pi --print --mode json`) produces JSONL for non-interactive parsing and can resume sessions with a token. + +### RPC commands +RPC commands listed in `rpc.md` include: +- `new_session`, `get_state`, `list_sessions`, `delete_session`, `rename_session`, `clear_session` +- `prompt`, `queue_message`, `abort`, `get_queued_messages` + +### RPC event types +RPC events listed in `rpc.md` include: +- `agent_start`, `agent_end` +- `turn_start`, `turn_end` +- `message_start`, `message_update`, `message_end` +- `tool_execution_start`, `tool_execution_update`, `tool_execution_end` +- `auto_compaction`, `auto_retry`, `hook_error` + +`message_update` uses `assistantMessageEvent` deltas such as: +- `start`, `text_start`, `text_delta`, `text_end` +- `thinking_start`, `thinking_delta`, `thinking_end` +- `toolcall_start`, `toolcall_delta`, `toolcall_end` +- `toolcall_args_start`, `toolcall_args_delta`, `toolcall_args_end` +- `done`, `error` + +`tool_execution_update` includes `partialResult`, which is described as accumulated output so far. + +### Schema source locations (pi-mono) +RPC types are documented as living in: +- `packages/ai/src/types.ts` (Model types) +- `packages/agent/src/types.ts` (AgentResponse types) +- `packages/coding-agent/src/core/messages.ts` (message types) +- `packages/coding-agent/src/modes/rpc/rpc-types.ts` (RPC protocol types) + +### Distribution assets +Pi releases provide platform-specific binaries such as: +- `pi-darwin-arm64`, `pi-darwin-x64` +- `pi-linux-arm64`, `pi-linux-x64` +- `pi-win-x64.zip` + +## Integration Decisions +- Follow the OpenCode pattern: a shared long-running process (stdio RPC) with session multiplexing. +- Primary integration path is RPC streaming (`pi --mode rpc`). +- JSON/print mode is a fallback only (diagnostics or non-interactive runs). +- Create sessions via `new_session`; store the returned `sessionId` as `native_session_id`. +- Use `get_state` as a re-sync path after server restarts. +- Use `prompt` for send-message, with optional image content. +- Convert Pi events into universal events; emit daemon synthetic `session.started` on session creation and `session.ended` only on errors/termination. + +## Implementation Plan + +### 1) Agent Identity + Capabilities +Files: +- `server/packages/agent-management/src/agents.rs` +- `server/packages/sandbox-agent/src/router.rs` +- `docs/cli.mdx`, `docs/conversion.mdx`, `docs/session-transcript-schema.mdx` +- `README.md`, `frontend/packages/website/src/components/FAQ.tsx` + +Tasks: +- Add `AgentId::Pi` with string/binary name `"pi"` and parsing rules. +- Add Pi to `all_agents()` and agent lists. +- Define `AgentCapabilities` for Pi: + - `tool_calls=true`, `tool_results=true` + - `text_messages=true`, `streaming_deltas=true`, `item_started=true` + - `reasoning=true` (from `thinking_*` deltas) + - `images=true` (ImageContent in `prompt`) + - `permissions=false`, `questions=false`, `mcp_tools=false` + - `shared_process=true`, `session_lifecycle=false` (no native session events) + - `error_events=true` (hook_error) + - `command_execution=false`, `file_changes=false`, `file_attachments=false` + +### 2) Installer and Binary Resolution +Files: +- `server/packages/agent-management/src/agents.rs` + +Tasks: +- Add `install_pi()` that: + - Downloads the correct release asset per platform (`pi-`). + - Handles `.zip` on Windows and raw binaries elsewhere. + - Marks binary executable. +- Add Pi to `AgentManager::install`, `is_installed`, `version`. +- Version detection: try `--version`, `version`, `-V`. + +### 3) Schema Extraction for Pi +Files: +- `resources/agent-schemas/src/pi.ts` (new) +- `resources/agent-schemas/src/index.ts` +- `resources/agent-schemas/artifacts/json-schema/pi.json` +- `server/packages/extracted-agent-schemas/build.rs` +- `server/packages/extracted-agent-schemas/src/lib.rs` + +Tasks: +- Implement `extractPiSchema()`: + - Download pi-mono sources (zip/tarball) into a temp dir. + - Use `ts-json-schema-generator` against `packages/coding-agent/src/modes/rpc/rpc-types.ts`. + - Include dependent files per `rpc.md` (ai/types, agent/types, core/messages). + - Extract `RpcEvent`, `RpcResponse`, `RpcCommand` unions (exact type names from source). +- Add fallback schema if remote fetch fails (minimal union with event/response fields). +- Wire pi into extractor index and artifact generation. + +### 4) Universal Schema Conversion (Pi -> Universal) +Files: +- `server/packages/universal-agent-schema/src/agents/pi.rs` (new) +- `server/packages/universal-agent-schema/src/agents/mod.rs` +- `server/packages/universal-agent-schema/src/lib.rs` +- `server/packages/sandbox-agent/src/router.rs` + +Mapping rules: +- `message_start` -> `item.started` (kind=message, role=assistant, native_item_id=messageId) +- `message_update`: + - `text_*` -> `item.delta` (assistant text delta) + - `thinking_*` -> `item.delta` with `ContentPart::Reasoning` (visibility=Private) + - `toolcall_*` and `toolcall_args_*` -> ignore for now (tool_execution_* is authoritative) + - `error` -> `item.completed` with `ItemStatus::Failed` (if no later message_end) +- `message_end` -> `item.completed` (finalize assistant message) +- `tool_execution_start` -> `item.started` (kind=tool_call, ContentPart::ToolCall) +- `tool_execution_update` -> `item.delta` for a synthetic tool_result item: + - Maintain a per-toolCallId buffer to compute delta from accumulated `partialResult`. +- `tool_execution_end` -> `item.completed` (kind=tool_result, output from `result.content`) + - If `isError=true`, set item status to failed. +- `agent_start`, `turn_start`, `turn_end`, `agent_end`, `auto_compaction`, `auto_retry`, `hook_error`: + - Map to `ItemKind::Status` with a label like `pi.agent_start`, `pi.auto_retry`, etc. + - Do not emit `session.ended` for these events. +- If event parsing fails, emit `agent.unparsed` (source=daemon, synthetic=true) and fail tests. + +### 5) Shared RPC Server Integration +Files: +- `server/packages/sandbox-agent/src/router.rs` + +Tasks: +- Add a new managed stdio server type for Pi, similar to Codex: + - Create `PiServer` struct with: + - stdin sender + - pending request map keyed by request id + - per-session native session id mapping + - Extend `ManagedServerKind` to include Pi. + - Add `ensure_pi_server()` and `spawn_pi_server()` using `pi --mode rpc`. + - Add a `handle_pi_server_output()` loop to parse stdout lines into events/responses. +- Session creation: + - On `create_session`, ensure Pi server is running, send `new_session`, store sessionId. + - Register session with `server_manager.register_session` for native mapping. +- Sending messages: + - Use `prompt` command; include sessionId and optional images. + - Emit synthetic `item.started` only if Pi does not emit `message_start`. + +### 6) Router + Streaming Path Changes +Files: +- `server/packages/sandbox-agent/src/router.rs` + +Tasks: +- Add Pi handling to: + - `create_session` (new_session) + - `send_message` (prompt) + - `parse_agent_line` (Pi event conversion) + - `agent_modes` (default to `default` unless Pi exposes a mode list) + - `agent_supports_resume` (true if Pi supports session resume) + +### 7) Tests +Files: +- `server/packages/sandbox-agent/tests/...` +- `server/packages/universal-agent-schema/tests/...` (if present) + +Tasks: +- Unit tests for conversion: + - `message_start/update/end` -> item.started/delta/completed + - `tool_execution_*` -> tool call/result mapping with partialResult delta + - failure -> agent.unparsed +- Integration tests: + - Start Pi RPC server, create session, send prompt, stream events. + - Validate `native_session_id` mapping and event ordering. +- Update HTTP/SSE test coverage to include Pi agent if relevant. + +## Risk Areas / Edge Cases +- `tool_execution_update.partialResult` is cumulative; must compute deltas. +- `message_update` may emit `done`/`error` without `message_end`; handle both paths. +- No native session lifecycle events; rely on daemon synthetic events. +- Session recovery after RPC server restart requires `get_state` + re-register sessions. + +## Acceptance Criteria +- Pi appears in `/v1/agents`, CLI list, and docs. +- `create_session` returns `native_session_id` from Pi `new_session`. +- Streaming prompt yields universal events with proper ordering: + - message -> item.started/delta/completed + - tool execution -> tool call + tool result +- Tests pass and no synthetic data is used in test fixtures. + +## Sources +- https://upd.dev/badlogic/pi-mono/src/commit/d36e0ea07303d8a76d51b4a7bd5f0d6d3c490860/packages/coding-agent/docs/rpc.md +- https://buildwithpi.ai/pi-cli +- https://takopi.dev/docs/pi-cli/ +- https://upd.dev/badlogic/pi-mono/releases diff --git a/docs/quickstart.mdx b/docs/quickstart.mdx index 223a54d..9aa0bb0 100644 --- a/docs/quickstart.mdx +++ b/docs/quickstart.mdx @@ -64,7 +64,7 @@ icon: "rocket" docker run -p 2468:2468 \ -e ANTHROPIC_API_KEY="sk-ant-..." \ -e OPENAI_API_KEY="sk-..." \ - rivetdev/sandbox-agent:0.4.2-full \ + rivetdev/sandbox-agent:0.4.1-full \ server --no-token --host 0.0.0.0 --port 2468 ``` diff --git a/docs/session-transcript-schema.mdx b/docs/session-transcript-schema.mdx new file mode 100644 index 0000000..c9c004a --- /dev/null +++ b/docs/session-transcript-schema.mdx @@ -0,0 +1,388 @@ +--- +title: "Session Transcript Schema" +description: "Universal event schema for session transcripts across all agents." +--- + +Each coding agent outputs events in its own native format. The sandbox-agent converts these into a universal event schema, giving you a consistent session transcript regardless of which agent you use. + +The schema is defined in [OpenAPI format](https://github.com/rivet-dev/sandbox-agent/blob/main/docs/openapi.json). See the [HTTP API Reference](/api-reference) for endpoint documentation. + +## Coverage Matrix + +This table shows which agent feature coverage appears in the universal event stream. All agents retain their full native feature coverage—this only reflects what's normalized into the schema. + +| Feature | Claude | Codex | OpenCode | Amp | Pi (RPC) | +|--------------------|:------:|:-----:|:------------:|:------------:|:------------:| +| Stability | Stable | Stable| Experimental | Experimental | Experimental | +| Text Messages | ✓ | ✓ | ✓ | ✓ | ✓ | +| Tool Calls | ✓ | ✓ | ✓ | ✓ | ✓ | +| Tool Results | ✓ | ✓ | ✓ | ✓ | ✓ | +| Questions (HITL) | ✓ | | ✓ | | | +| Permissions (HITL) | ✓ | ✓ | ✓ | - | | +| Images | - | ✓ | ✓ | - | ✓ | +| File Attachments | - | ✓ | ✓ | - | | +| Session Lifecycle | - | ✓ | ✓ | - | | +| Error Events | - | ✓ | ✓ | ✓ | ✓ | +| Reasoning/Thinking | - | ✓ | - | - | ✓ | +| Command Execution | - | ✓ | - | - | | +| File Changes | - | ✓ | - | - | | +| MCP Tools | ✓ | ✓ | ✓ | ✓ | | +| Streaming Deltas | ✓ | ✓ | ✓ | - | ✓ | +| Variants | | ✓ | ✓ | ✓ | ✓ | + +Agents: [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) · [Pi](https://buildwithpi.ai/pi-cli) + +- ✓ = Appears in session events +- \- = Agent supports natively, schema conversion coming soon +- (blank) = Not supported by agent +- Pi runtime model is router-managed per-session RPC (`pi --mode rpc`); it does not use generic subprocess streaming. + + + + Basic message exchange between user and assistant. + + + Visibility into tool invocations (file reads, command execution, etc.) and their results. When not natively supported, tool activity is embedded in message content. + + + Interactive questions the agent asks the user. Emits `question.requested` and `question.resolved` events. + + + Permission requests for sensitive operations. Emits `permission.requested` and `permission.resolved` events. + + + Support for image attachments in messages. + + + Support for file attachments in messages. + + + Native `session.started` and `session.ended` events. When not supported, the daemon emits synthetic lifecycle events. + + + Structured error events for runtime failures. + + + Extended thinking or reasoning content with visibility controls. + + + Detailed command execution events with stdout/stderr. + + + Structured file modification events with diffs. + + + Model Context Protocol tool support. + + + Native streaming of content deltas. When not supported, the daemon emits a single synthetic delta before `item.completed`. + + + Model variants such as reasoning effort or depth. Agents may expose different variant sets per model. + + + +Want support for another agent? [Open an issue](https://github.com/rivet-dev/sandbox-agent/issues/new) to request it. + +## UniversalEvent + +Every event from the API is wrapped in a `UniversalEvent` envelope. + +| Field | Type | Description | +|-------|------|-------------| +| `event_id` | string | Unique identifier for this event | +| `sequence` | integer | Monotonic sequence number within the session (starts at 1) | +| `time` | string | RFC3339 timestamp | +| `session_id` | string | Daemon-generated session identifier | +| `native_session_id` | string? | Provider-native session/thread identifier (e.g., Codex `threadId`, OpenCode `sessionID`) | +| `source` | string | Event origin: `agent` (native) or `daemon` (synthetic) | +| `synthetic` | boolean | Whether this event was generated by the daemon to fill gaps | +| `type` | string | Event type (see [Event Types](#event-types)) | +| `data` | object | Event-specific payload | +| `raw` | any? | Original provider payload (only when `include_raw=true`) | + +```json +{ + "event_id": "evt_abc123", + "sequence": 1, + "time": "2025-01-28T12:00:00Z", + "session_id": "my-session", + "native_session_id": "thread_xyz", + "source": "agent", + "synthetic": false, + "type": "item.completed", + "data": { ... } +} +``` + +## Event Types + +### Session Lifecycle + +| Type | Description | Data | +|------|-------------|------| +| `session.started` | Session has started | `{ metadata?: any }` | +| `session.ended` | Session has ended | `{ reason, terminated_by, message?, exit_code? }` | + +### Turn Lifecycle + +| Type | Description | Data | +|------|-------------|------| +| `turn.started` | Turn has started | `{ phase: "started", turn_id?, metadata? }` | +| `turn.ended` | Turn has ended | `{ phase: "ended", turn_id?, metadata? }` | + +**SessionEndedData** + +| Field | Type | Values | +|-------|------|--------| +| `reason` | string | `completed`, `error`, `terminated` | +| `terminated_by` | string | `agent`, `daemon` | +| `message` | string? | Error message (only present when reason is `error`) | +| `exit_code` | int? | Process exit code (only present when reason is `error`) | +| `stderr` | StderrOutput? | Structured stderr output (only present when reason is `error`) | + +**StderrOutput** + +| Field | Type | Description | +|-------|------|-------------| +| `head` | string? | First 20 lines of stderr (if truncated) or full stderr (if not truncated) | +| `tail` | string? | Last 50 lines of stderr (only present if truncated) | +| `truncated` | boolean | Whether the output was truncated | +| `total_lines` | int? | Total number of lines in stderr | + +### Item Lifecycle + +| Type | Description | Data | +|------|-------------|------| +| `item.started` | Item creation | `{ item }` | +| `item.delta` | Streaming content delta | `{ item_id, native_item_id?, delta }` | +| `item.completed` | Item finalized | `{ item }` | + +Items follow a consistent lifecycle: `item.started` → `item.delta` (0 or more) → `item.completed`. + +### HITL (Human-in-the-Loop) + +| Type | Description | Data | +|------|-------------|------| +| `permission.requested` | Permission request pending | `{ permission_id, action, status, metadata? }` | +| `permission.resolved` | Permission decision recorded | `{ permission_id, action, status, metadata? }` | +| `question.requested` | Question pending user input | `{ question_id, prompt, options, status }` | +| `question.resolved` | Question answered or rejected | `{ question_id, prompt, options, status, response? }` | + +**PermissionEventData** + +| Field | Type | Description | +|-------|------|-------------| +| `permission_id` | string | Identifier for the permission request | +| `action` | string | What the agent wants to do | +| `status` | string | `requested`, `accept`, `accept_for_session`, `reject` | +| `metadata` | any? | Additional context | + +**QuestionEventData** + +| Field | Type | Description | +|-------|------|-------------| +| `question_id` | string | Identifier for the question | +| `prompt` | string | Question text | +| `options` | string[] | Available answer options | +| `status` | string | `requested`, `answered`, `rejected` | +| `response` | string? | Selected answer (when resolved) | + +### Errors + +| Type | Description | Data | +|------|-------------|------| +| `error` | Runtime error | `{ message, code?, details? }` | +| `agent.unparsed` | Parse failure | `{ error, location, raw_hash? }` | + +The `agent.unparsed` event indicates the daemon failed to parse an agent payload. This should be treated as a bug. + +## UniversalItem + +Items represent discrete units of content within a session. + +| Field | Type | Description | +|-------|------|-------------| +| `item_id` | string | Daemon-generated identifier | +| `native_item_id` | string? | Provider-native item/message identifier | +| `parent_id` | string? | Parent item ID (e.g., tool call/result parented to a message) | +| `kind` | string | Item category (see below) | +| `role` | string? | Actor role for message items | +| `status` | string | Lifecycle status | +| `content` | ContentPart[] | Ordered list of content parts | + +### ItemKind + +| Value | Description | +|-------|-------------| +| `message` | User or assistant message | +| `tool_call` | Tool invocation | +| `tool_result` | Tool execution result | +| `system` | System message | +| `status` | Status update | +| `unknown` | Unrecognized item type | + +### ItemRole + +| Value | Description | +|-------|-------------| +| `user` | User message | +| `assistant` | Assistant response | +| `system` | System prompt | +| `tool` | Tool-related message | + +### ItemStatus + +| Value | Description | +|-------|-------------| +| `in_progress` | Item is streaming or pending | +| `completed` | Item is finalized | +| `failed` | Item execution failed | + +## Content Parts + +The `content` array contains typed parts that make up an item's payload. + +### text + +Plain text content. + +```json +{ "type": "text", "text": "Hello, world!" } +``` + +### json + +Structured JSON content. + +```json +{ "type": "json", "json": { "key": "value" } } +``` + +### tool_call + +Tool invocation. + +| Field | Type | Description | +|-------|------|-------------| +| `name` | string | Tool name | +| `arguments` | string | JSON-encoded arguments | +| `call_id` | string | Unique call identifier | + +```json +{ + "type": "tool_call", + "name": "read_file", + "arguments": "{\"path\": \"/src/main.ts\"}", + "call_id": "call_abc123" +} +``` + +### tool_result + +Tool execution result. + +| Field | Type | Description | +|-------|------|-------------| +| `call_id` | string | Matching call identifier | +| `output` | string | Tool output | + +```json +{ + "type": "tool_result", + "call_id": "call_abc123", + "output": "File contents here..." +} +``` + +### file_ref + +File reference with optional diff. + +| Field | Type | Description | +|-------|------|-------------| +| `path` | string | File path | +| `action` | string | `read`, `write`, `patch` | +| `diff` | string? | Unified diff (for patches) | + +```json +{ + "type": "file_ref", + "path": "/src/main.ts", + "action": "write", + "diff": "@@ -1,3 +1,4 @@\n+import { foo } from 'bar';" +} +``` + +### image + +Image reference. + +| Field | Type | Description | +|-------|------|-------------| +| `path` | string | Image file path | +| `mime` | string? | MIME type | + +```json +{ "type": "image", "path": "/tmp/screenshot.png", "mime": "image/png" } +``` + +### reasoning + +Model reasoning/thinking content. + +| Field | Type | Description | +|-------|------|-------------| +| `text` | string | Reasoning text | +| `visibility` | string | `public` or `private` | + +```json +{ "type": "reasoning", "text": "Let me think about this...", "visibility": "public" } +``` + +### status + +Status indicator. + +| Field | Type | Description | +|-------|------|-------------| +| `label` | string | Status label | +| `detail` | string? | Additional detail | + +```json +{ "type": "status", "label": "Running tests", "detail": "3 of 10 passed" } +``` + +## Source & Synthetics + +### EventSource + +The `source` field indicates who emitted the event: + +| Value | Description | +|-------|-------------| +| `agent` | Native event from the agent | +| `daemon` | Synthetic event generated by the daemon | + +### Synthetic Events + +The daemon emits synthetic events (`synthetic: true`, `source: "daemon"`) to provide a consistent event stream across all agents. Common synthetics: + +| Synthetic | When | +|-----------|------| +| `session.started` | Agent doesn't emit explicit session start | +| `session.ended` | Agent doesn't emit explicit session end | +| `turn.started` | Agent doesn't emit explicit turn start | +| `turn.ended` | Agent doesn't emit explicit turn end | +| `item.started` | Agent doesn't emit item start events | +| `item.delta` | Agent doesn't stream deltas natively | +| `question.*` | Claude Code plan mode (from ExitPlanMode tool) | + +### Raw Payloads + +Pass `include_raw=true` to event endpoints to receive the original agent payload in the `raw` field. Useful for debugging or accessing agent-specific data not in the universal schema. + +```typescript +const events = await client.getEvents("my-session", { includeRaw: true }); +// events[0].raw contains the original agent payload +``` diff --git a/docs/troubleshooting.mdx b/docs/troubleshooting.mdx index 18186d6..838cc28 100644 --- a/docs/troubleshooting.mdx +++ b/docs/troubleshooting.mdx @@ -29,6 +29,25 @@ Verify the agent is installed: ls -la ~/.local/share/sandbox-agent/bin/ ``` +### 4. Binary libc mismatch (musl vs glibc) + +Claude Code binaries are available in both musl and glibc variants. If you see errors like: + +``` +cannot execute: required file not found +Error loading shared library libstdc++.so.6: No such file or directory +``` + +This means the wrong binary variant was downloaded. + +**For sandbox-agent 0.2.0+**: Platform detection is automatic. The correct binary (musl or glibc) is downloaded based on the runtime environment. + +**For sandbox-agent 0.1.x**: Use Alpine Linux which has native musl support: + +```dockerfile +FROM alpine:latest +RUN apk add --no-cache curl ca-certificates libstdc++ libgcc bash +``` ## Daytona Network Restrictions diff --git a/examples/shared/src/docker.ts b/examples/shared/src/docker.ts index f4161fb..8e5ce32 100644 --- a/examples/shared/src/docker.ts +++ b/examples/shared/src/docker.ts @@ -9,7 +9,7 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url)); const REPO_ROOT = path.resolve(__dirname, "..", "..", ".."); /** Pre-built Docker image with all agents installed. */ -export const FULL_IMAGE = "rivetdev/sandbox-agent:0.4.2-full"; +export const FULL_IMAGE = "rivetdev/sandbox-agent:0.4.1-full"; export interface DockerSandboxOptions { /** Container port used by sandbox-agent inside Docker. */ diff --git a/sdks/acp-http-client/package.json b/sdks/acp-http-client/package.json index 0d61dc3..0ab23bc 100644 --- a/sdks/acp-http-client/package.json +++ b/sdks/acp-http-client/package.json @@ -1,6 +1,6 @@ { "name": "acp-http-client", - "version": "0.4.2", + "version": "0.4.1", "description": "Protocol-faithful ACP JSON-RPC over streamable HTTP client.", "license": "Apache-2.0", "repository": { diff --git a/sdks/cli-shared/package.json b/sdks/cli-shared/package.json index 4b9a0ae..b1dc636 100644 --- a/sdks/cli-shared/package.json +++ b/sdks/cli-shared/package.json @@ -1,6 +1,6 @@ { "name": "@sandbox-agent/cli-shared", - "version": "0.4.2", + "version": "0.4.1", "description": "Shared helpers for sandbox-agent CLI and SDK", "license": "Apache-2.0", "repository": { diff --git a/sdks/cli/package.json b/sdks/cli/package.json index a7e42c1..862679c 100644 --- a/sdks/cli/package.json +++ b/sdks/cli/package.json @@ -1,6 +1,6 @@ { "name": "@sandbox-agent/cli", - "version": "0.4.2", + "version": "0.4.1", "description": "CLI for sandbox-agent - run AI coding agents in sandboxes", "license": "Apache-2.0", "repository": { diff --git a/sdks/cli/platforms/darwin-arm64/package.json b/sdks/cli/platforms/darwin-arm64/package.json index 9ed1a85..622fa87 100644 --- a/sdks/cli/platforms/darwin-arm64/package.json +++ b/sdks/cli/platforms/darwin-arm64/package.json @@ -1,6 +1,6 @@ { "name": "@sandbox-agent/cli-darwin-arm64", - "version": "0.4.2", + "version": "0.4.1", "description": "sandbox-agent CLI binary for macOS ARM64", "license": "Apache-2.0", "repository": { diff --git a/sdks/cli/platforms/darwin-x64/package.json b/sdks/cli/platforms/darwin-x64/package.json index 6379cdf..132a5c5 100644 --- a/sdks/cli/platforms/darwin-x64/package.json +++ b/sdks/cli/platforms/darwin-x64/package.json @@ -1,6 +1,6 @@ { "name": "@sandbox-agent/cli-darwin-x64", - "version": "0.4.2", + "version": "0.4.1", "description": "sandbox-agent CLI binary for macOS x64", "license": "Apache-2.0", "repository": { diff --git a/sdks/cli/platforms/linux-arm64/package.json b/sdks/cli/platforms/linux-arm64/package.json index bbd677a..f318dc4 100644 --- a/sdks/cli/platforms/linux-arm64/package.json +++ b/sdks/cli/platforms/linux-arm64/package.json @@ -1,6 +1,6 @@ { "name": "@sandbox-agent/cli-linux-arm64", - "version": "0.4.2", + "version": "0.4.1", "description": "sandbox-agent CLI binary for Linux arm64", "license": "Apache-2.0", "repository": { diff --git a/sdks/cli/platforms/linux-x64/package.json b/sdks/cli/platforms/linux-x64/package.json index 9793e98..5c67882 100644 --- a/sdks/cli/platforms/linux-x64/package.json +++ b/sdks/cli/platforms/linux-x64/package.json @@ -1,6 +1,6 @@ { "name": "@sandbox-agent/cli-linux-x64", - "version": "0.4.2", + "version": "0.4.1", "description": "sandbox-agent CLI binary for Linux x64", "license": "Apache-2.0", "repository": { diff --git a/sdks/cli/platforms/win32-x64/package.json b/sdks/cli/platforms/win32-x64/package.json index 0fec6cd..f3cdad6 100644 --- a/sdks/cli/platforms/win32-x64/package.json +++ b/sdks/cli/platforms/win32-x64/package.json @@ -1,6 +1,6 @@ { "name": "@sandbox-agent/cli-win32-x64", - "version": "0.4.2", + "version": "0.4.1", "description": "sandbox-agent CLI binary for Windows x64", "license": "Apache-2.0", "repository": { diff --git a/sdks/gigacode/package.json b/sdks/gigacode/package.json index 80ed110..227654f 100644 --- a/sdks/gigacode/package.json +++ b/sdks/gigacode/package.json @@ -1,6 +1,6 @@ { "name": "@sandbox-agent/gigacode", - "version": "0.4.2", + "version": "0.4.1", "description": "Gigacode CLI (sandbox-agent with OpenCode attach by default)", "license": "Apache-2.0", "repository": { diff --git a/sdks/gigacode/platforms/darwin-arm64/package.json b/sdks/gigacode/platforms/darwin-arm64/package.json index 5a347ba..ec768f0 100644 --- a/sdks/gigacode/platforms/darwin-arm64/package.json +++ b/sdks/gigacode/platforms/darwin-arm64/package.json @@ -1,6 +1,6 @@ { "name": "@sandbox-agent/gigacode-darwin-arm64", - "version": "0.4.2", + "version": "0.4.1", "description": "gigacode CLI binary for macOS arm64", "license": "Apache-2.0", "repository": { diff --git a/sdks/gigacode/platforms/darwin-x64/package.json b/sdks/gigacode/platforms/darwin-x64/package.json index 976bdb4..4500c0a 100644 --- a/sdks/gigacode/platforms/darwin-x64/package.json +++ b/sdks/gigacode/platforms/darwin-x64/package.json @@ -1,6 +1,6 @@ { "name": "@sandbox-agent/gigacode-darwin-x64", - "version": "0.4.2", + "version": "0.4.1", "description": "gigacode CLI binary for macOS x64", "license": "Apache-2.0", "repository": { diff --git a/sdks/gigacode/platforms/linux-arm64/package.json b/sdks/gigacode/platforms/linux-arm64/package.json index 94ee741..dbaf731 100644 --- a/sdks/gigacode/platforms/linux-arm64/package.json +++ b/sdks/gigacode/platforms/linux-arm64/package.json @@ -1,6 +1,6 @@ { "name": "@sandbox-agent/gigacode-linux-arm64", - "version": "0.4.2", + "version": "0.4.1", "description": "gigacode CLI binary for Linux arm64", "license": "Apache-2.0", "repository": { diff --git a/sdks/gigacode/platforms/linux-x64/package.json b/sdks/gigacode/platforms/linux-x64/package.json index e6c8f36..02905c5 100644 --- a/sdks/gigacode/platforms/linux-x64/package.json +++ b/sdks/gigacode/platforms/linux-x64/package.json @@ -1,6 +1,6 @@ { "name": "@sandbox-agent/gigacode-linux-x64", - "version": "0.4.2", + "version": "0.4.1", "description": "gigacode CLI binary for Linux x64", "license": "Apache-2.0", "repository": { diff --git a/sdks/gigacode/platforms/win32-x64/package.json b/sdks/gigacode/platforms/win32-x64/package.json index 4458d3b..0d87633 100644 --- a/sdks/gigacode/platforms/win32-x64/package.json +++ b/sdks/gigacode/platforms/win32-x64/package.json @@ -1,6 +1,6 @@ { "name": "@sandbox-agent/gigacode-win32-x64", - "version": "0.4.2", + "version": "0.4.1", "description": "gigacode CLI binary for Windows x64", "license": "Apache-2.0", "repository": { diff --git a/sdks/persist-indexeddb/package.json b/sdks/persist-indexeddb/package.json index 98c59c7..723aec1 100644 --- a/sdks/persist-indexeddb/package.json +++ b/sdks/persist-indexeddb/package.json @@ -1,6 +1,6 @@ { "name": "@sandbox-agent/persist-indexeddb", - "version": "0.4.2", + "version": "0.4.1", "description": "IndexedDB persistence driver for the Sandbox Agent TypeScript SDK (DEPRECATED)", "license": "Apache-2.0", "repository": { diff --git a/sdks/persist-postgres/package.json b/sdks/persist-postgres/package.json index 3ffba1b..1aac22d 100644 --- a/sdks/persist-postgres/package.json +++ b/sdks/persist-postgres/package.json @@ -1,6 +1,6 @@ { "name": "@sandbox-agent/persist-postgres", - "version": "0.4.2", + "version": "0.4.1", "description": "PostgreSQL persistence driver for the Sandbox Agent TypeScript SDK (DEPRECATED)", "license": "Apache-2.0", "repository": { diff --git a/sdks/persist-rivet/package.json b/sdks/persist-rivet/package.json index a8ea332..62eb57d 100644 --- a/sdks/persist-rivet/package.json +++ b/sdks/persist-rivet/package.json @@ -1,6 +1,6 @@ { "name": "@sandbox-agent/persist-rivet", - "version": "0.4.2", + "version": "0.4.1", "description": "Rivet Actor persistence driver for the Sandbox Agent TypeScript SDK (DEPRECATED)", "license": "Apache-2.0", "repository": { diff --git a/sdks/persist-sqlite/package.json b/sdks/persist-sqlite/package.json index c0a3133..7198a57 100644 --- a/sdks/persist-sqlite/package.json +++ b/sdks/persist-sqlite/package.json @@ -1,6 +1,6 @@ { "name": "@sandbox-agent/persist-sqlite", - "version": "0.4.2", + "version": "0.4.1", "description": "SQLite persistence driver for the Sandbox Agent TypeScript SDK (DEPRECATED)", "license": "Apache-2.0", "repository": { diff --git a/sdks/react/package.json b/sdks/react/package.json index cb4cf7b..c7f1fda 100644 --- a/sdks/react/package.json +++ b/sdks/react/package.json @@ -1,6 +1,6 @@ { "name": "@sandbox-agent/react", - "version": "0.4.2", + "version": "0.4.1", "description": "React components for Sandbox Agent frontend integrations", "license": "Apache-2.0", "repository": { diff --git a/sdks/typescript/package.json b/sdks/typescript/package.json index afd0190..04fb7fa 100644 --- a/sdks/typescript/package.json +++ b/sdks/typescript/package.json @@ -1,6 +1,6 @@ { "name": "sandbox-agent", - "version": "0.4.2", + "version": "0.4.1", "description": "Universal API for automatic coding agents in sandboxes. Supports Claude Code, Codex, OpenCode, and Amp.", "license": "Apache-2.0", "repository": { diff --git a/server/packages/agent-management/src/agents.rs b/server/packages/agent-management/src/agents.rs index 4f90634..785603d 100644 --- a/server/packages/agent-management/src/agents.rs +++ b/server/packages/agent-management/src/agents.rs @@ -1093,9 +1093,9 @@ fn write_mock_agent_process_launcher(path: &Path) -> Result<(), AgentError> { fs::create_dir_all(parent)?; } let script = if cfg!(windows) { - "@echo off\r\nif not \"%SANDBOX_AGENT_BIN%\"==\"\" (\r\n \"%SANDBOX_AGENT_BIN%\" mock-agent-process %*\r\n exit /b %errorlevel%\r\n)\r\nsandbox-agent mock-agent-process %*\r\n" + "@echo off\r\nsandbox-agent mock-agent-process %*\r\n" } else { - "#!/usr/bin/env sh\nif [ -n \"${SANDBOX_AGENT_BIN:-}\" ]; then\n exec \"$SANDBOX_AGENT_BIN\" mock-agent-process \"$@\"\nfi\nexec sandbox-agent mock-agent-process \"$@\"\n" + "#!/usr/bin/env sh\nexec sandbox-agent mock-agent-process \"$@\"\n" }; write_text_file(path, script) } @@ -1969,34 +1969,6 @@ exit 0 assert_eq!(result.artifacts[0].source, InstallSource::Builtin); } - #[test] - fn mock_launcher_prefers_sandbox_agent_bin() { - let temp_dir = tempfile::tempdir().expect("create tempdir"); - let manager = AgentManager::with_platform(temp_dir.path(), Platform::LinuxX64); - - manager - .install( - AgentId::Mock, - InstallOptions { - reinstall: true, - version: None, - agent_process_version: None, - }, - ) - .expect("mock install"); - - let launcher = manager.agent_process_path(AgentId::Mock); - let mut file = fs::File::open(&launcher).expect("open mock launcher"); - let mut contents = String::new(); - file.read_to_string(&mut contents) - .expect("read mock launcher"); - - assert!( - contents.contains("SANDBOX_AGENT_BIN"), - "mock launcher should reference SANDBOX_AGENT_BIN" - ); - } - #[test] fn install_pi_skips_native_and_installs_fallback_npm_launcher() { let _env_lock = env_lock().lock().expect("env lock"); diff --git a/server/packages/sandbox-agent/src/acp_proxy_runtime.rs b/server/packages/sandbox-agent/src/acp_proxy_runtime.rs index e0a4f99..3f5a594 100644 --- a/server/packages/sandbox-agent/src/acp_proxy_runtime.rs +++ b/server/packages/sandbox-agent/src/acp_proxy_runtime.rs @@ -298,7 +298,7 @@ impl AcpProxyRuntime { let resolve_started = std::time::Instant::now(); let manager = self.inner.agent_manager.clone(); - let mut launch = tokio::task::spawn_blocking(move || manager.resolve_agent_process(agent)) + let launch = tokio::task::spawn_blocking(move || manager.resolve_agent_process(agent)) .await .map_err(|err| SandboxError::StreamError { message: format!("failed to resolve agent process launch spec: {err}"), @@ -307,16 +307,6 @@ impl AcpProxyRuntime { message: err.to_string(), })?; - if agent == AgentId::Mock { - if let Ok(exe) = std::env::current_exe() { - let path = exe.to_string_lossy().to_string(); - launch - .env - .entry("SANDBOX_AGENT_BIN".to_string()) - .or_insert(path); - } - } - tracing::info!( server_id = server_id, agent = agent.as_str(),