One terminal UI for every coding agent you already have an account for.
unharness runs Claude Code, Codex, pi, Antigravity, Mistral Vibe and any Agent Client Protocol agent behind one interface. The transcript, keys, permission prompts, model pickers and resume work the same whichever agent is running, and you can switch agents mid-conversation without losing context.
- One interface. Every agent gets the same transcript, keybindings, model and effort pickers, and session resume.
- Switch mid-conversation.
/harness codexhands the conversation to another agent. Switching back resumes the first agent's own session. - One sandbox for every agent. Landlock on Linux and
sandbox-execon macOS confine the whole agent process: it can write only to your project, and cannot read your cloud and signing credentials. askmeansask. Nothing writes, runs or reaches out without your answer, on every harness that offers the policy. "Always allow" rules belong to unharness and apply to every agent.- Rewind with your files. Go back to any earlier prompt, and restore
the working tree to how it was then, on any harness. Checkpoints never
touch your
.git. - Configure once. MCP servers, skills and
AGENTS.mdare set up once and handed to each agent in the form it understands.
# Shell installer (Linux, macOS)
curl -fsSL https://github.com/Liquescent-Development/unharness/releases/latest/download/unharness-installer.sh | sh
# Homebrew
brew install Liquescent-Development/tap/unharness
# From source (Rust 1.89+)
cargo install --locked --git https://github.com/Liquescent-Development/unharnessYou also need at least one agent CLI, installed and signed in:
Claude Code,
Codex, pi,
Antigravity (agy), Mistral Vibe, or an
ACP agent. The sandbox needs
Linux 6.2 or newer; the macOS backend has not been run on a Mac yet.
Managing skills needs Node.js.
unharness update # shell installer: installs the latest release
unharness update --check # only says whether a newer release exists
unharness update 0.3.0 # a specific version (also to go back)unharness update replaces the binary only when the shell installer put
it there (it reads the installer's receipt in ~/.config/unharness/).
For Homebrew run brew upgrade unharness; for cargo install, run the
same cargo install again with --force. Rerunning the curl command
above also updates a shell-installer install. unharness doctor shows
which one you have. It never checks for updates by itself, and sessions
already running keep the old version until restarted.
Then check what unharness found:
unharness doctorunharness # TUI with the default harness
unharness -H codex "Refactor the parser" # pick a harness, start with a prompt
unharness --policy accept-edits # ask | accept-edits | auto | bypass
unharness --sandbox read-only # read-only | workspace-write (default) | off
unharness --resume # resume the latest conversation
unharness -p "Summarise src/" # headless: print the answer
unharness models # providers and models per harnessSee Usage for every command and flag.
| Harness | Transport | Interactive permissions | Resume | Live models |
|---|---|---|---|---|
| Claude Code | claude -p --input-format stream-json (long-lived) |
yes, incl. AskUserQuestion | --resume |
initialize; Anthropic, Bedrock, Vertex, Foundry |
| Codex | codex app-server JSON-RPC (long-lived), exec --json fallback |
yes (app-server) | thread id | model/list; built-in and configured providers |
| pi | pi --mode rpc (long-lived) |
yes, through an extension unharness loads | --session-id |
get_available_models, many providers |
| Antigravity | agy --print= --input-format stream-json (long-lived) |
no | --conversation |
agy models |
| Mistral Vibe | vibe-acp, ACP over stdio (long-lived) |
yes, in its ask mode |
session/load |
from the session, chosen when it starts |
| ACP agents | Agent Client Protocol v1 over stdio (long-lived) | yes, when the agent asks | session/resume or session/load |
from the session |
unharness drives the vendor CLIs, which change more slowly than the agent SDKs, and normalises their streaming protocols into one event model. Each harness declares what it can do, and anything it lacks shows up as a caveat in the TUI instead of failing silently:
| Claude Code | Codex (app-server) | pi | ACP agents | Codex exec, Antigravity | |
|---|---|---|---|---|---|
| Image attachments | yes | yes | per model | per agent | exec only |
| PDF and text file attachments | yes | no | no | yes (as a link the agent reads) | no |
| Plan / todo list | yes | yes | no | yes | exec only |
| Subagents: start, end, tool calls, report | yes | yes | no | Mistral Vibe: start; end and report when its agent waits for one | Antigravity: start, end, report |
| Subagents: task description and progress in words | yes | no (a name and its tool calls) | no | no | no (Antigravity: a role) |
| Subagents: stop one | yes | yes | no | no | no |
| Steer a running turn | yes | yes | yes | no (queued) | no (queued) |
| Compact on request | yes | yes | yes | no | no |
| Context-window gauge | yes | yes | yes | yes | no |
| Rate-limit windows | yes | yes | no | no | no |
| Native rewind | yes | yes | yes | Mistral Vibe; others no (fresh session) | no (fresh session) |
| Native fork | yes | yes | yes | per agent (session/fork) |
no (fresh session) |
| MCP servers from unharness's config | yes | yes | no | yes (http ones per agent) | exec only |
Four policies mean the same on every harness. If a harness lacks the one you asked for, unharness falls back to the nearest less permissive policy, never a more permissive one. If there is none, it asks you which to use.
| Policy | Claude Code | Codex (app-server) | Codex (exec) | pi | Antigravity |
|---|---|---|---|---|---|
ask |
default mode, prompts in the TUI | untrusted, prompts in the TUI |
not available | every tool call but a read prompts in the TUI | not available |
accept-edits |
acceptEdits |
on-request |
no prompts | falls back to ask* | --mode accept-edits: edits run, a shell command is refused and ends the turn |
auto |
auto (classifier) |
on-request |
--approve-for-me |
falls back to ask* | falls back to accept-edits* |
bypass |
bypassPermissions |
never + full access |
--dangerously-bypass-approvals-and-sandbox |
nothing is asked, your extensions' dialogs are auto-accepted* | --dangerously-skip-permissions |
* shown as a warning in the header.
Mistral Vibe and the other ACP agents are in ACP agents and Mistral Vibe.
The sandbox is a separate setting. No policy changes it, not even
bypass:
| Level | Writes |
|---|---|
workspace-write (default) |
workspace, harness state, temp |
read-only |
harness state and temp only |
off |
unconfined |
Read more in Permissions and Sandbox.
| Key | Action |
|---|---|
Enter / Alt+Enter |
Send (queued during a turn) / steer the running turn |
Esc |
Interrupt the running turn |
@ |
Reference a file, with fuzzy search |
Ctrl+H · Ctrl+M · Ctrl+E · Ctrl+P |
Harness · model · effort · policy |
Ctrl+R |
Resume a saved conversation |
Ctrl+V |
Attach the clipboard image |
Ctrl+S |
Open a subagent's transcript |
/rewind, /fork |
Go back to an earlier prompt; branch the conversation |
All keys and slash commands are listed in The TUI.
- Usage: commands, flags, resume, switching harnesses
- Headless runs:
-p, its output formats and schema - The TUI: keys, slash commands, attachments, rewind, subagents
- Permissions: policies and allow rules
- Sandbox: what is confined and how
- Configuration:
config.tomlreference - MCP servers · ACP agents · Mistral Vibe · Rules, skills, commands and subagents
Bug reports and pull requests are welcome. CONTRIBUTING.md covers the development setup and architecture, and AGENTS.md lists the invariants every change keeps.
unharness is free software under the GNU Affero General Public License, version 3 or later.
