Use tmux, Zellij, Herdr, cmux, or kitty to watch subagents work in live panes while OpenCode keeps running in your main session.
When OpenCode launches child agent sessions, oh-my-opencode-slim can open panes for those sessions automatically.
OpenCode running in tmux with live subagent panes.
OpenCode 1.17.18's normal default (port 0) does not expose a TCP listener that
another opencode attach process can use from a multiplexer pane. Start
OpenCode with an explicit --port, but do not hard-code 4096 when running
multiple instances. The plugin now reads ctx.serverUrl only when checking,
spawning, or polling, which avoids snapshotting the temporary startup URL; it
cannot create a listener that OpenCode did not start.
For cmux, a session status that remains missing for more than the 30-second grace period is treated as an idle candidate. Closing still requires the pane to have been attached for at least 10 seconds, three stable idle-candidate checks, and a final status recheck.
If all bounded close attempts and both cooldown retries are exhausted, the pane remains tracked as an orphan without a running timer. A later lifecycle for the same directory claims it with a fresh, bounded close-attempt budget.
This zsh helper preserves an explicit --port and exports the matching
OPENCODE_PORT. Otherwise, it asks Python to select an available loopback port
and starts OpenCode with that port explicitly:
omos() {
local port arg
for arg in "$@"; do
if [[ "$arg" == --port=* ]]; then
port="${arg#--port=}"
break
fi
done
if [[ -z "$port" ]]; then
local -a args=("$@")
local -i index
for ((index = 1; index <= ${#args}; index++)); do
if [[ "${args[index]}" == --port ]]; then
port="${args[index + 1]}"
break
fi
done
fi
if [[ -n "$port" ]]; then
OPENCODE_PORT="$port" command opencode "$@"
return
fi
port=$(python3 -c 'import socket; s = socket.socket(); s.bind(("127.0.0.1", 0)); print(s.getsockname()[1]); s.close()') || return
OPENCODE_PORT="$port" command opencode --port "$port" "$@"
}
Edit ~/.config/opencode/oh-my-opencode-slim.json (or .jsonc):
Auto-detect (recommended):
{
"multiplexer": {
"type": "auto",
"layout": "main-vertical",
"main_pane_size": 60
}
}
Tmux only:
{
"multiplexer": {
"type": "tmux",
"layout": "main-vertical",
"main_pane_size": 60
}
}
Zellij only:
{
"multiplexer": {
"type": "zellij"
}
}
Herdr only:
{
"multiplexer": {
"type": "herdr"
}
}
cmux only:
{
"multiplexer": {
"type": "cmux"
}
}
cmux 0.64.14 or newer is required; 0.64.17 or newer is recommended.
Kitty only:
{
"multiplexer": {
"type": "kitty"
}
}
Kitty requires allow_remote_control and listen_on in kitty.conf.
listen_on opens a UNIX socket and kitty exports KITTY_LISTEN_ON to its
child processes (including OpenCode). The plugin passes that env through to
every kitten @ invocation automatically — no extra plugin config is needed.
This is required because OpenCode spawns subagent commands in a process
detached from the kitty window's controlling terminal, where the tty-based
remote-control path does not work.
allow_remote_control yes
listen_on unix:/tmp/kitty-rc-$(USER)
After editing kitty.conf, restart kitty (quit and relaunch) so the socket
is created. Verify with kitten @ ls from a normal shell — it should print JSON
instead of timing out.
Tmux:
tmux
opencode --port 4096
Zellij:
zellij
opencode --port 4096
Herdr:
herdr
opencode --port 4096
cmux: Start OpenCode in a cmux surface. Auto-detection requires cmux to
provide CMUX_SOCKET_PATH, CMUX_WORKSPACE_ID, and CMUX_SURFACE_ID.
Kitty:
kitty
opencode --port 4096
Ask OpenCode to do something that launches subagents. New panes should appear automatically.
Example:
Please analyze this codebase and create a documentation structure.
{
"multiplexer": {
"type": "auto",
"layout": "main-vertical",
"main_pane_size": 60
}
}
| Setting | Type | Default | Description |
|---|---|---|---|
type |
string | "none" |
"auto", "tmux", "zellij", "herdr", "cmux", "kitty", or "none" |
layout |
string | "main-vertical" |
Layout preset for tmux; mapped to Zellij/Herdr pane directions where possible; ignored by cmux |
main_pane_size |
number | 60 |
Main pane size percentage for tmux only (20-80); ignored by Zellij, Herdr, and cmux |
zellij_pane_mode |
string | "agent-tab" |
Zellij pane placement: "agent-tab" creates/reuses a dedicated tab; "current-tab" opens panes in the tab containing the parent OpenCode pane |
| Multiplexer | Status | Notes |
|---|---|---|
| Tmux | ✅ Supported | Full layout control with main-vertical, main-horizontal, tiled, and more |
| Zellij | ✅ Supported | Creates a dedicated opencode-agents tab by default; can open panes in the parent OpenCode tab with zellij_pane_mode: "current-tab"; maps main-* layouts to pane directions |
| Herdr | ✅ Supported | Splits panes in the current Herdr workspace; maps main-vertical/even-horizontal/tiled layouts to right splits and main-horizontal/even-vertical to down splits; no layout rebalancing (like Zellij) |
| cmux | ✅ Supported | Requires cmux 0.64.14+ (0.64.17+ recommended); creates the agent column to the right and stacks subsequent agents downward without moving focus |
| Kitty | ✅ Supported | Uses kitten @ launch to open new windows; requires allow_remote_control and listen_on in kitty.conf (OpenCode must run inside a kitty window; kitty exports KITTY_LISTEN_ON which the plugin passes through to reach kitty from detached subagent processes). No layout rebalancing (like Zellij/Herdr) |
The cmux adapter equalizes vertical splits after each successful add and close.
It always creates the first agent to the right and subsequent agents downward;
both layout and main_pane_size are ignored by cmux.
cmux's workspace-level vertical equalization can affect other vertical
subtrees, so the adapter assumes its managed right-hand agent column is the
only vertical subtree in the workspace that should be automatically
equalized. layout and main_pane_size do not alter cmux's left/right width.
cmux follows the OpenCode/OMO lifecycle rather than using a placeholder pane.
Attach commands require an existing absolute OpenCode executable, resolved in
the order explicit setting, OPENCODE_BIN, process.execPath, and
process.argv[0]. If none is valid, no surface is created and a bare
opencode command is never emitted.
Activity cancels an idle close, while deletion upgrades it and retries
immediately. Failed closes and failed startup cleanup retain the encoded pane
handle and enter a 30-second then 60-second orphan cooldown. Tracking is only
removed after cmux reports closed or not_found; bounded disposal cleanup
retains unresolved orphan records.
Recovery is bounded per lifecycle instance: after its finite close-attempt
budget is exhausted, the orphan remains in the process-global registry without
an infinite retry timer. A later cmux lifecycle for the same directory takes
ownership and receives a fresh finite budget. This registry survives plugin
hot reloads in the same process, but is not persistent storage and cannot
recover state after a hard process crash.
cmux also pins pane attachment to the host OpenCode executable (or a valid
absolute OPENCODE_BIN override) instead of resolving opencode from the new
pane's PATH. This prevents a different installed OpenCode version from
starting and immediately exiting during attach.
It polls the configured server's /session/status endpoint and creates the
pane only after the child is reported as idle, running, busy, or retry.
Transient network errors, missing statuses, and server startup races are
bounded waits and therefore do not flash an empty pane. A readiness timeout or
a recoverable cmux split-capacity/layout error is retried about two seconds
later, deduplicated by session, for up to five minutes. Deleting the session or
shutting down the plugin cancels that deferred work.
To avoid closing a pane during transient idle notifications, cmux keeps it for at least ten seconds after attachment and requires three consecutive idle polls plus a final idle recheck with no intervening activity. Missing status is treated as a grace condition. This stability policy is cmux-specific; existing tmux, Zellij, and Herdr close behavior is unchanged. Native cmux support does not create placeholder panes while waiting and does not currently expose a configurable cmux column width.
Example: open Zellij subagents in the parent OpenCode tab
{
"multiplexer": {
"type": "zellij",
"zellij_pane_mode": "current-tab"
}
}
In current-tab mode, panes are targeted to the tab that contains the parent
OpenCode pane, even if another Zellij tab is focused when a subagent starts.
If the parent pane cannot be resolved, it falls back to the currently focused
tab.
Older configs still work:
{
"tmux": {
"enabled": true,
"layout": "main-vertical",
"main_pane_size": 60
}
}
This is converted automatically to multiplexer.type: "tmux".
Tmux supports full layout control and main pane sizing. Zellij and Herdr map
only the main-* layout settings to pane creation directions; exact
main_pane_size rebalancing is tmux-only.
| Layout | Description |
|---|---|
main-vertical |
Your session on the left, agents stacked on the right |
main-horizontal |
Your session on top, agents stacked below |
tiled |
All panes in an equal-sized grid |
even-horizontal |
All panes side by side |
even-vertical |
All panes stacked vertically |
For Zellij:
| Layout | Zellij behavior |
|---|---|
main-vertical |
Opens new subagent panes to the right |
main-horizontal |
Opens new subagent panes down |
even-horizontal |
Uses Zellij's native pane placement |
even-vertical |
Uses Zellij's native pane placement |
tiled |
Uses Zellij's native pane placement |
For kitty:
| Layout | Kitty behavior |
|---|---|
main-vertical |
Uses tall layout (full-height main pane on left, side panes stacked on right) |
main-horizontal |
Uses fat layout (full-width main pane on top, side panes tiled below) |
even-horizontal |
Uses horizontal layout (all panes side-by-side, equal width) |
even-vertical |
Uses vertical layout (all panes stacked, equal height) |
tiled |
Uses grid layout (all panes in equal-sized grid) |
Note: kitty has no layout rebalancing API like tmux's
select-layout, and no per-window layout — so the multiplexer applies the mapped kitty layout (tall,fat,grid,horizontal, orvertical) as a global change to the active tab, overriding whatever layout you had there. Themain_pane_sizeconfig is ignored. The layout is only re-applied when it differs from the currently applied one (e.g., viaapplyLayout).
For Herdr:
| Layout | Herdr behavior |
|---|---|
main-vertical |
Parent OpenCode pane stays on the left. First subagent opens in a right-side pane; subsequent subagents stack vertically in that right column. Parent pane remains dominant. |
main-horizontal |
Each subagent splits below the parent (down). |
even-horizontal |
Each subagent splits to the right of the parent. |
even-vertical |
Each subagent splits below the parent. |
tiled |
Each subagent splits to the right of the parent. |
Note: Herdr has no layout rebalancing API like tmux's select-layout.
The main_pane_size config is ignored. The main-vertical layout approximates
tmux's behavior by tracking the first right-side pane and stacking later agents
vertically within it. If the agent-area pane is closed, the next spawn
re-creates it from the parent.
Note:
main_pane_sizeis ignored by herdr. All layouts split from the parent pane.
Example: wide-screen layout
{
"multiplexer": {
"type": "tmux",
"layout": "main-horizontal",
"main_pane_size": 50
}
}
Example: maximum parallel visibility
{
"multiplexer": {
"type": "tmux",
"layout": "tiled",
"main_pane_size": 50
}
}