mirror of
https://github.com/harivansh-afk/sandbox-agent.git
synced 2026-04-15 15:03:37 +00:00
379 lines
13 KiB
Text
379 lines
13 KiB
Text
---
|
|
title: "Session Transcript Schema"
|
|
description: "Universal event schema for session transcripts across all agents."
|
|
icon: "brackets-curly"
|
|
---
|
|
|
|
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 |
|
|
|--------------------|:------:|:-----:|:------------:|:------------:|
|
|
| Stability | Stable | Stable| 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)
|
|
|
|
- ✓ = Appears in session events
|
|
- \- = Agent supports natively, schema conversion coming soon
|
|
- (blank) = Not supported by agent
|
|
|
|
<AccordionGroup>
|
|
<Accordion title="Text Messages">
|
|
Basic message exchange between user and assistant.
|
|
</Accordion>
|
|
<Accordion title="Tool Calls & Results">
|
|
Visibility into tool invocations (file reads, command execution, etc.) and their results. When not natively supported, tool activity is embedded in message content.
|
|
</Accordion>
|
|
<Accordion title="Questions (HITL)">
|
|
Interactive questions the agent asks the user. Emits `question.requested` and `question.resolved` events.
|
|
</Accordion>
|
|
<Accordion title="Permissions (HITL)">
|
|
Permission requests for sensitive operations. Emits `permission.requested` and `permission.resolved` events.
|
|
</Accordion>
|
|
<Accordion title="Images">
|
|
Support for image attachments in messages.
|
|
</Accordion>
|
|
<Accordion title="File Attachments">
|
|
Support for file attachments in messages.
|
|
</Accordion>
|
|
<Accordion title="Session Lifecycle">
|
|
Native `session.started` and `session.ended` events. When not supported, the daemon emits synthetic lifecycle events.
|
|
</Accordion>
|
|
<Accordion title="Error Events">
|
|
Structured error events for runtime failures.
|
|
</Accordion>
|
|
<Accordion title="Reasoning/Thinking">
|
|
Extended thinking or reasoning content with visibility controls.
|
|
</Accordion>
|
|
<Accordion title="Command Execution">
|
|
Detailed command execution events with stdout/stderr.
|
|
</Accordion>
|
|
<Accordion title="File Changes">
|
|
Structured file modification events with diffs.
|
|
</Accordion>
|
|
<Accordion title="MCP Tools">
|
|
Model Context Protocol tool support.
|
|
</Accordion>
|
|
<Accordion title="Streaming Deltas">
|
|
Native streaming of content deltas. When not supported, the daemon emits a single synthetic delta before `item.completed`.
|
|
</Accordion>
|
|
<Accordion title="Variants">
|
|
Model variants such as reasoning effort or depth. Agents may expose different variant sets per model.
|
|
</Accordion>
|
|
</AccordionGroup>
|
|
|
|
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? }` |
|
|
|
|
**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 granted or denied | `{ 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`, `approved`, `denied` |
|
|
| `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 |
|
|
| `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
|
|
```
|