Headless runs

The headless mode of the command line runs one task and exits, without opening the app. Use it from a terminal or a script to hand Oda one task and read the answer, to follow the run as JSON events, or to continue a chat an earlier run saved. Oda profiles and launchers covers profiles in general, and Command line and flags covers how Oda reads the line.

Run one task and exit

  1. Open a terminal on the computer where Oda runs.

  2. Start the headless mode with the task:

    sh
    oda --profile headless "run the tests"
  3. Read the answer on standard output. Oda exits when the task settles.

Pass the task in

Read the answer and the exit code

Continue a chat from an earlier run

Each run works in a chat of its own, saved for later, unless you name an earlier chat to continue.

  1. Find the chat's id. A program that follows the JSON stream reads it from the opening session event.

  2. Start the mode with the option and the new task:

    sh
    oda --profile headless --session-id <id> "carry on with the task"

Stream the run as JSON events

oda --profile headless --json "<task>" writes newline-delimited events to standard output instead of the final message.

EventWhat it carries
sessionOpens the stream: the chat's id and the folder the run works in.
statusThe run's phase: turn_start, step_start, step_end or turn_end.
textA piece of the assistant's answer, at the moment it is committed.
thinkingThe model's reasoning, at the moment it is committed.
tool_callA tool the run starts, with its arguments.
tool_resultHow a tool ended: completed or error.
finalThe run's final answer.
errorA problem with the invocation itself, such as a bad option or a missing task.

The JSON stream's bounds

Questions, answered

How do I pass the task to a headless run?

Three ways: as words on the line, which are joined with spaces into one task; as piped text, when you give no task; or with a lone -, which reads standard input. A blank task is refused before anything runs.

Does a headless run open a window or a port?

No. The mode starts no interface, web server or browser layer and opens no listening port, and it exits on its own when the task settles.

What do the exit codes of a headless run mean?

0 means the task completed. 1 means it did not: the run was aborted or errored, or it finished without an assistant message.

Why is standard output empty but the exit code is 1?

The run produced no assistant message. Oda writes an empty line to standard output to mark that, and exits 1 because the task did not complete.

What is oda: reasoning: on standard error?

The heading before the model's reasoning for the task. Reasoning goes to standard error so the answer on standard output stays clean; a run that reasons nothing leaves standard error empty.

Can I continue a chat from an earlier headless run?

Yes. --session-id <id> adopts the saved chat with that id and runs your task as its next message; without the option, the run starts a new chat. The chat must have been saved in the folder you run from, must not be running here, and must be one this mode can adopt; a refused adoption fails before the task runs.

Why was my --session-id refused?

The chat cannot be adopted from this run: it is unknown, still running in this Oda process, saved under a preset this mode does not run, a subagent or forked chat, saved with no folder or in a different folder, or its records are damaged. The chat's saved records and their storage may also be missing where you run. Fix the named case, or omit the option to start a new chat.

What does the --json stream contain?

One event per line: session, the status phases, text, thinking, tool_call, tool_result, final, and error for a problem with the invocation itself. Text and thinking arrive at commit points, not per token.

Does the --json stream lose anything?

It is not a full copy of the run. It shows what the run committed at each point: a discarded attempt never appears, a tool result is written once even if the chat is compacted later, and a failing task still ends with final instead of an error event.

What happens to very long output in --json mode?

Strings and keys stop at 8 KiB with truncated: true, one event line stays under 32 KiB, and values nested deeper than 64 levels are cut. The final answer is never capped.