mirror of
https://github.com/openai/codex.git
synced 2026-10-28 18:58:46 +00:00
docs: add orchestrator/executor design note
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
# Orchestrator and Executor Split
|
||||
|
||||
## Goal
|
||||
|
||||
Codex should be able to route tool calls through a remote executor without
|
||||
teaching every tool implementation about remote filesystems, RPC retries, or
|
||||
model-specific defaults.
|
||||
|
||||
For unified exec, the main rule should be:
|
||||
|
||||
- the executor advertises the `exec_command` and `write_stdin` tool contracts
|
||||
- the orchestrator forwards tool calls plus any orchestrator-owned overrides
|
||||
- the executor owns host-local execution details, session ids, and process
|
||||
lifecycle
|
||||
- the executor does not invent model-dependent policy that only the
|
||||
orchestrator can know
|
||||
|
||||
## Current Ownership Leak
|
||||
|
||||
Today the unified exec path is split across several layers:
|
||||
|
||||
- `core/src/tools/handlers/unified_exec.rs` parses model args and normalizes
|
||||
approval-related fields
|
||||
- `core/src/unified_exec/process_manager.rs` still derives execution env,
|
||||
approval requirements, and other execution inputs from `TurnContext`
|
||||
- `core/src/tools/runtimes/unified_exec.rs` still rewrites commands for shell
|
||||
snapshot and PowerShell handling
|
||||
- `core/src/tools/spec.rs` still statically advertises unified exec from the
|
||||
orchestrator side instead of learning it from the executor
|
||||
|
||||
That makes the executor boundary fuzzy. A future remote executor would need to
|
||||
know more than "handle this tool call" because some request fields are still
|
||||
hidden behind local helpers and the tool itself is not yet executor-owned.
|
||||
|
||||
## Ownership Rule
|
||||
|
||||
The orchestrator owns:
|
||||
|
||||
- model sampling
|
||||
- tool routing and dynamic tool registration
|
||||
- model-facing tool-call parsing
|
||||
- shell selection and final argv construction
|
||||
- working-directory resolution
|
||||
- execution environment construction
|
||||
- approval-policy evaluation
|
||||
- output-policy selection, including max output truncation for the tool result
|
||||
|
||||
The executor owns:
|
||||
|
||||
- advertising host-local tool specs such as unified exec
|
||||
- spawning and tracking the process
|
||||
- streaming and buffering raw output
|
||||
- minting unified-exec session ids
|
||||
- applying the already selected sandbox attempt
|
||||
- returning the requested output slice for the current poll
|
||||
- filesystem and host-local resource access that should happen on the executor
|
||||
host
|
||||
|
||||
## Unified Exec Wire Contract
|
||||
|
||||
The unified exec boundary should be expressed as an executor-advertised tool
|
||||
contract plus explicit orchestrator overrides that can be serialized and sent
|
||||
over a future executor transport.
|
||||
|
||||
`UnifiedExecExecCommandRequest`
|
||||
|
||||
- `command`: final argv vector
|
||||
- `cwd`: absolute working directory
|
||||
- `env`: fully constructed environment for the command
|
||||
- `tty`: whether to allocate a PTY
|
||||
- `yield_time_ms`: initial poll window
|
||||
- `max_output_tokens`: tool-output truncation budget selected by the
|
||||
orchestrator
|
||||
- `sandbox_permissions`: already normalized sandbox request
|
||||
- `additional_permissions`: already normalized additional permissions
|
||||
- `additional_permissions_preapproved`: whether those permissions are already
|
||||
sticky-approved
|
||||
- `justification`: optional user-facing approval reason
|
||||
- `exec_approval_requirement`: resolved approval plan for this request
|
||||
|
||||
The executor's `exec_command` tool result should include:
|
||||
|
||||
- `process_id`: stable unified-exec session id chosen by the executor when the
|
||||
session stays open after the initial poll
|
||||
- no `process_id` for short-lived commands that exit before the initial poll
|
||||
completes
|
||||
|
||||
`UnifiedExecWriteStdinRequest`
|
||||
|
||||
- `process_id`: existing unified-exec session id
|
||||
- `input`: bytes to write, expressed as text for the tool path
|
||||
- `yield_time_ms`: poll window for this write or empty poll
|
||||
- `max_output_tokens`: tool-output truncation budget selected by the
|
||||
orchestrator
|
||||
|
||||
These are intentionally higher level than `codex-exec-server`'s low-level
|
||||
`process/start`, `process/read`, and `process/write` methods. Unified exec is a
|
||||
tool contract owned by the executor; the executor transport can map that
|
||||
contract onto lower-level process RPCs internally.
|
||||
|
||||
## Phase Plan
|
||||
|
||||
1. Let the executor advertise unified exec tool specs to the orchestrator,
|
||||
similar to MCP `listTools`.
|
||||
2. Define protocol-owned unified-exec request types for executor-owned tool
|
||||
calls plus orchestrator-owned overrides.
|
||||
3. Make the executor side mint unified-exec session ids and return them in tool
|
||||
results.
|
||||
4. Move the host-local unified-exec implementation behind the executor boundary.
|
||||
5. Reuse the same request types for a remote executor transport.
|
||||
|
||||
## Crate Split
|
||||
|
||||
- `codex-executor-protocol`: shared executor transport types with minimal
|
||||
dependencies
|
||||
- `codex-executor`: executor-side receive loop and future transport adapter
|
||||
- `codex-cli`: `codex executor` subcommand that launches the executor process
|
||||
|
||||
## Follow-ups
|
||||
|
||||
This work does not fully remove every host-local concern yet. In particular:
|
||||
|
||||
- remote network-proxy binding is still local to the executor path
|
||||
- zsh-fork backend setup remains local because it depends on executor-host
|
||||
binaries and inherited file descriptors
|
||||
- shell snapshot capture and executor bootstrapping still need a broader
|
||||
context-gathering protocol
|
||||
- unified exec is still statically registered by the orchestrator today rather
|
||||
than advertised by the executor
|
||||
|
||||
Those should be follow-up steps, but they should build on the same principle:
|
||||
the orchestrator chooses only the policy and routing inputs that are truly
|
||||
orchestrator-owned, while the executor owns host-local tool behavior.
|
||||
Reference in New Issue
Block a user