CLI

ovn is the command-line client inside every workspace. It talks to the workspace's runtime - the process that owns every agent's terminal - over a local socket, so it works from any agent's shell without a login. Its main user is an agent: Claude Code in a workspace already knows ovn, and uses it to split a task across other agents and supervise them.

ovn spawn codex 'add a regression test for the auth timeout bug'
ovn list
ovn wait ag_01J8… --until idle --until blocked
ovn read ag_01J8…
ovn input ag_01J8… 1
ovn kill ag_01J8…

Every command is also spelled ovn agent <command> (ovn agent spawn …); the names follow Herdr's, so scripts written for one read naturally on the other.

Commands

CommandDoes
ovn listevery agent: id, kind, status, task
ovn spawn <kind> [task]start an agent (alias start)
ovn read <id>print its screen
ovn wait <id>block until it reaches a status
ovn input <id> <text>type into it, then Enter (alias prompt)
ovn kill <id>stop it

Global flags

Flag
--jsonprint the result as JSON; every agent carries a lifecycle field
--socket <path>talk to another socket
-h, --helphelp, overall or for one command (ovn spawn --help)
-V, --versionthe version

Flags can go anywhere; -- ends them.

ovn spawn <kind> [task]

<kind> is claude, codex, opencode or gemini, started exactly as the cockpit starts them (Agents).

FlagDefault
--task <text>the task; wins over the positional one
--no-worktreework in the shared checkout instead of a worktree of its own
--askask before every command and edit, instead of Auto (Agents)
--cwd <path>start in this directory, with no worktree
--cols, --rows120 × 40terminal size
--waitwait for the agent before returning
--until <status>wait for this status; repeatable; implies --wait
--timeout <ms>300000how long to wait; 0 waits forever

Without --no-worktree the agent gets /workspace/wt-<id> on branch ovn/<id>. When the agent takes its task as input rather than an argument, ovn types it in once the agent is ready.

ovn read <id>

FlagDefault
--lines <n>allonly the last n non-empty lines
--format text|ansitextansi keeps colours and cursor codes (also --ansi)

--json adds the terminal title and progress the agent reported.

ovn wait <id>

FlagDefault
--until <status>idle, blocked, donerepeatable
--timeout <ms>3000000 waits forever

Statuses are working, blocked, idle, error and done - the process exited. wait answers at once when the agent is already there.

A freshly started agent is not idle: until its screen is recognised it is starting, and wait keeps waiting through it. Otherwise wait --until idle would return the moment an agent launched.

ovn input <id> <text>

Flag
--no-entertype without pressing Enter
--wait, --until, --timeoutas for spawn

The rest of the line is the text. - reads it from standard input. Input sent before the agent is ready is queued, not lost.

ovn kill <id>

--signal <name>, default SIGTERM.

Exit codes

CodeMeaning
0done
1refused or failed - including an agent that exited before the status you waited for
2usage error, or an unknown agent id
3wait timed out
4no runtime on the socket

Agent to agent

An agent that supervises others follows one loop: start, wait, look, answer, repeat.

id=$(ovn spawn claude 'add a regression test for the auth timeout bug' --json | jq -r .agent.id)

while true; do
  ovn wait "$id" --until idle --until blocked --until done --timeout 600000 --json > /tmp/state.json
  case "$(jq -r .agent.lifecycle /tmp/state.json)" in
    blocked) ovn read "$id" ;;        # look, decide, then `ovn input`
    idle)    break ;;                 # it finished its turn
    exited)  break ;;
  esac
done

Answering a question:

ovn read ag_01J8…            # see what it is asking
ovn input ag_01J8… 1         # numbered options: send the number
ovn input ag_01J8… yes       # free text: send the text
ovn input ag_01J8… 1 --until idle --until blocked --timeout 300000

Branching on how a wait ended:

ovn wait "$id" --until idle --timeout 60000 || case $? in
  3) echo "still working" ;;
  1) echo "it exited first" ;;
esac

The rules an agent in a workspace is taught:

  • Wait, do not poll. ovn wait returns the moment something changes; looping over ovn list does not.
  • Read before you answer, and leave a destructive prompt blocked for a person to decide.
  • Merge its work with git merge ovn/<id> from your own worktree, or let it push and open its own pull request - git push and gh pr create already work.
  • Kill what you start when you are done with it.

Agents started with ovn appear in the cockpit like any other: you can watch their terminals and answer them from your phone. They get no connector tokens of their own.

The socket

ovn finds the runtime at --socket, then $OVERNITE_SOCKET, then $XDG_RUNTIME_DIR/overnite/server.sock. In a workspace OVERNITE_SOCKET is /run/overnite.sock, and every agent inherits it.

The socket speaks newline-delimited JSON - { "id", "method", "params" } in, { "id", "ok", "result" | "error" } out - with the methods agent.list, agent.spawn, agent.read, agent.wait, agent.input and agent.kill, and pushes agent.spawned, agent.status and agent.exited events.