Add it in Tori
- Open Settings with the gear in the title bar.
- Go to Agents.
- OpenCode is already there: it ships inside Tori. When the catalog has a newer version, Add an agent lists it with Update.
Adding writes one file and runs nothing. Installing it with npm is a separate click on its card.
Use it without the catalog
Download the file and drop it in the folder below. Tori loads it the next time it starts.
Download file opencode.toml
~/.config/tori/packs/agents/
Chat and accounts
ChatChat over ACP
Tori opens a chat pane next to the terminal and talks to OpenCode through the Agent Client Protocol.
AccountsOne account
Tori uses whichever account the agent is signed in to.
Usage readoutsNot available
The agent does not report usage, so Tori shows none.
The file
opencode.tomlRead only
1schema_version = 32id = "opencode"3label = "OpenCode"4description = "OpenCode, with chat over ACP"5license = "Apache-2.0"6contributor = { name = "Tori", github = "gettori" }7icon = "opencode"89# Measured against this build with dev/acp-probe.mjs. Unlike claude.toml, almost10# nothing below is a captured wire format: an ACP adapter's job is to say how to11# *launch* the agent, and everything else - the model list, the session modes,12# what a permission prompt offers - is read from that agent's own handshake at13# runtime. So this file is short on purpose, and a shorter file is the point of14# the ACP transport rather than a gap in this one.15verified_against = "opencode 1.18.3"16verified_on = "2026-08-14"1718[launch]19program = "opencode"20# The TUI, for a PTY tab. Nothing to do with the chat surface below, which21# launches `opencode acp` instead.22base_args = []23# `--auto` is opencode's own "approve anything not explicitly denied". Its help24# calls it dangerous, which is the same standing as claude's25# `--dangerously-skip-permissions`.26yolo_args = ["--auto"]27# Deliberately empty, which the UI reads as "this agent's sessions can't be28# resumed" (see ADAPTERS.md) and says so rather than mis-resuming.29#30# opencode *does* accept `--session <id>`, so this is not a missing capability31# of the CLI. It is that the id Tori holds for an ACP row is **Tori's own**: the32# agent's id lives in the locator store (chat/acp_sessions.rs) and only the chat33# path reads it. Substituting `{id}` here would hand opencode an id it never34# minted, and it would start a fresh session while claiming to resume one.35resume_args = []3637# No [discovery], [parser] or [running].38#39# All three describe sessions that are files on disk, and an ACP agent's are40# not: the id is minted inside `session/new`, the conversation is reached with41# `session/load`, and every session of one agent shares the command line42# `opencode acp`, so a pgrep pattern would report all of them running whenever43# any one was. Tori records what the protocol told it in its own locator store44# instead, which sessions.rs merges in beside adapter discovery. Omitting the45# three is what the loader requires of a protocol-backed transport; declaring46# plausible values would be worse than declaring none.4748[capabilities]49pty_quiet_ms = 200050# The quiet-PTY x pending-tool_use join is Claude-shaped and has not been51# measured against opencode's TUI, so it is not claimed. A chat tab does not52# need it: over ACP the agent asks in-protocol and Tori renders the question.53needs_you = false54hooks = false5556# --- schema v3: accounts ---57#58# Measured 2026-08-14 against the build named in `verified_against`.59#60# **`opencode auth list` exits 0 whether or not anything is signed in.** It is61# the reason `whoami_kind` exists at all: read the exit code, as the obvious62# generic reader would, and OpenCode reports itself signed in while holding no63# credentials. What actually differs is the last line of its own table, `164# credentials` against `0 credentials`, so that count is what Tori reads.65#66# A count, not an identity. OpenCode's credentials are per *provider* (the67# measured install held one, GitHub Copilot), so "signed in" here means at least68# one provider is configured, and there is no account name to learn.69#70# **No `logout_args`.** `opencode auth logout` takes a provider argument and71# prompts when it is missing, so there is no single command that signs this72# agent out. Left empty on purpose: an empty list is the state the removal73# flow has to say out loud, and it is truer than a command that would sit74# waiting for a keystroke nobody sent.75#76# **No `home_env`.** `XDG_DATA_HOME` does relocate `auth.json` (measured: the77# probe reports 0 credentials under a fresh one), but that variable is not78# OpenCode's to own, and nothing has measured two accounts holding simultaneous79# logins. So no isolation is claimed and no "add account" action is offered.80[accounts]81login_args = ["auth", "login"]82whoami_args = ["auth", "list"]83whoami_kind = "opencode_credentials"8485[chat]86transport = "acp"8788# Its model names arrive as a "Provider/Name" path with the id repeating the89# route in lowercase ("GitHub Copilot/Claude Sonnet 4.6" over90# "github-copilot/claude-sonnet-4.6"; measured on opencode acp 1.18.3).91# Lets the picker split them for display; the wire id is never rewritten.92split_model_names = true93# The whole launch. `opencode acp` speaks ACP over stdio, and Tori spawns the94# child itself (transport::build_command, so PATH comes from a login shell)95# rather than letting the SDK spawn it.96base_args = ["acp"]9798# Every one of these is empty because ACP has no command line for it, not99# because opencode lacks the feature:100#101# * session ids are minted by the agent inside `session/new`,102# * resume is `session/load`, fork is `session/fork`,103# * a model switch is `session/set_config_option` (measured: `session/set_model`104# does not exist in ACP 2.0.0),105# * modes are a `mode`-category config option the agent advertises, measured106# as `build` and `plan` on 1.18.3.107session_id_args = []108resume_args = []109fork_args = []110model_args = []111effort_args = []112mode_args = []113add_dir_args = []114115# No [[chat.models]].116#117# Measured 2026-08-14 on opencode 1.18.3: `session/new` answers with a118# `model`-category select carrying 15 provider-qualified ids119# (`github-copilot/claude-sonnet-4.6`, `opencode/big-pickle`, ...), and120# `opencode models` reports the same 15. So the live catalogue is complete, and121# it is also **per user**: it lists the providers that account has authenticated,122# which a bundled table cannot know. The picker takes the live list whenever123# there is one and falls back to this table only when there is not, so leaving124# it empty is what makes the picker show the user's own models rather than a125# hand-maintained guess about somebody else's.126127# No [[chat.modes]] either, for the same reason: `build` and `plan` arrive as a128# config option on the handshake.129130[chat.acp]131# The two per-agent departures from a spec-correct client. It does its own file132# I/O, so the client's fs stays declined.133serve_client_fs = false134# Measured with dev/mcp-probe.mjs --acp on 1.18.3: it starts a server handed in135# `mcpServers`, passes the `env` pairs through, and the model calls the tool136# with no `session/request_permission`.137send_mcp_servers = true138139[install]140# npm names the package that ships the launch binary, and all three verbs were141# run on macOS. The binary lands in npm's global bin, which has to be on the142# login shell's PATH for Tori to find it.143program = "npm"144args = ["install", "-g", "opencode-ai"]145update_args = ["install", "-g", "opencode-ai"]146uninstall_args = ["uninstall", "-g", "opencode-ai"]