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.
Open a terminal on the computer where Oda runs.
Start the headless mode with the task:
oda --profile headless "run the tests"
Read the answer on standard output. Oda exits when the task settles.
As words on the line. oda --profile headless run the tests works the same way: the words are joined with spaces into a single task.
As piped text. With no task on the line, Oda reads the task from standard input:
{ echo "Summarise these changes:"; git diff --stat; } | oda --profile headlessWith the lone dash. A - as the only task argument reads the task from standard input as well. Mixing it with words is refused: `-` must be the only task argument.
A blank task is refused before anything runs. A blank or whitespace-only task stops the run; a whitespace-only task exits 1 even when text is piped in. With no task on the line and nothing piped in, the run stops with a task is required, for example: oda --profile headless "run the tests".
Piped text is sent as it arrives, including its trailing newline.
oda: reasoning: heading, so it never mixes into the answer; a run that reasons nothing leaves standard error empty.oda: <code>: <message>: the command prefix, a code that names the kind of problem, and the detail. The code distinguishes causes, for example FS_NOT_FOUND for something that cannot be found or FS_SANDBOX_DENIED when the sandbox refuses access.Each run works in a chat of its own, saved for later, unless you name an earlier chat to continue.
Find the chat's id. A program that follows the JSON stream reads it from the opening session event.
Start the mode with the option and the new task:
oda --profile headless --session-id <id> "carry on with the task"
--session-id <id> adopts the saved chat with this id and runs the new task as its next message. An id Oda does not know fails before the task runs: session "<id>" does not exist; omit --session-id to start a new Session.--session-id requires a non-empty session id; otherwise Oda passes the exact text of the id on to the run.oda --profile headless --json "<task>" writes newline-delimited events to standard output instead of the final message.
| Event | What it carries |
|---|---|
session | Opens the stream: the chat's id and the folder the run works in. |
status | The run's phase: turn_start, step_start, step_end or turn_end. |
text | A piece of the assistant's answer, at the moment it is committed. |
thinking | The model's reasoning, at the moment it is committed. |
tool_call | A tool the run starts, with its arguments. |
tool_result | How a tool ended: completed or error. |
final | The run's final answer. |
error | A problem with the invocation itself, such as a bad option or a missing task. |
text and thinking arrive when they are committed; an attempt that was discarded never reaches the stream.final. A task that fails during the run ends with a final event and no error event; the failure signal is exit code 1 and the reason on the closing turn_end.--json turns the stream on. A literal --json after --, or as another option's value, does not switch the mode.truncated: true.type.[truncated: depth] marker.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.
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.
0 means the task completed. 1 means it did not: the run was aborted or errored, or it finished without an assistant message.
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.
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.
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.
--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.
--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.
--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.
--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.