#moonbitlang/workflow/shim/codex

    Run Codex as a workflow child. This executable speaks the child contract: it reads one request line on stdin, drives codex exec --json, reads completed items and turns for the steps, usage, and answer, and writes one result file. It supports native and wasm and exposes no callable library API.

    #Launch from a workflow

    The machine running the shim needs the codex CLI on PATH (or a --command override), configured with its normal credentials. Environment variables are inherited. moonx obtains the published shim; it does not install or authenticate Codex.

    Add moonbitlang/workflow to your module and import moonbitlang/workflow and moonbitlang/workflow/spawn in moon.pkg. This example invokes a real agent, so documentation tests do not run it:

    ///|
    async fn main {
    let wf = @workflow.Workflow(
    runner=@spawn.contract_runner(launch=_ => {
    @spawn.LaunchSpec(command="moonx", args=[
    "moonbitlang/workflow/shim/codex",
    "--result-file",
    @spawn.ResultFilePlaceholder,
    ])
    }),
    max_calls=1,
    )
    let report = wf.agent(
    prompt="Describe the repository's public API in three sentences.",
    kind="explore",
    max_steps=8,
    )
    println(report.stringify())
    }

    The runner replaces {result_file} (@spawn.ResultFilePlaceholder) with a fresh private path per launch and reads the result from it; the shim's stdout carries nothing. An unversioned coordinate takes the latest published module; use moonbitlang/workflow/shim/codex@<version> to pin it. Add a workflow journal to reuse prior reports without launching Codex again.

    #Options and sandbox selection

    moonx moonbitlang/workflow/shim/codex --help

    --help prints plain text before reading a request. An option error, or a request that names no request_id, is printed on stderr and the shim exits nonzero without a result; any other malformed request is a failed result. All options, --result-file included, precede the passthrough separator.

    OptionCodex mapping
    --result-file PATHRequired: where the result file goes.
    --command EXECLI executable; default codex.
    --model NAME-m NAME; absent leaves selection to the CLI.
    --cwd DIRWorking directory of the CLI process.
    --writableSelects the write-capable sandbox below.
    --schema FILE--output-schema FILE, unless the request supplies a schema.
    -- ARGS...Passed after the shim's options and before the positional prompt.

    The shim builds exec --json --skip-git-repo-check -s read-only by default. With kind="worker" or --writable, it chooses workspace-write. These are the flags this adapter requests; the CLI's configuration and host permissions also apply. Passthrough flags can alter behavior, so keep the JSON event output intact.

    The prompt is the last positional argument. The CLI's stdin is closed from the start so it cannot wait for additional input. The shim's own stdin stays open as the parent's cancellation channel.

    Kinds do not select different Codex tasks; worker selects the sandbox policy. Input must be a string, {query, hints?}, or {prompt}. wf.agent builds the supported query shape; an object containing only task does not.

    #Reports and schema handling

    The last completed agent_message supplies the answer. A completed result's output, which the parent receives as the report, has this shape:

    { "answer": "The repository exposes ...", "engine": "codex", "thread_id": "example-thread", "notices": ["An optional configuration setting was ignored."] }

    thread_id is included when observed and notices only when nonempty. The shim does not automatically resume that thread. Workflow journal replay is a separate mechanism.

    A request schema takes precedence over --schema FILE. The shim writes the request's schema to a temporary JSON file and supplies it through --output-schema; a command-line schema path is passed through to Codex. Use an absolute path when also changing --cwd.

    With a schema, the final message is parsed as JSON and placed in answer. If parsing fails it remains a string. This adapter does not perform local JSON Schema validation. The schema describes the answer, while wf.agent_as[T] decodes the complete report wrapper. Use a wrapper type with an answer field, or decode that field explicitly after wf.agent.

    #Stream translation and limits

    CLI eventAdapter behavior
    thread.startedRecords the thread id.
    item.completed with reasoningIgnored for step counting.
    item.completed with errorA notice, not a step or terminal failure.
    Other item.completedAdds a step; an agent_message replaces the stored answer.
    turn.completedAdds its usage to the run's when input/output counters are valid.
    turn.failed or top-level error with a messageRecords a terminal failure; the first message wins.

    Steps measure completed non-reasoning, non-notice items, including commands, file changes, tool calls, and messages. They are not a count of model calls. The shim counts an item before checking steps > limits.max_steps, so the over-limit item is counted and the stop is reactive.

    Usage arrives at turn completion and is summed over turns. Prompt tokens are input_tokens, cache hits are cached_input_tokens capped at input tokens, and misses are their difference. Codex states no dollar price, so the result's usage has no cost_usd. In the recorded/tested codex-cli 0.151 behavior, interrupting a turn does not flush its usage; a capped or cancelled run's result therefore carries no usage at all, and the parent records the spend as unaccounted rather than free.

    The shared shim runtime sends SIGINT on a step stop and on the parent's cancel, and observes late output for up to 3 seconds before termination. The endings:

    How the run endedResult status
    An agent message, and a clean or nonzero exit without a recorded failurecompleted, with the report as output
    The step ceilingmax_steps_exhausted
    Parent stdin EOFinterrupted
    turn.failed or a top-level error, a launch failure, or a nonzero exit without a messagefailed, with the reason
    A clean exit without an agent messageno_report

    These rules follow the implementation and recorded codex-cli 0.151.0 fixtures in dialect.mbt, rather than assuming that every future CLI release has the same format.

    #Development

    From the repository root, inspect local help without running an agent and exercise the recorded classifiers:

    moon run shim/codex --target native -- --help moon test shim/codex --target native moon test shim/codex --target wasm

    just shims builds both CLI adapters for both targets. main.mbt wires schema files and process execution into @shim.serve; dialect.mbt interprets the stream and builds the result. When a CLI format changes, update its recorded fixtures and classifier in the same change.