Worker backends
September 20, 2026 · View on GitHub
Foreman supervises a coding agent, not Codex specifically. The runtime
depends on the small Worker protocol in src/foreman/workers/base.py —
one capability and three coroutines:
supports_steering— whether the worker has a live input channel.run(record, repository, emit, timeout_seconds)— start the agent withrecord.missionas its prompt, stream bounded stdout/stderr throughemit, and record the terminal status onrecordbefore returning.steer(message)— deliver supervisory guidance into an in-flight agent. ReturnFalseif a delivery attempt is rejected. Backends without a live input channel declaresupports_steering = False, so policy uses stop/retry without attempting delivery.terminate(reason)— stop the agent promptly: interrupt first, then kill after a bounded grace period.
Built-in backends
| Backend | Selection | Steering |
|---|---|---|
| Codex App Server | FOREMAN_WORKER_BACKEND=codex (default) + FOREMAN_CODEX_BACKEND=app-server (default) | Yes, into the active turn |
| Codex exec | FOREMAN_CODEX_BACKEND=exec | No — stop/retry only |
| OpenCode | FOREMAN_WORKER_BACKEND=opencode | No — stop/retry only |
The OpenCode backend shells out to opencode run in non-interactive mode.
The prompt is passed positionally and --auto keeps the headless run from
stalling on permission prompts. Auto mode approves requests that are not
explicitly denied and Foreman does not sandbox OpenCode, so configure restrictive
permission rules in opencode.json before running untrusted jobs. An optional
model can be pinned per worker when embedding Foreman
(OpenCodeWorker(model="provider/model")).
Adding a backend
- Implement the
Workerprotocol (seeOpenCodeWorkerfor the subprocess template: bounded streaming,start_new_sessionprocess groups, interrupt-then-kill termination). - Keep the agent's environment clean: reuse
worker_environment(), which inherits the process environment minus Foreman'sTYPESAFE_*credentials. - Wire it into
FactoryRuntime's default worker factory (or pass your ownworker_factory) and setsupports_steeringaccurately. Unknown custom workers default to non-steerable for backward compatibility. - Cover it with offline tests: command construction, pipe streaming,
launch failure, and
steer()behavior. The suite must stay offline — no credentials, network, or real agent binaries.