What feature would you like to see?
Add a codex exec fork <session-id> [prompt] subcommand that creates a forked conversation from an existing session and runs the prompt on the new thread, mirroring the codex exec resume pattern. All server-side infrastructure (thread/fork RPC, ThreadForkParams/ThreadForkResponse types, Thread.forked_from_id metadata) already exists and is consumed by the TUI; only the codex exec CLI layer is missing.
Why
The TUI supports forking sessions via /fork and codex fork, but there is no non-interactive equivalent. External tools and automation that wrap codex exec --json cannot branch from an existing session programmatically:
- Agent frameworks that want to explore multiple solution paths from the same starting point cannot fork without spawning the full TUI.
- MCP bridge tools (like
codex-mcp-bridge) that expose codex exec as an MCP server cannot offer forkFromSession parameters because the CLI has no fork subcommand.
- CI/CD pipelines that want to re-run a previous session's context with a different prompt must either resume (mutating the original session) or start fresh (losing context).
The gap is purely at the CLI layer. The app-server already handles thread/fork (see app-server/README.md lines 260-268), the protocol types are stable and used by the TUI, and SessionConfiguredEvent already has a forked_from_id field.
Current state (verified against 3895ddd6)
codex-rs/tui/src/app_server_session.rs
fork_thread() at line 335: sends ClientRequest::ThreadFork, builds session state
thread_fork_params_from_config() at line 907: builds ThreadForkParams from Config
thread_session_state_from_thread_fork_response() at line 1024: maps response
codex-rs/exec/src/lib.rs
Command enum at line 118: Resume, Review — no Fork variant
Thread setup block at line 636: handles Resume, falls through to ThreadStart — no Fork branch
session_configured_from_thread_response() at line 1007: hardcodes forked_from_id: None
(pre-existing: discards Thread.forked_from_id from start/resume responses too)
The TUI path shows the complete fork wiring. codex exec handles resume through the same thread/list + thread/resume APIs. Fork follows the identical pattern but with ClientRequest::ThreadFork and a hard error on session-not-found (no fallback to new session).
Proposal
Add Fork(ForkArgs) to the Command enum and wire it into the execution flow. Three files, single crate (codex-exec):
cli.rs: New ForkArgs struct and Command::Fork variant.
Command::Fork(ForkArgs)
pub struct ForkArgs {
pub session_id: String, // required positional (UUID or thread name)
pub images: Vec<PathBuf>, // optional --image/-i
pub prompt: Option<String>, // optional positional (reads stdin if omitted, same as resume)
}
Simpler than ResumeArgs: no --last/--all (fork source is always explicit).
lib.rs: Three additions:
-
Prompt match arm for ExecCommand::Fork (mirrors resume: resolves prompt, builds UserInput items, returns InitialOperation::UserTurn).
-
Thread setup branch: resolves source thread ID (UUID parse or name lookup via state DB + thread/list), sends ClientRequest::ThreadFork, converts response to SessionConfiguredEvent. If source session not found, hard error (not a fallback to new session).
-
Helper functions mirroring the existing resume/start pattern:
thread_fork_params_from_config: matches TUI's version (sets ephemeral, persist_extended_history: true, same field list).
session_configured_from_thread_fork_response: maps ThreadForkResponse through the shared session_configured_from_thread_response.
resolve_fork_source_thread_id: UUID parse or name lookup, filters by cwd.
Bonus fix: session_configured_from_thread_response currently hardcodes forked_from_id: None. The patch adds a forked_from_id parameter and updates all call sites (start, resume, fork) to pass response.thread.forked_from_id.clone(). This matches how the TUI already handles it (app_server_session.rs lines 984-1044) and fixes --json mode silently discarding fork metadata from start/resume responses.
cli_tests.rs: Four parsing tests following the existing resume pattern:
fork_with_session_id_and_prompt
fork_with_session_id_only
fork_with_images
fork_requires_session_id (clap rejects missing positional)
Scope
Three files, +241 LOC, -3 LOC (the 3 deletions are the hardcoded None in existing call sites), within codex-exec only:
codex-rs/exec/src/cli.rs: ForkArgs struct, Command::Fork variant.
codex-rs/exec/src/lib.rs: fork execution flow, helpers, forked_from_id plumbing fix.
codex-rs/exec/src/cli_tests.rs: four CLI parsing tests.
No changes to codex-core, codex-protocol, codex-app-server, or codex-app-server-protocol. No new crate dependencies.
Backwards compatibility
Purely additive. The fork subcommand is a new Command variant. Existing resume, review, and bare codex exec behavior is unchanged. The forked_from_id fix is also backwards-compatible: it populates a field that was previously always None, so --json consumers that ignored it continue to work and those that wanted it now get it.
Alternatives considered
- Use
resume then manually reset context: mutates the original session. Fork creates a clean branch point without side effects on the source thread.
- Spawn TUI with
codex fork <id> and extract output: requires terminal allocation, defeats the purpose of non-interactive execution, brittle to parse.
- Add
--fork-from flag to bare codex exec: conflates session creation modes. The subcommand pattern (resume, review, fork) is cleaner and matches how the TUI already organizes these operations.
- Support
--last for fork: deferred for v1. Fork source should be explicit to avoid accidentally branching the wrong session. Can be added later if there's demand.
Open questions
- Should fork support
--all (search globally, not just current cwd) for name-based resolution? Currently scoped to cwd for safety, matching resume's default behavior.
- Should a promptless fork be allowed (just create the forked thread and exit without starting a turn)? Current behavior matches resume: reads from stdin if no prompt argument, errors if stdin is empty. A no-op fork could be useful for pre-creating branches.
Implementation
I have a working implementation on a feature branch against 3895ddd6. Happy to open a PR if this is directionally acceptable.
Verification results:
cargo check -p codex-exec: clean
cargo test -p codex-exec: 61 tests passing (including 4 new CLI parsing tests)
cargo clippy -p codex-exec: clean
Context
Driven by codex-mcp-bridge, which wraps codex exec as an MCP server. Adding a forkFromSession parameter to its codex_exec tool depends on codex exec fork existing. Without it, the bridge cannot offer session branching, which is valuable for multi-path exploration and A/B comparison workflows in agent frameworks that sit on top of MCP.
What feature would you like to see?
Add a
codex exec fork <session-id> [prompt]subcommand that creates a forked conversation from an existing session and runs the prompt on the new thread, mirroring thecodex exec resumepattern. All server-side infrastructure (thread/forkRPC,ThreadForkParams/ThreadForkResponsetypes,Thread.forked_from_idmetadata) already exists and is consumed by the TUI; only thecodex execCLI layer is missing.Why
The TUI supports forking sessions via
/forkandcodex fork, but there is no non-interactive equivalent. External tools and automation that wrapcodex exec --jsoncannot branch from an existing session programmatically:codex-mcp-bridge) that exposecodex execas an MCP server cannot offerforkFromSessionparameters because the CLI has no fork subcommand.The gap is purely at the CLI layer. The app-server already handles
thread/fork(seeapp-server/README.mdlines 260-268), the protocol types are stable and used by the TUI, andSessionConfiguredEventalready has aforked_from_idfield.Current state (verified against
3895ddd6)The TUI path shows the complete fork wiring.
codex exechandlesresumethrough the samethread/list+thread/resumeAPIs. Fork follows the identical pattern but withClientRequest::ThreadForkand a hard error on session-not-found (no fallback to new session).Proposal
Add
Fork(ForkArgs)to theCommandenum and wire it into the execution flow. Three files, single crate (codex-exec):cli.rs: New
ForkArgsstruct andCommand::Forkvariant.Simpler than
ResumeArgs: no--last/--all(fork source is always explicit).lib.rs: Three additions:
Prompt match arm for
ExecCommand::Fork(mirrors resume: resolves prompt, buildsUserInputitems, returnsInitialOperation::UserTurn).Thread setup branch: resolves source thread ID (UUID parse or name lookup via state DB +
thread/list), sendsClientRequest::ThreadFork, converts response toSessionConfiguredEvent. If source session not found, hard error (not a fallback to new session).Helper functions mirroring the existing resume/start pattern:
thread_fork_params_from_config: matches TUI's version (setsephemeral,persist_extended_history: true, same field list).session_configured_from_thread_fork_response: mapsThreadForkResponsethrough the sharedsession_configured_from_thread_response.resolve_fork_source_thread_id: UUID parse or name lookup, filters by cwd.Bonus fix:
session_configured_from_thread_responsecurrently hardcodesforked_from_id: None. The patch adds aforked_from_idparameter and updates all call sites (start, resume, fork) to passresponse.thread.forked_from_id.clone(). This matches how the TUI already handles it (app_server_session.rslines 984-1044) and fixes--jsonmode silently discarding fork metadata from start/resume responses.cli_tests.rs: Four parsing tests following the existing resume pattern:
fork_with_session_id_and_promptfork_with_session_id_onlyfork_with_imagesfork_requires_session_id(clap rejects missing positional)Scope
Three files, +241 LOC, -3 LOC (the 3 deletions are the hardcoded
Nonein existing call sites), withincodex-execonly:codex-rs/exec/src/cli.rs:ForkArgsstruct,Command::Forkvariant.codex-rs/exec/src/lib.rs: fork execution flow, helpers,forked_from_idplumbing fix.codex-rs/exec/src/cli_tests.rs: four CLI parsing tests.No changes to
codex-core,codex-protocol,codex-app-server, orcodex-app-server-protocol. No new crate dependencies.Backwards compatibility
Purely additive. The
forksubcommand is a newCommandvariant. Existingresume,review, and barecodex execbehavior is unchanged. Theforked_from_idfix is also backwards-compatible: it populates a field that was previously alwaysNone, so--jsonconsumers that ignored it continue to work and those that wanted it now get it.Alternatives considered
resumethen manually reset context: mutates the original session. Fork creates a clean branch point without side effects on the source thread.codex fork <id>and extract output: requires terminal allocation, defeats the purpose of non-interactive execution, brittle to parse.--fork-fromflag to barecodex exec: conflates session creation modes. The subcommand pattern (resume,review,fork) is cleaner and matches how the TUI already organizes these operations.--lastfor fork: deferred for v1. Fork source should be explicit to avoid accidentally branching the wrong session. Can be added later if there's demand.Open questions
--all(search globally, not just current cwd) for name-based resolution? Currently scoped to cwd for safety, matching resume's default behavior.Implementation
I have a working implementation on a feature branch against
3895ddd6. Happy to open a PR if this is directionally acceptable.Verification results:
cargo check -p codex-exec: cleancargo test -p codex-exec: 61 tests passing (including 4 new CLI parsing tests)cargo clippy -p codex-exec: cleanContext
Driven by
codex-mcp-bridge, which wrapscodex execas an MCP server. Adding aforkFromSessionparameter to itscodex_exectool depends oncodex exec forkexisting. Without it, the bridge cannot offer session branching, which is valuable for multi-path exploration and A/B comparison workflows in agent frameworks that sit on top of MCP.