feat: sprites support

This commit is contained in:
Nathan Flurry 2026-03-25 12:22:00 -07:00
parent 9cd9252725
commit 5da35e6dfa
35 changed files with 746 additions and 1257 deletions

View file

@ -81,7 +81,7 @@ Update this file continuously during the migration.
- Date: 2026-02-10
- Area: Session lifecycle surface
- Issue: ACP stable does not include v1-equivalent methods for session listing, explicit session termination/delete, or event-log polling.
- Impact: Direct lift-and-shift of `/v1/sessions`, `/terminate`, and `/events` polling is not possible with ACP core only.
- Impact: Direct lift-and-shift of the legacy session REST list, terminate, and event-polling behavior is not possible with ACP core only.
- Proposed direction: Define `_sandboxagent/session/*` extension methods for these control operations, while keeping core prompt flow on standard ACP methods.
- Decision: Open.
- Owner: Unassigned.

View file

@ -11,4 +11,4 @@ This tracks legacy inspector behaviors that do not yet have full parity on ACP v
5. TDOO: Agent mode discovery before creating a session is not implemented (inspector currently returns cached-or-empty mode lists).
6. TDOO: Agent model discovery before creating a session is not implemented (inspector currently returns cached-or-empty model lists).
7. TDOO: Session listing only reflects sessions created by this inspector client instance (not full server/global session inventory).
8. TDOO: Event history shown in inspector is synthesized from ACP traffic handled by the inspector compatibility layer, not the old canonical `/v1/sessions/*/events` backend history.
8. TDOO: Event history shown in inspector is synthesized from ACP traffic handled by the inspector compatibility layer, not the old canonical session-events backend history.

View file

@ -20,8 +20,8 @@ Static v1 endpoints today:
- `GET /v1/agents`
- `POST /v1/agents/:agent/install`
- `GET /v1/sessions`
- `GET /v1/sessions/:session_id`
- legacy session list endpoint
- legacy session detail endpoint
- `GET /v1/fs/entries`
- `GET /v1/fs/file`
- `PUT /v1/fs/file`
@ -76,8 +76,8 @@ Interpretation for clients: all agent/session operations and non-binary filesyst
| --- | --- | --- |
| `GET /v1/agents` | `_sandboxagent/agent/list` | Response keeps current `AgentListResponse` shape for low migration risk. |
| `POST /v1/agents/:agent/install` | `_sandboxagent/agent/install` | Params include `agent`, `reinstall`, `agentVersion`, `agentProcessVersion`. |
| `GET /v1/sessions` | `_sandboxagent/session/list` | Return current `SessionListResponse` shape (not ACP unstable list shape). |
| `GET /v1/sessions/:session_id` | `_sandboxagent/session/get` | Return current `SessionInfo` shape; error on missing session. |
| legacy session list endpoint | `_sandboxagent/session/list` | Return current `SessionListResponse` shape (not ACP unstable list shape). |
| legacy session detail endpoint | `_sandboxagent/session/get` | Return current `SessionInfo` shape; error on missing session. |
| `GET /v1/fs/entries` | `_sandboxagent/fs/list_entries` | Preserve path + optional `sessionId` resolution semantics. |
| `GET /v1/fs/file` | keep HTTP + `_sandboxagent/fs/read_file` | HTTP is primary because responses may require large streaming reads; ACP variant exists for compatibility/smaller payloads. |
| `PUT /v1/fs/file` | keep HTTP + `_sandboxagent/fs/write_file` | HTTP is primary for large binary writes; ACP variant exists for compatibility/smaller payloads. |
@ -143,7 +143,7 @@ Package boundary after migration:
- `acp-http-client` remains protocol-pure ACP transport and generic `extMethod`/`extNotification`.
- `sandbox-agent` remains the typed wrapper that maps convenience methods to `_sandboxagent/...` extension methods.
- No direct `/v1/agents*`, `/v1/sessions*`, or non-binary `/v1/fs/*` fetches in SDK runtime code.
- No direct legacy agents/session REST fetches or non-binary `/v1/fs/*` fetches in SDK runtime code.
- Binary file transfer keeps direct HTTP fetches on the three endpoints listed above.
- SDK policy: prefer HTTP for `readFsFile`/`writeFsFile`/`uploadFsBatch` even if ACP extension variants exist.
@ -184,17 +184,17 @@ Alternative (optional): introduce a runtime-only control connection mode that do
- TypeScript SDK (`sdks/typescript/src/client.ts`):
- Repoint `listAgents`, `installAgent`, `listSessions`, `getSession`, `listFsEntries`, `deleteFsEntry`, `mkdirFs`, `moveFs`, and `statFs` to ACP extension calls.
- Keep `readFsFile`, `writeFsFile`, and `uploadFsBatch` on HTTP endpoints.
- Remove direct runtime fetch usage for `/v1/agents*`, `/v1/sessions*`, and non-binary `/v1/fs/*`.
- Remove direct runtime fetch usage for legacy agents/session REST endpoints and non-binary `/v1/fs/*`.
- Keep method names stable for callers.
- Move these methods to connected-only semantics (`NotConnectedError` when disconnected).
- CLI (`server/packages/sandbox-agent/src/cli.rs`):
- Make `api agents list/install` call ACP extension methods (via ACP post flow), not direct `/v1/agents*` HTTP calls.
- Make `api agents list/install` call ACP extension methods (via ACP post flow), not direct legacy agent HTTP calls.
- Inspector flow/docs:
- Stop depending on `GET /v1/agents` in startup path; use ACP extension instead.
### Phase 3: Remove Static Endpoints (Except Health + Binary FS Transfer)
- Remove route registrations for `/v1/agents*`, `/v1/sessions*`, `/v1/fs/entries`, `/v1/fs/entry`, `/v1/fs/mkdir`, `/v1/fs/move`, `/v1/fs/stat` from `router.rs`.
- Remove route registrations for legacy agent/session REST endpoints and `/v1/fs/entries`, `/v1/fs/entry`, `/v1/fs/mkdir`, `/v1/fs/move`, `/v1/fs/stat` from `router.rs`.
- Keep `/v1/health`, `/v1/rpc`, `GET /v1/fs/file`, `PUT /v1/fs/file`, and `POST /v1/fs/upload-batch`.
- Optional short deprecation period: convert removed routes to `410 Gone` with explicit extension method in `detail`.
@ -237,6 +237,6 @@ Inspector:
## Open Decisions
1. Should removed `/v1/agents*`, `/v1/sessions*`, and non-binary `/v1/fs/*` return `410` for one release or be dropped immediately?
1. Should removed legacy agent/session REST endpoints and non-binary `/v1/fs/*` return `410` for one release or be dropped immediately?
2. Do we keep a strict response-shape parity layer for session/file methods, or normalize to ACP-native shapes?
3. Should `/` service-root remain as informational HTTP, or be treated as out-of-scope for this “only health static + binary fs transfer” policy?

View file

@ -59,11 +59,11 @@ struct PendingQuestion {
}
```
## v1 HTTP Endpoints (from `router.rs`)
## Legacy Session REST Endpoints (from `router.rs`)
```
POST /v1/sessions/{session_id}/questions/{question_id}/reply -> 204 No Content
POST /v1/sessions/{session_id}/questions/{question_id}/reject -> 204 No Content
session question reply endpoint -> 204 No Content
session question reject endpoint -> 204 No Content
```
### `reply_question` handler
@ -122,7 +122,7 @@ Key flow:
1. Agent emits `question.requested` event with `QuestionEventData { status: Requested, question_id, prompt, options }`
2. Client renders question UI
3. Client calls `POST /v1/sessions/{id}/questions/{qid}/reply` with `{ answers: [["selected"]] }` or `POST .../reject`
3. Client calls the legacy session question reply or reject endpoint with `{ answers: [["selected"]] }`
4. System emits `question.resolved` event with `QuestionEventData { status: Answered, response: Some("...") }` or `{ status: Rejected }`
## v1 Agent Capability

View file

@ -4,7 +4,7 @@
## Summary
v1 had explicit session termination (`POST /v1/sessions/{id}/terminate`). v1 only has `session/cancel` (turn cancellation, not session kill) and `DELETE /v1/rpc` (connection close, not session termination). Need explicit session destroy/terminate semantics.
The legacy session REST API had an explicit terminate endpoint. ACP only has `session/cancel` (turn cancellation, not session kill) and `DELETE /v1/rpc` (connection close, not session termination). Need explicit session destroy/terminate semantics.
## Current v1 State
@ -20,7 +20,7 @@ v1 had explicit session termination (`POST /v1/sessions/{id}/terminate`). v1 onl
### HTTP Endpoint
```
POST /v1/sessions/{id}/terminate
legacy session terminate endpoint
```
### Handler (from `router.rs`)

View file

@ -49,7 +49,7 @@ Returned `AgentModelsResponse` with full model list including variants.
### Session Creation with Variant
```
POST /v1/sessions
legacy session create endpoint
```
Body included `variant: Option<String>` to select a specific model variant at session creation time.

View file

@ -34,7 +34,7 @@ pub struct UniversalEvent {
### v1 Usage
```
GET /v1/sessions/{id}/events?include_raw=true
legacy event polling endpoint with `include_raw=true`
```
When `include_raw=true`, each `UniversalEvent` included the verbatim JSON the agent process emitted before normalization into the universal schema.

View file

@ -1,10 +1,10 @@
# Feature 16: Session Info
**Implementation approach:** New HTTP endpoints (`GET /v1/sessions`, `GET /v1/sessions/{id}`)
**Implementation approach:** New session-info HTTP endpoints
## Summary
v1 `SessionInfo` tracked `event_count`, `created_at`, `updated_at`, and full `mcp` config. v1 has session data in the ACP runtime's `MetaSession` struct but no HTTP endpoints to query it. Add REST endpoints for session listing and detail.
v1 `SessionInfo` tracked `event_count`, `created_at`, `updated_at`, and full `mcp` config. v1 has session data in the ACP runtime's `MetaSession` struct but no HTTP endpoints to query it. Add HTTP endpoints for session listing and detail.
## Current v1 State
@ -117,8 +117,8 @@ fn build_session_info(state: &SessionState) -> SessionInfo {
### New HTTP Endpoints
```
GET /v1/sessions -> SessionListResponse
GET /v1/sessions/{id} -> SessionInfo
session list endpoint -> SessionListResponse
session detail endpoint -> SessionInfo
```
These are control-plane HTTP endpoints (not ACP), providing session visibility without requiring an active ACP connection.
@ -156,7 +156,7 @@ Need to add:
| File | Change |
|------|--------|
| `server/packages/sandbox-agent/src/router.rs` | Add `GET /v1/sessions` and `GET /v1/sessions/{id}` handlers; add response types |
| `server/packages/sandbox-agent/src/router.rs` | Add session list and session detail handlers; add response types |
| `server/packages/sandbox-agent/src/acp_runtime/mod.rs` | Add `created_at` to `MetaSession`; add `ended` tracking; expose `list_sessions()` and `get_session()` public methods |
| `sdks/typescript/src/client.ts` | Add `listSessions()` and `getSession(id)` methods |
| `server/packages/sandbox-agent/tests/v1_api.rs` | Add session listing and detail tests |
@ -165,6 +165,6 @@ Need to add:
| Doc | Change |
|-----|--------|
| `docs/openapi.json` | Add `/v1/sessions` and `/v1/sessions/{id}` endpoint specs |
| `docs/openapi.json` | Add session list and session detail endpoint specs |
| `docs/cli.mdx` | Add CLI `sessions list` and `sessions info` commands |
| `docs/sdks/typescript.mdx` | Document session listing SDK methods |

View file

@ -171,7 +171,7 @@ When an agent process terminates with an error:
### Session Info Integration
Termination metadata should be accessible via:
- `GET /v1/sessions/{id}` (Feature #16) — include `terminationInfo` in response when session has ended
- the session info endpoint (Feature #16) — include `terminationInfo` in response when session has ended
- `session/list` ACP response — include termination status in session entries
### Files to Modify

View file

@ -36,7 +36,7 @@ Session-level features that build on Phase A runtime tracking.
| Order | Feature | Spec | Approach | Effort |
|:-----:|--------------------------------------------------------------|:----:|------------------------------------------------------|:------:|
| B1 | [Session Info](./16-session-info.md) | #16 | New `GET /v1/sessions` and `GET /v1/sessions/{id}` | Medium |
| B1 | [Session Info](./16-session-info.md) | #16 | New session info HTTP endpoints | Medium |
| B2 | [Session Termination](./07-session-termination.md) | #7 | Idempotent `_sandboxagent/session/terminate` | Medium |
| B3 | [Error Termination Metadata](./17-error-termination-metadata.md) | #17 | Stderr capture + `_sandboxagent/session/ended` event | Medium |

View file

@ -17,16 +17,16 @@
| /v1/fs/stat | UNIMPLEMENTED |
| /v1/fs/upload-batch | UNIMPLEMENTED |
| /v1/health | UNIMPLEMENTED |
| /v1/sessions | session/list (UNSTABLE) |
| /v1/sessions/{session_id} | session/new \| session/load \| session/resume (UNSTABLE) |
| /v1/sessions/{session_id}/events | UNIMPLEMENTED |
| /v1/sessions/{session_id}/events/sse | session/update (notification stream) |
| /v1/sessions/{session_id}/messages | session/prompt |
| /v1/sessions/{session_id}/messages/stream | session/prompt + session/update notifications |
| /v1/sessions/{session_id}/permissions/{permission_id}/reply | session/request_permission response |
| /v1/sessions/{session_id}/questions/{question_id}/reject | UNIMPLEMENTED |
| /v1/sessions/{session_id}/questions/{question_id}/reply | UNIMPLEMENTED |
| /v1/sessions/{session_id}/terminate | session/cancel (turn cancellation only) |
| legacy session list route | session/list (UNSTABLE) |
| legacy session create/load/resume route | session/new \| session/load \| session/resume (UNSTABLE) |
| legacy session events polling route | UNIMPLEMENTED |
| legacy session events SSE route | session/update (notification stream) |
| legacy session prompt route | session/prompt |
| legacy session prompt + stream route | session/prompt + session/update notifications |
| legacy permission reply route | session/request_permission response |
| legacy question reject route | UNIMPLEMENTED |
| legacy question reply route | UNIMPLEMENTED |
| legacy session terminate route | session/cancel (turn cancellation only) |
| AgentCapabilities | initialize.result.agentCapabilities |
| AgentCapabilities.commandExecution | UNIMPLEMENTED |
| AgentCapabilities.errorEvents | UNIMPLEMENTED |
@ -427,7 +427,7 @@
- `UNIMPLEMENTED` means there is no ACP-standard field/method with equivalent semantics in `schema.unstable.json`; implementation would require ACP extension methods (`_...`) and/or `_meta` payloads.
- Rows mapped to `_meta[...]` are ACP-compatible extensions, not standard interoperable ACP fields; both sides must agree on names and semantics.
- Legacy event polling (`/v1/sessions/{session_id}/events`) has no ACP equivalent; ACP is stream-first via `session/update` notifications over streamable HTTP.
- Legacy event polling has no ACP equivalent; ACP is stream-first via `session/update` notifications over streamable HTTP.
- Session lifecycle differs: ACP has `session/new`, `session/load`, `session/resume` (UNSTABLE), and `session/fork` (UNSTABLE), but no standard explicit "close session" method.
- Permission handling is request/response (`session/request_permission`) tied to JSON-RPC request IDs; it does not use standalone REST reply endpoints.
- Question/answer HITL flow in the old schema has no standard ACP equivalent today (separate from permission prompts).

View file

@ -233,8 +233,6 @@ Non-ACP endpoints retained in v1:
- `GET /v1/health`
- `GET /v1/agents` (capabilities + install status)
- `POST /v1/agents/{agent}/install`
- `GET /v1/sessions`
- `GET /v1/sessions/{id}`
- `GET /v1/fs/file`
- `PUT /v1/fs/file`
- `POST /v1/fs/upload-batch`

View file

@ -54,16 +54,16 @@ Extension namespace used in this spec:
| `POST /v1/fs/move` | `POST /v1/fs/move` | HTTP platform API | Port v1 behavior. |
| `GET /v1/fs/stat` | `GET /v1/fs/stat` | HTTP platform API | Port v1 behavior. |
| `POST /v1/fs/upload-batch` | `POST /v1/fs/upload-batch` | HTTP platform API | Tar upload/extract behavior from v1. |
| `GET /v1/sessions` | `GET /v1/sessions` | HTTP control-plane | Session inventory without ACP connection requirement. |
| `POST /v1/sessions/{session_id}` | `session/new` | Standard | Path `session_id` becomes alias in `_meta["sandboxagent.dev"].requestedSessionId`. |
| `POST /v1/sessions/{session_id}/messages` | `session/prompt` | Standard | Asynchronous behavior comes from transport (request + stream). |
| `POST /v1/sessions/{session_id}/messages/stream` | `session/prompt` + consume `session/update` on SSE | Standard | Streaming is transport-level, not a distinct ACP method. |
| `POST /v1/sessions/{session_id}/terminate` | `_sandboxagent/session/terminate` | Extension | Idempotent termination semantics distinct from `DELETE /v1/rpc`. |
| `GET /v1/sessions/{session_id}/events` | `_sandboxagent/session/events` (poll view over ACP stream) | Extension | Optional compatibility helper; canonical v1 is stream consumption. |
| `GET /v1/sessions/{session_id}/events/sse` | `GET /v1/rpc` SSE stream | Standard transport | Filter by sessionId client-side or via connection/session binding. |
| `POST /v1/sessions/{session_id}/permissions/{permission_id}/reply` | JSON-RPC response to pending `session/request_permission` request id | Standard | Bridge `permission_id` to request `id` in transport state. |
| `POST /v1/sessions/{session_id}/questions/{question_id}/reply` | JSON-RPC response to pending `_sandboxagent/session/request_question` | Extension | ACP stable has no generic question/HITL request method. |
| `POST /v1/sessions/{session_id}/questions/{question_id}/reject` | JSON-RPC response to pending `_sandboxagent/session/request_question` | Extension | Encode rejection in response outcome. |
| legacy session list route | session/list | HTTP control-plane | Session inventory without ACP connection requirement. |
| legacy session create route | `session/new` | Standard | Path `session_id` becomes alias in `_meta["sandboxagent.dev"].requestedSessionId`. |
| legacy session prompt route | `session/prompt` | Standard | Asynchronous behavior comes from transport (request + stream). |
| legacy session prompt + stream route | `session/prompt` + consume `session/update` on SSE | Standard | Streaming is transport-level, not a distinct ACP method. |
| legacy session terminate route | `_sandboxagent/session/terminate` | Extension | Idempotent termination semantics distinct from `DELETE /v1/rpc`. |
| legacy event polling route | `_sandboxagent/session/events` (poll view over ACP stream) | Extension | Optional compatibility helper; canonical v1 is stream consumption. |
| legacy event SSE route | `GET /v1/rpc` SSE stream | Standard transport | Filter by sessionId client-side or via connection/session binding. |
| legacy permission reply route | JSON-RPC response to pending `session/request_permission` request id | Standard | Bridge `permission_id` to request `id` in transport state. |
| legacy question reply route | JSON-RPC response to pending `_sandboxagent/session/request_question` | Extension | ACP stable has no generic question/HITL request method. |
| legacy question reject route | JSON-RPC response to pending `_sandboxagent/session/request_question` | Extension | Encode rejection in response outcome. |
### 3.1 `CreateSessionRequest` field mapping