Skip to content

Commit a0434bb

Browse files
authored
[app-server] doc: approvals (#7105)
Add documentation for shell and apply_patch approvals
1 parent d5f661c commit a0434bb

File tree

1 file changed

+85
-54
lines changed

1 file changed

+85
-54
lines changed

codex-rs/app-server/README.md

Lines changed: 85 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
- [Initialization](#initialization)
1010
- [Core primitives](#core-primitives)
1111
- [Thread & turn endpoints](#thread--turn-endpoints)
12+
- [Events (work-in-progress)](#events-work-in-progress)
1213
- [Auth endpoints](#auth-endpoints)
13-
- [Events (work-in-progress)](#v2-streaming-events-work-in-progress)
1414

1515
## Protocol
1616

@@ -234,6 +234,90 @@ When the reviewer finishes, the server emits `item/completed` containing the sam
234234

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

237+
## Events (work-in-progress)
238+
239+
Event notifications are the server-initiated event stream for thread lifecycles, turn lifecycles, and the items within them. After you start or resume a thread, keep reading stdout for `thread/started`, `turn/*`, and `item/*` notifications.
240+
241+
### Turn events
242+
243+
The app-server streams JSON-RPC notifications while a turn is running. Each turn starts with `turn/started` (initial `turn`) and ends with `turn/completed` (final `turn` plus token `usage`), and clients subscribe to the events they care about, rendering each item incrementally as updates arrive. The per-item lifecycle is always: `item/started` → zero or more item-specific deltas → `item/completed`.
244+
245+
- `turn/started``{ turn }` with the turn id, empty `items`, and `status: "inProgress"`.
246+
- `turn/completed``{ turn }` where `turn.status` is `completed`, `interrupted`, or `failed`; failures carry `{ error: { message, codexErrorInfo? } }`.
247+
248+
Today both notifications carry an empty `items` array even when item events were streamed; rely on `item/*` notifications for the canonical item list until this is fixed.
249+
250+
#### Thread items
251+
252+
`ThreadItem` is the tagged union carried in turn responses and `item/*` notifications. Currently we support events for the following items:
253+
- `userMessage``{id, content}` where `content` is a list of user inputs (`text`, `image`, or `localImage`).
254+
- `agentMessage``{id, text}` containing the accumulated agent reply.
255+
- `reasoning``{id, summary, content}` where `summary` holds streamed reasoning summaries (applicable for most OpenAI models) and `content` holds raw reasoning blocks (applicable for e.g. open source models).
256+
- `commandExecution``{id, command, cwd, status, commandActions, aggregatedOutput?, exitCode?, durationMs?}` for sandboxed commands; `status` is `inProgress`, `completed`, `failed`, or `declined`.
257+
- `fileChange``{id, changes, status}` describing proposed edits; `changes` list `{path, kind, diff}` and `status` is `inProgress`, `completed`, `failed`, or `declined`.
258+
- `mcpToolCall``{id, server, tool, status, arguments, result?, error?}` describing MCP calls; `status` is `inProgress`, `completed`, or `failed`.
259+
- `webSearch``{id, query}` for a web search request issued by the agent.
260+
261+
All items emit two shared lifecycle events:
262+
- `item/started` — emits the full `item` when a new unit of work begins so the UI can render it immediately; the `item.id` in this payload matches the `itemId` used by deltas.
263+
- `item/completed` — sends the final `item` once that work finishes (e.g., after a tool call or message completes); treat this as the authoritative state.
264+
265+
There are additional item-specific events:
266+
#### agentMessage
267+
- `item/agentMessage/delta` — appends streamed text for the agent message; concatenate `delta` values for the same `itemId` in order to reconstruct the full reply.
268+
#### reasoning
269+
- `item/reasoning/summaryTextDelta` — streams readable reasoning summaries; `summaryIndex` increments when a new summary section opens.
270+
- `item/reasoning/summaryPartAdded` — marks the boundary between reasoning summary sections for an `itemId`; subsequent `summaryTextDelta` entries share the same `summaryIndex`.
271+
- `item/reasoning/textDelta` — streams raw reasoning text (only applicable for e.g. open source models); use `contentIndex` to group deltas that belong together before showing them in the UI.
272+
#### commandExecution
273+
- `item/commandExecution/outputDelta` — streams stdout/stderr for the command; append deltas in order to render live output alongside `aggregatedOutput` in the final item.
274+
Final `commandExecution` items include parsed `commandActions`, `status`, `exitCode`, and `durationMs` so the UI can summarize what ran and whether it succeeded.
275+
#### fileChange
276+
`fileChange` items contain a `changes` list with `{path, kind, diff}` entries (`kind` is `add`, `delete`, or `update` with an optional `movePath`). The `status` tracks whether apply succeeded (`completed`), failed, or was `declined`.
277+
278+
### Errors
279+
`error` event is emitted whenever the server hits an error mid-turn (for example, upstream model errors or quota limits). Carries the same `{ error: { message, codexErrorInfo? } }` payload as `turn.status: "failed"` and may precede that terminal notification.
280+
281+
`codexErrorInfo` maps to the `CodexErrorInfo` enum. Common values:
282+
- `ContextWindowExceeded`
283+
- `UsageLimitExceeded`
284+
- `HttpConnectionFailed { httpStatusCode? }`: upstream HTTP failures including 4xx/5xx
285+
- `ResponseStreamConnectionFailed { httpStatusCode? }`: failure to connect to the response SSE stream
286+
- `ResponseStreamDisconnected { httpStatusCode? }`: disconnect of the response SSE stream in the middle of a turn before completion
287+
- `ResponseTooManyFailedAttempts { httpStatusCode? }`
288+
- `BadRequest`
289+
- `Unauthorized`
290+
- `SandboxError`
291+
- `InternalServerError`
292+
- `Other`: all unclassified errors
293+
294+
When an upstream HTTP status is available (for example, from the Responses API or a provider), it is forwarded in `httpStatusCode` on the relevant `codexErrorInfo` variant.
295+
296+
## Approvals
297+
298+
Certain actions (shell commands or modifying files) may require explicit user approval depending on the user's config. When `turn/start` is used, the app-server drives an approval flow by sending a server-initiated JSON-RPC request to the client. The client must respond to tell Codex whether to proceed. UIs should present these requests inline with the active turn so users can review the proposed command or diff before choosing.
299+
300+
- Requests include `threadId` and `turnId`—use them to scope UI state to the active conversation.
301+
- Respond with a single `{ "decision": "accept" | "decline" }` payload (plus optional `acceptSettings` on command executions). The server resumes or declines the work and ends the item with `item/completed`.
302+
303+
### Command execution approvals
304+
305+
Order of messages:
306+
1. `item/started` — shows the pending `commandExecution` item with `command`, `cwd`, and other fields so you can render the proposed action.
307+
2. `item/commandExecution/requestApproval` (request) — carries the same `itemId`, `threadId`, `turnId`, optionally `reason` or `risk`, plus `parsedCmd` for friendly display.
308+
3. Client response — `{ "decision": "accept", "acceptSettings": { "forSession": false } }` or `{ "decision": "decline" }`.
309+
4. `item/completed` — final `commandExecution` item with `status: "completed" | "failed" | "declined"` and execution output. Render this as the authoritative result.
310+
311+
### File change approvals
312+
313+
Order of messages:
314+
1. `item/started` — emits a `fileChange` item with `changes` (diff chunk summaries) and `status: "inProgress"`. Show the proposed edits and paths to the user.
315+
2. `item/fileChange/requestApproval` (request) — includes `itemId`, `threadId`, `turnId`, and an optional `reason`.
316+
3. Client response — `{ "decision": "accept" }` or `{ "decision": "decline" }`.
317+
4. `item/completed` — returns the same `fileChange` item with `status` updated to `completed`, `failed`, or `declined` after the patch attempt. Rely on this to show success/failure and finalize the diff state in your UI.
318+
319+
UI guidance for IDEs: surface an approval dialog as soon as the request arrives. The turn will proceed after the server receives a response to the approval request. The terminal `item/completed` notification will be sent with the appropriate status.
320+
237321
## Auth endpoints
238322

239323
The JSON-RPC auth/account surface exposes request/response methods plus server-initiated notifications (no `id`). Use these to determine auth state, start or cancel logins, logout, and inspect ChatGPT rate limits.
@@ -329,56 +413,3 @@ Field notes:
329413
- `codex app-server generate-ts --out <dir>` emits v2 types under `v2/`.
330414
- `codex app-server generate-json-schema --out <dir>` outputs `codex_app_server_protocol.schemas.json`.
331415
- See [“Authentication and authorization” in the config docs](../../docs/config.md#authentication-and-authorization) for configuration knobs.
332-
333-
334-
## Events (work-in-progress)
335-
336-
Event notifications are the server-initiated event stream for thread lifecycles, turn lifecycles, and the items within them. After you start or resume a thread, keep reading stdout for `thread/started`, `turn/*`, and `item/*` notifications.
337-
338-
### Turn events
339-
340-
The app-server streams JSON-RPC notifications while a turn is running. Each turn starts with `turn/started` (initial `turn`) and ends with `turn/completed` (final `turn` plus token `usage`), and clients subscribe to the events they care about, rendering each item incrementally as updates arrive. The per-item lifecycle is always: `item/started` → zero or more item-specific deltas → `item/completed`.
341-
342-
- `turn/started``{ turn }` with the turn id, empty `items`, and `status: "inProgress"`.
343-
- `turn/completed``{ turn }` where `turn.status` is `completed`, `interrupted`, or `failed`; failures carry `{ error: { message, codexErrorInfo? } }`.
344-
345-
Today both notifications carry an empty `items` array even when item events were streamed; rely on `item/*` notifications for the canonical item list until this is fixed.
346-
347-
#### Errors
348-
`error` event is emitted whenever the server hits an error mid-turn (for example, upstream model errors or quota limits). Carries the same `{ error: { message, codexErrorInfo? } }` payload as `turn.status: "failed"` and may precede that terminal notification.
349-
350-
`codexErrorInfo` maps to the `CodexErrorInfo` enum. Common values:
351-
- `ContextWindowExceeded`
352-
- `UsageLimitExceeded`
353-
- `HttpConnectionFailed { httpStatusCode? }`: upstream HTTP failures including 4xx/5xx
354-
- `ResponseStreamConnectionFailed { httpStatusCode? }`: failure to connect to the response SSE stream
355-
- `ResponseStreamDisconnected { httpStatusCode? }`: disconnect of the response SSE stream in the middle of a turn before completion
356-
- `ResponseTooManyFailedAttempts { httpStatusCode? }`
357-
- `BadRequest`
358-
- `Unauthorized`
359-
- `SandboxError`
360-
- `InternalServerError`
361-
- `Other`: all unclassified errors
362-
363-
When an upstream HTTP status is available (for example, from the Responses API or a provider), it is forwarded in `httpStatusCode` on the relevant `codexErrorInfo` variant.
364-
365-
#### Thread items
366-
367-
`ThreadItem` is the tagged union carried in turn responses and `item/*` notifications. Currently we support events for the following items:
368-
- `userMessage``{id, content}` where `content` is a list of user inputs (`text`, `image`, or `localImage`).
369-
- `agentMessage``{id, text}` containing the accumulated agent reply.
370-
- `reasoning``{id, summary, content}` where `summary` holds streamed reasoning summaries (applicable for most OpenAI models) and `content` holds raw reasoning blocks (applicable for e.g. open source models).
371-
- `mcpToolCall``{id, server, tool, status, arguments, result?, error?}` describing MCP calls; `status` is `inProgress`, `completed`, or `failed`.
372-
- `webSearch``{id, query}` for a web search request issued by the agent.
373-
374-
All items emit two shared lifecycle events:
375-
- `item/started` — emits the full `item` when a new unit of work begins so the UI can render it immediately; the `item.id` in this payload matches the `itemId` used by deltas.
376-
- `item/completed` — sends the final `item` once that work finishes (e.g., after a tool call or message completes); treat this as the authoritative state.
377-
378-
There are additional item-specific events:
379-
#### agentMessage
380-
- `item/agentMessage/delta` — appends streamed text for the agent message; concatenate `delta` values for the same `itemId` in order to reconstruct the full reply.
381-
#### reasoning
382-
- `item/reasoning/summaryTextDelta` — streams readable reasoning summaries; `summaryIndex` increments when a new summary section opens.
383-
- `item/reasoning/summaryPartAdded` — marks the boundary between reasoning summary sections for an `itemId`; subsequent `summaryTextDelta` entries share the same `summaryIndex`.
384-
- `item/reasoning/textDelta` — streams raw reasoning text (only applicable for e.g. open source models); use `contentIndex` to group deltas that belong together before showing them in the UI.

0 commit comments

Comments
 (0)