mirror of
https://github.com/harivansh-afk/sandbox-agent.git
synced 2026-04-15 19:05:18 +00:00
fix: add native turn lifecycle and stabilize opencode session flow
This commit is contained in:
parent
2b0507c3f5
commit
91cac052b8
35 changed files with 1688 additions and 486 deletions
|
|
@ -29,7 +29,7 @@ const sessionId = `session-${crypto.randomUUID()}`;
|
|||
await client.createSession(sessionId, {
|
||||
agent: "claude",
|
||||
agentMode: "code", // Optional: agent-specific mode
|
||||
permissionMode: "default", // Optional: "default" | "plan" | "bypass"
|
||||
permissionMode: "default", // Optional: "default" | "plan" | "bypass" | "acceptEdits" (Claude: accept edits; Codex: auto-approve file changes; others: default)
|
||||
model: "claude-sonnet-4", // Optional: model override
|
||||
});
|
||||
```
|
||||
|
|
@ -155,6 +155,16 @@ function handleEvent(event: UniversalEvent) {
|
|||
break;
|
||||
}
|
||||
|
||||
case "turn.started": {
|
||||
// Turn began (useful for showing per-turn loading state)
|
||||
break;
|
||||
}
|
||||
|
||||
case "turn.ended": {
|
||||
// Turn completed (useful for ending per-turn loading state)
|
||||
break;
|
||||
}
|
||||
|
||||
case "error": {
|
||||
const { message, code } = event.data as ErrorData;
|
||||
// Display error to user
|
||||
|
|
|
|||
|
|
@ -246,7 +246,7 @@ sandbox-agent api sessions create <SESSION_ID> [OPTIONS]
|
|||
|--------|-------------|
|
||||
| `-a, --agent <AGENT>` | Agent identifier (required) |
|
||||
| `-g, --agent-mode <MODE>` | Agent mode |
|
||||
| `-p, --permission-mode <MODE>` | Permission mode (`default`, `plan`, `bypass`) |
|
||||
| `-p, --permission-mode <MODE>` | Permission mode (`default`, `plan`, `bypass`, `acceptEdits`) |
|
||||
| `-m, --model <MODEL>` | Model override |
|
||||
| `-v, --variant <VARIANT>` | Model variant |
|
||||
| `-A, --agent-version <VERSION>` | Agent version |
|
||||
|
|
@ -258,6 +258,8 @@ sandbox-agent api sessions create my-session \
|
|||
--permission-mode default
|
||||
```
|
||||
|
||||
`acceptEdits` passes through to Claude, auto-approves file changes for Codex, and is treated as `default` for other agents.
|
||||
|
||||
#### Send Message
|
||||
|
||||
```bash
|
||||
|
|
|
|||
|
|
@ -29,9 +29,11 @@ Events / Message Flow
|
|||
+------------------------+------------------------------+--------------------------------------------+-----------------------------------------+----------------------------------+
|
||||
| session.started | none | method=thread/started | type=session.created | none |
|
||||
| session.ended | SDKMessage.type=result | no explicit session end (turn/completed) | no explicit session end (session.deleted)| type=done |
|
||||
| turn.started | synthetic on message send | method=turn/started | type=session.status (busy) | synthetic on message send |
|
||||
| turn.ended | synthetic after result | method=turn/completed | type=session.idle | synthetic on done |
|
||||
| message (user) | SDKMessage.type=user | item/completed (ThreadItem.type=userMessage)| message.updated (Message.role=user) | type=message |
|
||||
| message (assistant) | SDKMessage.type=assistant | item/completed (ThreadItem.type=agentMessage)| message.updated (Message.role=assistant)| type=message |
|
||||
| message.delta | stream_event (partial) or synthetic | method=item/agentMessage/delta | type=message.part.updated (delta) | synthetic |
|
||||
| message.delta | stream_event (partial) or synthetic | method=item/agentMessage/delta | type=message.part.updated (text-part delta) | synthetic |
|
||||
| tool call | type=tool_use | method=item/mcpToolCall/progress | message.part.updated (part.type=tool) | type=tool_call |
|
||||
| tool result | user.message.content.tool_result | item/completed (tool result ThreadItem variants) | message.part.updated (part.type=tool, state=completed) | type=tool_result |
|
||||
| permission.requested | control_request.can_use_tool | none | type=permission.asked | none |
|
||||
|
|
@ -52,6 +54,8 @@ Synthetics
|
|||
+------------------------------+------------------------+--------------------------+--------------------------------------------------------------+
|
||||
| session.started | When agent emits no explicit start | session.started event | Mark source=daemon |
|
||||
| session.ended | When agent emits no explicit end | session.ended event | Mark source=daemon; reason may be inferred |
|
||||
| turn.started | When agent emits no explicit turn start | turn.started event | Mark source=daemon |
|
||||
| turn.ended | When agent emits no explicit turn end | turn.ended event | Mark source=daemon |
|
||||
| item_id (Claude) | Claude provides no item IDs | item_id | Maintain provider_item_id map when possible |
|
||||
| user message (Claude) | Claude emits only assistant output | item.completed | Mark source=daemon; preserve raw input in event metadata |
|
||||
| question events (Claude) | AskUserQuestion tool usage | question.requested/resolved | Derived from tool_use blocks (source=agent) |
|
||||
|
|
@ -60,7 +64,7 @@ Synthetics
|
|||
| message.delta (Claude) | No native deltas emitted | item.delta | Synthetic delta with full message content; source=daemon |
|
||||
| message.delta (Amp) | No native deltas | item.delta | Synthetic delta with full message content; source=daemon |
|
||||
+------------------------------+------------------------+--------------------------+--------------------------------------------------------------+
|
||||
| message.delta (OpenCode) | part delta before message | item.delta | If part arrives first, create item.started stub then delta |
|
||||
| message.delta (OpenCode) | text part delta before message | item.delta | If part arrives first, create item.started stub then delta |
|
||||
+------------------------------+------------------------+--------------------------+--------------------------------------------------------------+
|
||||
|
||||
Delta handling
|
||||
|
|
@ -70,10 +74,11 @@ Delta handling
|
|||
- Claude can emit stream_event deltas when partial streaming is enabled; Amp does not emit deltas.
|
||||
|
||||
Policy:
|
||||
- Always emit item.delta across all providers.
|
||||
- Emit item.delta for streamable text content across providers.
|
||||
- For providers without native deltas, emit a single synthetic delta containing the full content prior to item.completed.
|
||||
- For Claude when partial streaming is enabled, forward native deltas and skip the synthetic full-content delta.
|
||||
- For providers with native deltas, forward as-is; also emit item.completed when final content is known.
|
||||
- For OpenCode reasoning part deltas, emit typed reasoning item updates (item.started/item.completed with content.type=reasoning) instead of item.delta.
|
||||
|
||||
Message normalization notes
|
||||
|
||||
|
|
|
|||
|
|
@ -1157,6 +1157,10 @@
|
|||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"directory": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"model": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
|
|
@ -1165,6 +1169,10 @@
|
|||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"title": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"variant": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
|
|
@ -1595,7 +1603,9 @@
|
|||
"agentMode",
|
||||
"permissionMode",
|
||||
"ended",
|
||||
"eventCount"
|
||||
"eventCount",
|
||||
"createdAt",
|
||||
"updatedAt"
|
||||
],
|
||||
"properties": {
|
||||
"agent": {
|
||||
|
|
@ -1604,6 +1614,14 @@
|
|||
"agentMode": {
|
||||
"type": "string"
|
||||
},
|
||||
"createdAt": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
},
|
||||
"directory": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"ended": {
|
||||
"type": "boolean"
|
||||
},
|
||||
|
|
@ -1626,6 +1644,14 @@
|
|||
"sessionId": {
|
||||
"type": "string"
|
||||
},
|
||||
"title": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"updatedAt": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
},
|
||||
"variant": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
|
|
@ -1689,6 +1715,31 @@
|
|||
"daemon"
|
||||
]
|
||||
},
|
||||
"TurnEventData": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"phase"
|
||||
],
|
||||
"properties": {
|
||||
"metadata": {
|
||||
"nullable": true
|
||||
},
|
||||
"phase": {
|
||||
"$ref": "#/components/schemas/TurnPhase"
|
||||
},
|
||||
"turn_id": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"TurnPhase": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"started",
|
||||
"ended"
|
||||
]
|
||||
},
|
||||
"TurnStreamQuery": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
|
@ -1748,6 +1799,9 @@
|
|||
},
|
||||
"UniversalEventData": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/TurnEventData"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/SessionStartedData"
|
||||
},
|
||||
|
|
@ -1779,6 +1833,8 @@
|
|||
"enum": [
|
||||
"session.started",
|
||||
"session.ended",
|
||||
"turn.started",
|
||||
"turn.ended",
|
||||
"item.started",
|
||||
"item.delta",
|
||||
"item.completed",
|
||||
|
|
|
|||
|
|
@ -124,6 +124,13 @@ Every event from the API is wrapped in a `UniversalEvent` envelope.
|
|||
| `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 |
|
||||
|
|
@ -365,6 +372,8 @@ The daemon emits synthetic events (`synthetic: true`, `source: "daemon"`) to pro
|
|||
|-----------|------|
|
||||
| `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) |
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue