Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions codex-rs/app-server-protocol/src/protocol/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,12 @@ client_request_definitions! {
response: v2::CommandExecResponse,
},

/// Run a single turn to completion without managing a thread lifecycle.
ExecRun => "exec/run" {
params: v2::ExecRunParams,
response: v2::ExecRunResponse,
},

ConfigRead => "config/read" {
params: v2::ConfigReadParams,
response: v2::ConfigReadResponse,
Expand Down
29 changes: 29 additions & 0 deletions codex-rs/app-server-protocol/src/protocol/v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1083,6 +1083,35 @@ pub struct CommandExecResponse {
pub stderr: String,
}

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub struct ExecRunParams {
pub input: Vec<UserInput>,
pub model: Option<String>,
pub model_provider: Option<String>,
pub cwd: Option<String>,
pub effort: Option<ReasoningEffort>,
pub summary: Option<ReasoningSummary>,
pub collaboration_mode: Option<CollaborationMode>,
pub personality: Option<Personality>,
pub config: Option<HashMap<String, JsonValue>>,
pub base_instructions: Option<String>,
pub developer_instructions: Option<String>,
pub output_schema: Option<JsonValue>,
}

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub struct ExecRunResponse {
pub thread_id: String,
pub turn_id: String,
pub status: TurnStatus,
pub last_agent_message: Option<String>,
pub error: Option<TurnError>,
}

// === Threads, Turns, and Items ===
// Thread APIs
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default, JsonSchema, TS)]
Expand Down
30 changes: 30 additions & 0 deletions codex-rs/app-server/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ Use the thread APIs to create, list, or archive conversations. Drive a conversat
- Initialize once: Immediately after launching the codex app-server process, send an `initialize` request with your client metadata, then emit an `initialized` notification. Any other request before this handshake gets rejected.
- Start (or resume) a thread: Call `thread/start` to open a fresh conversation. The response returns the thread object and you’ll also get a `thread/started` notification. If you’re continuing an existing conversation, call `thread/resume` with its ID instead. If you want to branch from an existing conversation, call `thread/fork` to create a new thread id with copied history.
- Begin a turn: To send user input, call `turn/start` with the target `threadId` and the user's input. Optional fields let you override model, cwd, sandbox policy, etc. This immediately returns the new turn object and triggers a `turn/started` notification.
- Or run once: Call `exec/run` to execute a single turn to completion and receive the final status/message in the response without subscribing to the event stream.
- Stream events: After `turn/start`, keep reading JSON-RPC notifications on stdout. You’ll see `item/started`, `item/completed`, deltas like `item/agentMessage/delta`, tool progress, etc. These represent streaming model output plus any side effects (commands, tool calls, reasoning notes).
- Finish the turn: When the model is done (or the turn is interrupted via making the `turn/interrupt` call), the server sends `turn/completed` with the final turn state and token usage.

Expand Down Expand Up @@ -85,6 +86,7 @@ Example (from OpenAI's official VSCode extension):
- `turn/start` — add user input to a thread and begin Codex generation; responds with the initial `turn` object and streams `turn/started`, `item/*`, and `turn/completed` notifications.
- `turn/interrupt` — request cancellation of an in-flight turn by `(thread_id, turn_id)`; success is an empty `{}` response and the turn finishes with `status: "interrupted"`.
- `review/start` — kick off Codex’s automated reviewer for a thread; responds like `turn/start` and emits `item/started`/`item/completed` notifications with `enteredReviewMode` and `exitedReviewMode` items, plus a final assistant `agentMessage` containing the review.
- `exec/run` — run a single turn to completion and return the final status/message inline without streaming notifications.
- `command/exec` — run a single command under the server sandbox without starting a thread/turn (handy for utilities and validation).
- `model/list` — list available models (with reasoning effort options).
- `collaborationMode/list` — list available collaboration mode presets (experimental, no pagination).
Expand Down Expand Up @@ -368,6 +370,34 @@ containing an `exitedReviewMode` item with the final review text:

The `review` string is plain text that already bundles the overall explanation plus a bullet list for each structured finding (matching `ThreadItem::ExitedReviewMode` in the generated schema). Use this notification to render the reviewer output in your client.

### Example: Exec run to completion

Run a single turn to completion without subscribing to streamed turn/item events:

```json
{ "method": "exec/run", "id": 31, "params": {
"input": [{
"type": "text",
"text": "Generate a concise thread title",
"textElements": []
}]
} }
{ "id": 31, "result": {
"threadId": "thread-123",
"turnId": "turn-456",
"status": "completed",
"lastAgentMessage": "Fix title generation for app-server",
"error": null
} }
```

Notes:

- `exec/run` is best for one-off utilities (for example, generating a title) where you just need the final result.
- `exec/run` always runs ephemerally, so it does not create a rollout and will not appear in `thread/list`.
- `exec/run` forces `approvalPolicy: "never"` and a read-only sandbox.
- Use `turn/start` when you want streaming events, intermediate items, or a long-lived thread.

### Example: One-off command execution

Run a standalone command (argv vector) in the server’s sandbox without creating a thread or turn:
Expand Down
Loading
Loading