mirror of
https://github.com/harivansh-afk/sandbox-agent.git
synced 2026-04-17 10:02:26 +00:00
acp spec
This commit is contained in:
parent
a33b1323ff
commit
2ba630c180
264 changed files with 18559 additions and 51021 deletions
221
research/acp/ts-client.md
Normal file
221
research/acp/ts-client.md
Normal file
|
|
@ -0,0 +1,221 @@
|
|||
# TypeScript Client Rewrite Spec (ACP HTTP Client + Sandbox Agent SDK)
|
||||
|
||||
## Status
|
||||
- Draft.
|
||||
- Captures confirmed decisions and server-verified contracts before implementation.
|
||||
|
||||
## Goals
|
||||
- Split TypeScript clients into:
|
||||
1. `acp-http-client`: protocol-pure ACP-over-HTTP transport/client.
|
||||
2. `sandbox-agent` SDK: Sandbox Agent wrapper that hides ACP terminology and applies Sandbox-specific metadata/extensions.
|
||||
- Make the Sandbox Agent SDK API as simple as creating a client and connecting once.
|
||||
- Remove ACP-facing API from `sandbox-agent` public surface.
|
||||
|
||||
## Confirmed Product Decisions
|
||||
- Dedicated protocol package name: `acp-http-client`.
|
||||
- `acp-http-client` must implement ACP HTTP protocol "to the T" and include no Sandbox-specific metadata/extensions.
|
||||
- Sandbox SDK public constructor pattern: `new SandboxAgentClient(...)`.
|
||||
- Sandbox SDK auto-connects by default, but supports disabling auto-connect.
|
||||
- ACP-related SDK calls must fail if `.connect()` has not been called.
|
||||
- After `.disconnect()`, ACP-related SDK calls must fail until reconnected.
|
||||
- A `SandboxAgentClient` instance can hold at most one active ACP connection.
|
||||
- No API for creating multiple ACP clients per wrapper instance.
|
||||
- ACP terminology should not appear in Sandbox SDK public API/docs.
|
||||
- Sandbox SDK should be a thin conversion layer on top of ACP protocol, mainly for metadata/event conversion.
|
||||
- Existing ACP-facing methods in `sandbox-agent` are removed (full rewrite).
|
||||
- Non-ACP HTTP helpers remain in `sandbox-agent` (health/agents/install/fs/etc).
|
||||
|
||||
## Server-Verified v2 ACP Contract
|
||||
|
||||
### HTTP endpoints and headers
|
||||
- Endpoints:
|
||||
1. `POST /v2/rpc`
|
||||
2. `GET /v2/rpc` (SSE)
|
||||
3. `DELETE /v2/rpc`
|
||||
- Headers:
|
||||
1. `x-acp-connection-id` for existing connection usage.
|
||||
2. `Last-Event-ID` for SSE replay.
|
||||
3. Agent selection is in payload metadata: `params._meta["sandboxagent.dev"].agent`.
|
||||
- Sources:
|
||||
1. `server/packages/sandbox-agent/src/router.rs:862`
|
||||
2. `server/packages/sandbox-agent/src/router.rs:913`
|
||||
3. `server/packages/sandbox-agent/src/router.rs:948`
|
||||
4. `server/packages/sandbox-agent/src/acp_runtime/mod.rs:26`
|
||||
5. `server/packages/sandbox-agent/src/acp_runtime/mod.rs:27`
|
||||
|
||||
### Custom `_sandboxagent/*` methods/events currently implemented
|
||||
- Request methods handled in runtime:
|
||||
1. `_sandboxagent/session/detach`
|
||||
2. `_sandboxagent/session/terminate`
|
||||
3. `_sandboxagent/session/list_models`
|
||||
4. `_sandboxagent/session/set_metadata`
|
||||
- Notification methods handled in runtime:
|
||||
1. `_sandboxagent/session/detach`
|
||||
2. `_sandboxagent/session/terminate`
|
||||
3. `_sandboxagent/session/set_metadata`
|
||||
- Runtime notifications:
|
||||
1. `_sandboxagent/session/ended`
|
||||
2. `_sandboxagent/agent/unparsed`
|
||||
- Sources:
|
||||
1. `server/packages/sandbox-agent/src/acp_runtime/ext_methods.rs:3`
|
||||
2. `server/packages/sandbox-agent/src/acp_runtime/ext_methods.rs:4`
|
||||
3. `server/packages/sandbox-agent/src/acp_runtime/ext_methods.rs:5`
|
||||
4. `server/packages/sandbox-agent/src/acp_runtime/ext_methods.rs:6`
|
||||
5. `server/packages/sandbox-agent/src/acp_runtime/ext_methods.rs:7`
|
||||
6. `server/packages/sandbox-agent/src/acp_runtime/ext_methods.rs:8`
|
||||
7. `server/packages/sandbox-agent/src/acp_runtime/ext_methods.rs:11`
|
||||
8. `server/packages/sandbox-agent/src/acp_runtime/ext_methods.rs:30`
|
||||
9. `server/packages/sandbox-agent/src/acp_runtime/mod.rs:1496`
|
||||
10. `server/packages/sandbox-agent/src/acp_runtime/backend.rs:95`
|
||||
|
||||
### Custom extension capability advertisement
|
||||
- Injected into `initialize` response at:
|
||||
- `result.agentCapabilities._meta["sandboxagent.dev"].extensions`
|
||||
- Includes booleans and `methods` array for extension availability.
|
||||
- Source:
|
||||
1. `server/packages/sandbox-agent/src/acp_runtime/ext_meta.rs:32`
|
||||
2. `server/packages/sandbox-agent/src/acp_runtime/ext_meta.rs:55`
|
||||
3. `server/packages/sandbox-agent/tests/v2_api/acp_extensions.rs:3`
|
||||
|
||||
## Server-Verified `_meta["sandboxagent.dev"]` Behavior
|
||||
|
||||
### Namespace definition
|
||||
- Canonical metadata namespace key: `sandboxagent.dev`.
|
||||
- Source:
|
||||
1. `server/packages/sandbox-agent/src/acp_runtime/ext_meta.rs:4`
|
||||
|
||||
### Inbound metadata ingestion
|
||||
- `session/new` reads `_meta["sandboxagent.dev"]` as map and stores it.
|
||||
- Source:
|
||||
1. `server/packages/sandbox-agent/src/acp_runtime/mod.rs:610`
|
||||
2. `server/packages/sandbox-agent/src/acp_runtime/ext_meta.rs:21`
|
||||
|
||||
### Metadata mutation extension
|
||||
- `_sandboxagent/session/set_metadata` accepts either:
|
||||
1. `params.metadata` object, or
|
||||
2. `params._meta["sandboxagent.dev"]` object.
|
||||
- Source:
|
||||
1. `server/packages/sandbox-agent/src/acp_runtime/ext_methods.rs:163`
|
||||
2. `server/packages/sandbox-agent/src/acp_runtime/ext_methods.rs:182`
|
||||
|
||||
### Keys with explicit runtime behavior
|
||||
- `title`:
|
||||
1. Updates `session.title` and stored sandbox metadata.
|
||||
- `model`:
|
||||
1. Updates model hint and stored sandbox metadata.
|
||||
- `mode`:
|
||||
1. Updates mode hint and stored sandbox metadata.
|
||||
- Source:
|
||||
1. `server/packages/sandbox-agent/src/acp_runtime/mod.rs:1355`
|
||||
2. `server/packages/sandbox-agent/src/acp_runtime/mod.rs:1369`
|
||||
3. `server/packages/sandbox-agent/src/acp_runtime/mod.rs:1374`
|
||||
4. `server/packages/sandbox-agent/src/acp_runtime/mod.rs:1377`
|
||||
|
||||
### Keys injected/derived by runtime in `session/list`
|
||||
- Runtime always injects these keys under `_meta["sandboxagent.dev"]`:
|
||||
1. `agent`
|
||||
2. `createdAt`
|
||||
3. `updatedAt`
|
||||
4. `ended`
|
||||
5. `eventCount`
|
||||
6. `model` (if model hint exists)
|
||||
- Source:
|
||||
1. `server/packages/sandbox-agent/src/acp_runtime/mod.rs:817`
|
||||
|
||||
### Known pass-through keys (stored and returned, not strongly typed in runtime)
|
||||
- Observed in tests/docs as pass-through metadata:
|
||||
1. `variant`
|
||||
2. `requestedSessionId`
|
||||
3. `permissionMode`
|
||||
4. `skills`
|
||||
5. `agentVersionRequested`
|
||||
- Sources:
|
||||
1. `server/packages/sandbox-agent/tests/v2_api/acp_extensions.rs:145`
|
||||
2. `research/acp/v1-schema-to-acp-mapping.md:73`
|
||||
3. `research/acp/v1-schema-to-acp-mapping.md:80`
|
||||
|
||||
## Package Split
|
||||
|
||||
### Package A: `acp-http-client`
|
||||
- Scope:
|
||||
1. ACP JSON-RPC over streamable HTTP only (`/v2/rpc`, headers, SSE replay, close).
|
||||
2. Generic envelope send/receive and connection lifecycle.
|
||||
3. No `_sandboxagent/*` helpers.
|
||||
4. No `_meta["sandboxagent.dev"]` helpers.
|
||||
5. No Sandbox-specific type aliases.
|
||||
- API intent:
|
||||
1. Low-level, minimal, protocol-faithful.
|
||||
2. Usable by any ACP-compatible server.
|
||||
|
||||
### Package B: `sandbox-agent` (`SandboxAgentClient`)
|
||||
- Scope:
|
||||
1. Control-plane and host APIs: health, agents, install, filesystem, etc.
|
||||
2. Single ACP-backed session client lifecycle hidden behind sandbox naming.
|
||||
3. Metadata conversion in/out of `_meta["sandboxagent.dev"]`.
|
||||
4. Sandbox extension conversion for `_sandboxagent/*` methods/events.
|
||||
- Lifecycle rules:
|
||||
1. Constructor: `new SandboxAgentClient(options)`.
|
||||
2. Auto-connect by default (configurable opt-out).
|
||||
3. `.connect(...)` creates/activates one ACP connection.
|
||||
4. `.connect(...)` throws if already connected.
|
||||
5. `.disconnect(...)` closes current ACP connection.
|
||||
6. ACP-related methods throw a not-connected error when disconnected.
|
||||
|
||||
## ACP-Shaped vs Sandbox API Names
|
||||
|
||||
ACP-shaped names are method names that mirror ACP primitives directly (or current SDK wrappers around them), such as `initialize`, `newSession`, `prompt`, `extMethod`.
|
||||
|
||||
Naming rule: for stable ACP methods, Sandbox Agent SDK method names stay ACP-aligned; only extension/unstable helpers may use Sandbox-specific naming.
|
||||
|
||||
| ACP-shaped name | ACP protocol message | Sandbox-facing name (candidate) | Notes |
|
||||
|---|---|---|---|
|
||||
| `initialize()` | `{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"initialize\",\"params\":{...}}` | `connect()` | First request must include `params._meta[\"sandboxagent.dev\"].agent` when no connection id exists. |
|
||||
| `newSession()` | `{\"jsonrpc\":\"2.0\",\"id\":2,\"method\":\"session/new\",\"params\":{...}}` | `newSession()` | Stable ACP method name preserved. |
|
||||
| `loadSession()` | `{\"jsonrpc\":\"2.0\",\"id\":3,\"method\":\"session/load\",\"params\":{\"sessionId\":\"...\",...}}` | `loadSession()` | Stable ACP method name preserved. |
|
||||
| `prompt()` | `{\"jsonrpc\":\"2.0\",\"id\":4,\"method\":\"session/prompt\",\"params\":{...}}` | `prompt()` | Stable ACP method name preserved. |
|
||||
| `cancel()` / `session/cancel` | `{\"jsonrpc\":\"2.0\",\"method\":\"session/cancel\",\"params\":{\"sessionId\":\"...\"}}` | `cancel()` | Stable ACP method name preserved. |
|
||||
| `setSessionMode()` | `{\"jsonrpc\":\"2.0\",\"id\":5,\"method\":\"session/set_mode\",\"params\":{\"sessionId\":\"...\",\"modeId\":\"...\"}}` | `setSessionMode()` | Stable ACP method name preserved. |
|
||||
| `setSessionConfigOption()` | `{\"jsonrpc\":\"2.0\",\"id\":6,\"method\":\"session/set_config_option\",\"params\":{...}}` | `setSessionConfigOption()` | Stable ACP method name preserved. |
|
||||
| `unstableListSessions()` or `session/list` | `{\"jsonrpc\":\"2.0\",\"id\":7,\"method\":\"session/list\",\"params\":{...}}` | `listSessions()` | Wrapper chooses best server method. |
|
||||
| `unstableForkSession()` | `{\"jsonrpc\":\"2.0\",\"id\":8,\"method\":\"session/fork\",\"params\":{...}}` | `forkSession()` | Preserve capability if exposed. |
|
||||
| `unstableResumeSession()` | `{\"jsonrpc\":\"2.0\",\"id\":9,\"method\":\"session/resume\",\"params\":{...}}` | `resumeSession()` | Preserve capability if exposed. |
|
||||
| `unstableSetSessionModel()` / `session/set_model` | `{\"jsonrpc\":\"2.0\",\"id\":10,\"method\":\"session/set_model\",\"params\":{\"sessionId\":\"...\",\"modelId\":\"...\"}}` | `setSessionModel()` | ACP-aligned naming when exposed. |
|
||||
| `extMethod(\"_sandboxagent/session/list_models\")` | `{\"jsonrpc\":\"2.0\",\"id\":11,\"method\":\"_sandboxagent/session/list_models\",\"params\":{...}}` | `listModels()` | Native wrapper method. |
|
||||
| `extMethod(\"_sandboxagent/session/set_metadata\")` | `{\"jsonrpc\":\"2.0\",\"id\":12,\"method\":\"_sandboxagent/session/set_metadata\",\"params\":{...}}` | `setMetadata()` | Native wrapper method. |
|
||||
| `extMethod(\"_sandboxagent/session/detach\")` | `{\"jsonrpc\":\"2.0\",\"id\":13,\"method\":\"_sandboxagent/session/detach\",\"params\":{\"sessionId\":\"...\"}}` | `detachSession()` | Native wrapper method. |
|
||||
| `extMethod(\"_sandboxagent/session/terminate\")` | `{\"jsonrpc\":\"2.0\",\"id\":14,\"method\":\"_sandboxagent/session/terminate\",\"params\":{\"sessionId\":\"...\"}}` | `terminateSession()` | Native wrapper method. |
|
||||
| close ACP connection | `DELETE /v2/rpc` with header `x-acp-connection-id` | `disconnect()` | Transport-level close, not a JSON-RPC envelope. |
|
||||
|
||||
## Conversion Layer Requirements
|
||||
- Request conversion (sandbox -> ACP):
|
||||
1. Map sandbox method names to ACP methods.
|
||||
2. Inject/merge `_meta["sandboxagent.dev"]` where needed.
|
||||
- Response/event conversion (ACP -> sandbox):
|
||||
1. Convert `_sandboxagent/session/ended` to sandbox lifecycle event.
|
||||
2. Convert `_sandboxagent/agent/unparsed` to sandbox parse-error event.
|
||||
3. Surface metadata fields from `_meta["sandboxagent.dev"]` as first-class sandbox fields where appropriate.
|
||||
|
||||
## Error Model
|
||||
- Shared HTTP error type for non-2xx (`application/problem+json`) remains in sandbox SDK.
|
||||
- Additional wrapper errors:
|
||||
1. `NotConnectedError` for ACP-related calls before `.connect()`.
|
||||
2. `AlreadyConnectedError` when calling `.connect()` while connected.
|
||||
|
||||
## Rewrite Impact (expected)
|
||||
- Remove from `sandbox-agent` public API:
|
||||
1. `createAcpClient`
|
||||
2. `postAcpEnvelope`
|
||||
3. `closeAcpClient`
|
||||
4. ACP type re-exports from `@agentclientprotocol/sdk`
|
||||
5. ACP-named classes (`SandboxAgentAcpClient`)
|
||||
- Replace with sandbox-facing API on `SandboxAgentClient`.
|
||||
|
||||
## Testing Requirements
|
||||
- Continue integration tests against real server/runtime over real `/v2` HTTP APIs.
|
||||
- Add integration coverage for:
|
||||
1. Auto-connect on constructor.
|
||||
2. `autoConnect: false` behavior.
|
||||
3. Not-connected error gates.
|
||||
4. Single-connection guard (`connect()` twice).
|
||||
5. Metadata injection/extraction parity.
|
||||
6. Extension event conversion parity (`_sandboxagent/session/ended`, `_sandboxagent/agent/unparsed`).
|
||||
Loading…
Add table
Add a link
Reference in a new issue