Complete reference for all configuration files and options in oh-my-opencode-slim. For repository-specific configurations, custom agents, and prompt directory lookups, see the Project-local Customization Guide.
| File | Purpose |
|---|---|
~/.config/opencode/opencode.json |
OpenCode core settings (plugin registration, providers) |
~/.config/opencode/oh-my-opencode-slim.json |
Plugin settings - agents, multiplexer, MCPs, council |
~/.config/opencode/oh-my-opencode-slim.jsonc |
Same, but with JSONC (comments + trailing commas). Takes precedence over .json if both exist |
.opencode/oh-my-opencode-slim.json |
Project-local overrides (optional, higher precedence than user config) |
💡 JSONC recommended: Use the
.jsoncextension to add comments and trailing commas. If both.jsoncand.jsonexist,.jsonctakes precedence.
Set OPENCODE_CONFIG_DIR to use a custom user config directory instead of
~/.config/opencode; install and runtime config discovery both honor it.
Set OH_MY_OPENCODE_SLIM_DISABLE to 1, true, yes, or on to make
oh-my-opencode-slim return during startup without registering agents, tools,
MCPs, hooks, Companion, or the TUI sidebar. This is a temporary escape hatch:
OH_MY_OPENCODE_SLIM_DISABLE=1 opencode
If OmO-slim detects an invalid plugin config for the current project, the TUI sidebar shows a warning. Run oh-my-opencode-slim doctor from your project root for full diagnostics.
The TUI sidebar uses the compact layout by default. Set compactSidebar to
false in oh-my-opencode-slim.jsonc to use the expanded layout:
{
"compactSidebar": false
}
While an agent session reports busy or retry, the sidebar shows an
animated Braille indicator after that agent's name. The indicator disappears
after every active session for that agent becomes idle or is deleted.
Customize agent prompts without modifying source code. Create markdown files in ~/.config/opencode/oh-my-opencode-slim/:
| File | Effect |
|---|---|
{agent}.md |
Replaces the agent's default prompt entirely |
{agent}_append.md |
Appends custom instructions to the default prompt |
When a preset is active, the plugin checks preset directories before falling back to root directories. Both global user prompt directories and project-local prompt directories are searched. For the complete lookup precedence order, see Project-local Customization.
Example directory structure:
~/.config/opencode/oh-my-opencode-slim/
├── best/
│ ├── orchestrator.md # Preset-specific override (used when preset=best)
│ └── explorer_append.md
├── orchestrator.md # Fallback override
├── orchestrator_append.md
├── explorer.md
└── ...
Both {agent}.md and {agent}_append.md can coexist - the full replacement takes effect first, then the append. If neither exists, the built-in default prompt is used.
All config files support JSONC (JSON with Comments):
//)/* */)Example:
{
// Active preset
"preset": "openai",
/* Agent model mappings */
"presets": {
"openai": {
"oracle": { "model": "openai/gpt-5.6-sol" },
"explorer": { "model": "openai/gpt-5.6-luna" },
},
},
"multiplexer": {
"type": "tmux",
"layout": "main-vertical",
},
}
| Option | Type | Default | Description |
|---|---|---|---|
preset |
string | - | Active preset name (e.g. "openai", "best") |
stripOrchestratorModel |
boolean | false |
Preserve a runtime /model selection for the orchestrator after subagent dispatch by omitting its configured model from the SDK config. A selected preset's explicit orchestrator.model is retained. Without a runtime selection, this opt-in delegates the initial orchestrator choice to OpenCode's session default. |
Presets can also be switched at runtime without restarting using the /preset command. See Preset Switching for details.
presets |
object | - | Named preset configurations |
|---|---|---|---|
presets.<name>.<agent>.model |
string | - | Model ID in provider/model format |
presets.<name>.<agent>.temperature |
number | - | Optional temperature (0–2); when omitted, OpenCode chooses its default |
presets.<name>.<agent>.variant |
string | - | Reasoning effort: "low", "medium", "high", or "max" (provider-specific) |
presets.<name>.<agent>.displayName |
string | - | Custom user-facing alias for the agent (e.g. "advisor" for oracle) |
presets.<name>.<agent>.color |
string | - | Agent display color as #RRGGBB or a theme color: primary, secondary, accent, success, warning, error, or info |
presets.<name>.<agent>.skills |
string[] | - | Skills the agent can use ("*", "!item", explicit list) |
presets.<name>.<agent>.mcps |
string[] | - | MCPs the agent can use ("*", "!item", explicit list) |
presets.<name>.<agent>.options |
object | - | Provider-specific model options passed to the AI SDK (e.g., textVerbosity, thinking budget) |
agents.<customAgent>.model |
string|array | - | Required for custom agents inferred from unknown agents keys |
agents.<customAgent>.prompt |
string | - | Full execution prompt for a custom agent |
agents.<customAgent>.orchestratorPrompt |
string | - | Exact @agent block injected into the orchestrator prompt; must start with @<agent-name> |
agents.<agent>.permission |
object | string | - | Tool-level permission rules enforced by the SDK. See Agent Permissions |
agents.<agent>.displayName |
string | - | Custom user-facing alias for the agent in the active config |
agents.<agent>.color |
string | - | Agent display color as #RRGGBB or a theme color: primary, secondary, accent, success, warning, error, or info |
agents.<agent>.description |
string | generated | Description shown to OpenCode and the orchestrator; defaults to Custom subagent '<name>' for custom agents |
acpAgents.<name>.command |
string | - | Command for an external ACP-compatible agent; creates a wrapper subagent named <name> See ACP-connected agents. |
acpAgents.<name>.args |
string[] | [] |
Arguments for the ACP agent command See ACP-connected agents. |
acpAgents.<name>.env |
object | {} |
Extra environment variables for the ACP subprocess See ACP-connected agents. |
acpAgents.<name>.cwd |
string | session directory | Working directory override for this ACP subprocess; protocol paths should be absolute See ACP-connected agents. |
acpAgents.<name>.description |
string | - | Description shown to OpenCode and injected into the orchestrator routing prompt See ACP-connected agents. |
acpAgents.<name>.prompt |
string | generated wrapper prompt | Optional full prompt for the lightweight wrapper subagent See ACP-connected agents. |
acpAgents.<name>.orchestratorPrompt |
string | generated routing block | Optional exact routing block injected into the orchestrator prompt See ACP-connected agents. |
acpAgents.<name>.wrapperModel |
string | orchestrator default | Cheap OpenCode model used by the wrapper subagent that calls acp_run See ACP-connected agents. |
acpAgents.<name>.permissionMode |
string | ask |
How ACP permission requests are handled: ask, allow, or reject See ACP-connected agents. |
acpAgents.<name>.timeoutMs |
integer | 0 |
Timeout for a single ACP run in milliseconds. 0 disables the timeout so external agents can run indefinitely. Finite values can be up to 2147483647ms (~24.8 days) See ACP-connected agents. |
disabled_agents |
string[] | ["observer"] |
Agent names to disable globally. Set to [] to enable Observer; this is global, not per-preset See Custom Agents. |
image_routing |
"auto" | "direct" |
omitted (legacy conditional) | Optional. When omitted, resolves to "auto" if Observer is enabled, otherwise "direct". Explicit "auto" requires Observer enabled and saves image attachments to disk before nudging delegation to @observer. "direct": always pass images to the orchestrator. |
autoUpdate |
boolean | true |
Automatically install plugin updates in the background; set to false for notification-only mode |
multiplexer.type |
string | "none" |
Multiplexer mode: auto, tmux, zellij, herdr, cmux, kitty, or none See Multiplexer Integration. |
multiplexer.layout |
string | "main-vertical" |
Layout preset: main-vertical, main-horizontal, tiled, even-horizontal, even-vertical. Tmux applies full layouts; Zellij and Herdr map supported layouts to split directions; cmux maintains a right-hand agent column See Multiplexer Integration. |
multiplexer.main_pane_size |
number | 60 |
Main pane size as percentage (20–80) for tmux main layouts; ignored by Zellij, Herdr, and cmux See Multiplexer Integration. |
multiplexer.zellij_pane_mode |
string | "agent-tab" |
Zellij pane placement: agent-tab creates/reuses a dedicated opencode-agents tab; current-tab opens subagents as panes in the tab containing the parent OpenCode pane, falling back to the focused tab if the parent pane cannot be resolved See Multiplexer Integration. |
tmux.enabled |
boolean | false |
Legacy alias for multiplexer.type = "tmux" See Multiplexer Integration. |
tmux.layout |
string | "main-vertical" |
Legacy alias for multiplexer.layout See Multiplexer Integration. |
tmux.main_pane_size |
number | 60 |
Legacy alias for multiplexer.main_pane_size See Multiplexer Integration. |
backgroundJobs.maxSessionsPerAgent |
integer | 2 |
Maximum completed/reconciled reusable child sessions per specialist type in the current orchestrator session (1–10) See Background Job Management. |
backgroundJobs.maxContextLines |
integer | 50000 |
Maximum total context lines (sum of all tracked file line counts) for a session to remain reusable. Sessions exceeding this threshold are evicted from the reusable pool on completion See Background Job Management. |
backgroundJobs.readContextMinLines |
integer | 10 |
Minimum number of lines read from a file before it appears in reusable background-job context (0–1000) See Background Job Management. |
backgroundJobs.readContextMaxFiles |
integer | 8 |
Maximum number of recent read-context files shown per reusable child session (0–50) See Background Job Management. |
backgroundJobs.maxRetainedSnapshots |
integer | 20 |
Maximum board snapshots retained per checkpoint cache epoch (1–100). Adding a snapshot beyond the limit starts a new epoch with only the current snapshot, intentionally creating one cache miss See Background Job Management. |
backgroundJobs.strategy |
"latest" | "checkpoint-compatible" |
"latest" |
Board injection strategy. latest preserves the current strip-and-replace behavior; checkpoint-compatible appends only when the formatted board changes and uses backgroundJobs.maxRetainedSnapshots per cache epoch. Cache state resets on compaction/session boundaries and is lost on plugin restart See Background Job Management. |
backgroundJobs.orchestratorWake.enabled |
boolean | true |
When true, idle orchestrator sessions with incomplete todos may receive periodic internal wake prompts (default every 5 minutes of continuous parent idle). Requires host session APIs. See Background Orchestration See Background Job Management. |
backgroundJobs.orchestratorWake.intervalMs |
integer | 300000 |
Continuous parent-idle interval between wake evaluations (60000–2147483647 ms). 0 is invalid. See Background Orchestration See Background Job Management. |
backgroundJobs.orchestratorWake.mode |
string | "auto" |
Wake-condition source: "auto" uses todo-gating on OpenCode v1 and children-driven degraded mode on v2 hosts; "todo"/"children" pin one mode (explicit "todo" degrades to children where no todo API exists). See Background Orchestration. |
backgroundJobs.wallClockTimeoutMs |
integer | 0 |
Opt-in wall-clock supervisor. 0 disables it. Otherwise, only native task(..., background: true) child sessions are supervised; accepted values are 60000–2147483647 milliseconds See Background Job Management. |
backgroundJobs.abortGraceMs |
integer | 10000 |
Grace period after a wall-clock deadline for a terminal confirmation. Accepted values are 1000–60000 milliseconds; a hanging or failed abort does not extend this grace See Background Job Management. |
backgroundJobs.concurrency.defaultConcurrency |
integer | 0 |
Maximum concurrently running native background tasks. 0 means unlimited; accepted values are 0–1000 See Background Job Management. |
backgroundJobs.concurrency.providerConcurrency |
object | {} |
Per-provider caps keyed by provider ID. Each value must be 0–1000, where 0 means unlimited for that provider. The most specific configured cap wins: model > provider > default See Background Job Management. |
backgroundJobs.concurrency.modelConcurrency |
object | {} |
Per-model caps keyed by provider/model ID. Each value must be 0–1000, where 0 means unlimited for that model. The most specific configured cap wins: model > provider > default See Background Job Management. |
backgroundJobs.waitForUserGuard |
boolean | true |
When true, intercepts wait_for_user calls while background tasks are still running and the orchestrator wake scheduler is enabled, returning guidance to end the turn instead of blocking on manual input. See Background Job Management. |
disabled_mcps |
string[] | [] |
MCP server IDs to disable globally |
fallback.enabled |
boolean | true |
Enable Slim's foreground model-chain failover. It does not configure OpenCode provider/AI-SDK retries. |
fallback.maxRetries |
number | 3 |
Consecutive retryable 429 responses allowed for the same foreground model before Slim aborts or selects the next configured fallback model. It does not cap OpenCode provider retries or background subagent retries. |
fallback.initialRetryDelayMs |
number | 0 |
Delay in milliseconds before triggering the first fallback on a failover-worthy error. Gives intercepting plugins time to recover the current model before the fallback chain advances. 0 disables. |
fallback.retryDelayMs |
number | 500 |
Delay in milliseconds between consecutive fallback attempts after the initial trigger. 0 disables. |
council.presets |
object | - | Required if using council. Named councillor presets See Council configuration note. |
council.presets.<name>.<councillor>.model |
string | - | Councillor model See Council configuration note. |
council.presets.<name>.<councillor>.variant |
string | - | Councillor variant See Council configuration note. |
council.presets.<name>.<councillor>.prompt |
string | - | Optional role guidance for the councillor See Council configuration note. |
council.default_preset |
string | "default" |
Default preset when none is specified See Council configuration note. |
| — | — | — | Timeouts, execution mode, and retries are now handled by the orchestrator's council-mode prompt instructions; see src/agents/council.ts. |
interview.maxQuestions |
integer | 2 |
Max questions per interview round (1–10) See Interview configuration. |
interview.outputFolder |
string | "interview" |
Directory where interview markdown files are written relative to the project root; absolute paths and .. traversal are rejected See Interview configuration. |
interview.autoOpenBrowser |
boolean | true |
Automatically open the interview UI in your default browser during interactive runs; suppressed in tests and CI See Interview configuration. |
interview.port |
integer | 0 |
Interview server port (0–65535). 0 = OS-assigned random port (per-session mode). Any value > 0 enables dashboard mode See Interview configuration. |
interview.dashboard |
boolean | false |
Enable dashboard mode on the default port (43211). Setting port > 0 also enables dashboard mode. If both are set, port takes precedence See Interview configuration. |
companion.enabled |
boolean | false |
Enable/disable the floating window Rust companion See Desktop Companion App. |
companion.binaryPath |
string | - | Optional path to a custom companion binary to launch instead of the default install path See Desktop Companion App. |
companion.position |
string | "bottom-right" |
The initial corner position of the companion window: bottom-right, bottom-left, top-right, or top-left See Desktop Companion App. |
companion.size |
string | "medium" |
The default size preset of the companion window: small (80px), medium (120px), or large (160px) See Desktop Companion App. |
niri note:
companion-v0.1.3includes the fixed native companion release. To make it open as a bottom-right overlay, add a niri rule matching its stableapp-id/title (oh-my-opencode-slim-companion), for example:> window-rule { > match app-id=r"^oh-my-opencode-slim-companion$" > match title=r"^oh-my-opencode-slim-companion$" > open-floating true > open-focused false > default-floating-position x=16 y=16 relative-to="bottom-right" > } > ``` ### ACP-connected agents Use `acpAgents` to expose external Agent Client Protocol servers as optional OpenCode subagents. The plugin creates a lightweight wrapper agent for each entry. The wrapper calls the built-in `acp_run` tool, which starts the ACP process, creates a session, sends the task, and returns the streamed result. `command` is only the executable; put flags and subcommands in `args`. See **[ACP Agents](acp-agents.md)** for the dedicated setup guide, auth notes, and troubleshooting.jsonc { "acpAgents": {
"claude-research": { "command": "claude-code-acp", "args": [], "description": "Claude Code subscription agent for deep research", "wrapperModel": "openai/gpt-5.6-luna", "permissionMode": "ask", "timeoutMs": 300000 }, "gemini-acp": { "command": "gemini", "args": ["--experimental-acp"], "description": "Gemini CLI through ACP" }} }
> **Tip:** Use ACP to connect local agent CLIs. For example, `ollama` or `llama.cpp` > can be exposed as ACP agents by wrapping them in a lightweight ACP adapter. After restart, the orchestrator can delegate to `@claude-research` or `@gemini-acp`. Use safe names matching `^[a-z][a-z0-9_-]*$`; names cannot conflict with built-in or custom agents. `permissionMode` controls ACP permission requests, but the plugin still asks before launching the configured subprocess. ### Council configuration note - Councillor `model` and ACP `wrapperModel` values use `provider/model` references. The provider must be nonempty and cannot contain whitespace or `/`; the nonempty model remainder is retained verbatim and may contain spaces and nested `/` values, such as `opencode-omniroute-live/of/MiniMax M3`. - The **Council agent model** is configured like any other agent, for example in `presets.<name>.council.model`. - The **councillor models** are configured separately under `council.presets.<name>.<councillor>.model`. - `council.master` (exact key) has been removed; a deprecation warning is logged if a config still contains it. Other `council.master_*` variants (e.g., `council.master_timeout`, `council.master_fallback`) are silently dropped without warning — remove them manually.jsonc { "council": {
"default_preset": "balanced", "presets": { "balanced": { "alpha": { "model": "openai/gpt-5.6-sol", "variant": "high" }, "beta": { "model": "anthropic/claude-sonnet-4-5", "variant": "medium" } } }} }
### Manual Update Mode Set `autoUpdate` to `false` if you want update notifications without automatic `bun install` runs.jsonc { "autoUpdate": false }
With `autoUpdate` set to `false`, this becomes notification-only mode: you'll see that a new version is available, but the plugin won't install it automatically. Auto-update never crosses major versions. For example, a 1.x install can auto-update to a newer 1.x release, but it won't auto-install 2.x. When a newer major is available, the plugin shows a migration command instead. > Pinned plugin entries in `opencode.json` (for example > `"oh-my-opencode-slim@1.0.1"`) are the true version lock. Those stay pinned > regardless of `autoUpdate`. ### Background Job Management Background job management is enabled by default and does not need to be present in the starter config. Add `backgroundJobs` only if you want to tune how many completed/reconciled child-agent sessions are reusable, how much read context is shown, how board snapshots are injected, or to change the default-on orchestrator wake interval. For glossary definitions of background-job terms (board snapshot, checkpoint cache epoch, injection strategy, etc.), see [CONTEXT.md — Background Jobs](../CONTEXT.md#background-jobs). The wall-clock supervisor is separately opt-in and remains disabled unless `wallClockTimeoutMs` is set:jsonc { "backgroundJobs": {
"maxSessionsPerAgent": 3, "strategy": "checkpoint-compatible", "maxRetainedSnapshots": 10, "orchestratorWake": { "enabled": true, "intervalMs": 300000, "mode": "auto" }, "wallClockTimeoutMs": 900000, "abortGraceMs": 10000, "concurrency": { "defaultConcurrency": 2, "providerConcurrency": { "openai": 2 }, "modelConcurrency": { "openai/gpt-5.6-luna": 1 } }} }
`orchestratorWake` defaults to enabled with a 5-minute continuous-idle interval and `"auto"` mode (todo-gated on v1 hosts, children-driven on v2). Set `enabled: false` to keep idle reconciliation and background-job orchestration without periodic wake prompts. See the [Background Orchestration](background-orchestration.md) guide for the concept, defaults, and examples. `concurrency` limits only native background tasks with `task(..., background: true)`. Foreground tasks are unchanged. A task waits for admission before OpenCode creates its child session, so queued work does not consume a provider request. `0` means unlimited. Only the most specific configured cap applies to a task, matching the reference implementation's priority: a model cap for the task's model wins over a provider cap for its provider, which wins over the default cap. For example, with `defaultConcurrency: 2`, `providerConcurrency: {"openai": 5}` and `modelConcurrency: {"openai/gpt-4o": 10}`, up to 10 `openai/gpt-4o` tasks run concurrently. Queued tasks are admitted in order among tasks whose resolved cap has capacity. Terminal completion, cancellation, failure, session deletion, and plugin disposal release the slot. A task that switches models mid-flight (e.g. foreground model fallback) moves its accounting to the new model. The scheduler is process-scoped: when the plugin re-inits on a config update, running slots and queued tickets survive, so admission state is not reset mid-run. Two behaviors to know about when concurrency is enabled: - Sessions that are themselves managed tasks (a background subagent orchestrating its own nested `task(..., background: true)` calls) are exempt from admission. They already hold a slot while running, so waiting for a second one would self-deadlock once the queue saturates. - Admission has no timeout of its own. A running task that never reaches a terminal state keeps its slot forever, and queued tasks as well as the orchestrator's `task` calls block behind it. When you enable `concurrency`, pair it with an opt-in `wallClockTimeoutMs` so stalled tasks are eventually forced to a terminal state and release their slots. Configurations that still use the removed `backgroundJobs.continueOnIdle` key emit a deprecation warning and migrate its boolean value to `orchestratorWake.enabled`. An `orchestratorWake.enabled` value in the same config file takes precedence; replace the legacy key with that setting. `wallClockTimeoutMs` is a hard deadline that only supervises explicitly background native task calls; foreground calls or calls with `background` omitted are not supervised. It is independent from OpenCode's external task-wait timeout, and a wall-clock timeout cannot be recovered by reusing the running session. `fallback.maxRetries` is unrelated to the wall-clock supervisor and to OpenCode's provider retry policy. A value of `0` disables Slim's foreground 429 failover budget; it does not prevent OpenCode from retrying a provider request in a child session. ### Agent Display Names Use `displayName` to give an agent a user-facing alias while keeping the internal agent name unchanged.jsonc { "agents": {
"oracle": { "displayName": "advisor" }, "explorer": { "displayName": "researcher" }} }
With this config, users can refer to `@advisor` and `@researcher`, while the plugin still routes them to `oracle` and `explorer` internally. Notes: - `displayName` works in both top-level `agents` overrides and inside `presets` - `@` prefixes and surrounding whitespace are normalized automatically - Display names must be unique - Display names cannot conflict with internal agent names like `oracle` or `explorer` ### Independent agent model inheritance By default, the `fixer` agent inherits the `librarian` model when no fixer model is configured. To decouple agents, set `inheritModelFrom` on the agent that should follow the current session or the configured orchestrator model:jsonc { "agents": {
"librarian": { "model": "ollama/qwen3.8:27B" }, "fixer": { "inheritModelFrom": "session" }} }
Supported values are: - `session`: omit the agent model so OpenCode uses the current session model - `orchestrator`: use the orchestrator model resolved during configuration; if none is configured, fall back to the current session model `orchestrator` means the model resolved during plugin configuration. It does not dynamically follow a later foreground fallback to another model. Runtime fallback behavior is independent of `inheritModelFrom`. Model selection follows these rules: - If the same effective agent override contains both `model` and `inheritModelFrom`, the explicit `model` wins. - If `model` is omitted, `inheritModelFrom` is an explicit higher-layer directive: it clears a lower-layer `model` value, including a model supplied by the host agent configuration, and resolves the requested source. - If neither field is present, the existing model precedence and the historical fixer-to-librarian fallback remain unchanged. The setting works in both root `agents` overrides and preset agent overrides. ### Agent Colors Built-in agents ship without a default `color`, so the OpenCode TUI assigns each one a distinct color from its own theme-aware palette. Set `color` explicitly if you want a fixed color for a built-in or custom agent, using a six-digit hex value or an OpenCode theme color:jsonc { "agents": {
"oracle": { "color": "#FF5733" }, "reviewer": { "model": "openai/gpt-5.6", "color": "info" }} }
Theme colors adapt to the active OpenCode theme. Dynamic councillors inherit the configured `council` color unless `agents.councillor.color` overrides it. `color` works in top-level `agents` overrides and inside `presets`. ### Local marketplace packages Preset `marketplace` activation selects installed local packages for that preset. Installation and activation are separate; changes apply after the next OpenCode session or reload. See **[Local Marketplace](marketplace.md)** for the CLI, in-session `marketplace` tool, and status diagnostics.jsonc { "preset": "work", "presets": {
"work": { "marketplace": { "agents": ["community/example"], "profiles": { "oracle": "community/oracle-profile" } } }} }
### Per-preset agent configuration To get per-preset behavior for any agent, built-in (`council`, `oracle`, `explorer`, `librarian`, `fixer`, `designer`, `observer`) or custom, define the agent override inside each preset block, not in root `agents`.jsonc { "presets": {
"balanced": { "council": { "model": ["opencode/mimo-v2.5-free", "opencode-go/minimax-m3", "opencode/minimax-m3"] }, "oracle": { "model": "opencode/big-pickle", "variant": "high" }, "skeptic": { "model": ["opencode/big-pickle", "opencode-go/qwen3.7-plus"], "variant": "max" } }, "nvidia-free": { "council": { "model": ["nvidia/z-ai/glm-5.2", "nvidia/moonshotai/kimi-k2.6"] }, "oracle": { "model": "nvidia/deepseek-ai/deepseek-v4-pro", "variant": "high" }, "skeptic": { "model": ["nvidia/deepseek-ai/deepseek-v4-pro", "nvidia/mistralai/mistral-large-3-675b-instruct-2512"], "variant": "max" } }} }
#### Root `agents` wins the merge (config-file presets) At startup, config-file presets merge into `config.agents` via `deepMerge(preset, config.agents)` at `src/config/loader.ts:365`. The second argument wins for conflicting scalars, so root `agents` overrides the preset. A root entry for an agent makes the config-file preset value for that agent ignored — the agent becomes global instead of per-preset. Root `agents` is the escape hatch for values that should never vary by preset. **Runtime presets reverse this.** When a preset is activated at runtime via the `/preset` command, the merge at `src/index.ts:227` is `deepMerge(config.agents, presetAgents)` — the runtime preset is the override and wins. Root `agents` only guarantees precedence for config-file presets resolved at startup. #### Sharing a prompt across presets (custom agents) A custom agent with a long prompt does not need the prompt duplicated into every preset block. Put the prompt in a file and define the agent in each preset with only `model` (and `variant` if needed): 1. Create `<projectDir>/.opencode/oh-my-opencode-slim/<agentName>.md` with the shared prompt. 2. In each preset block, define the agent with only the model fields (no `prompt`):jsonc { "presets": {
"balanced": { "skeptic": { "model": ["opencode/big-pickle", "opencode-go/qwen3.7-plus"], "variant": "max" } }, "nvidia-free": { "skeptic": { "model": ["nvidia/deepseek-ai/deepseek-v4-pro", "nvidia/mistralai/mistral-large-3-675b-instruct-2512"], "variant": "max" } }} }
`loadAgentPrompt` (`src/config/loader.ts:418`) is preset-aware and reads `<agentName>.md` from the `oh-my-opencode-slim/` prompts directory. Lookup order: 1. `<projectDir>/.opencode/oh-my-opencode-slim/<preset>/<agentName>.md` (project, preset-specific) 2. `<projectDir>/.opencode/oh-my-opencode-slim/<agentName>.md` (project, preset-agnostic) 3. `~/.config/opencode/oh-my-opencode-slim/<preset>/<agentName>.md` (user, preset-specific) 4. `~/.config/opencode/oh-my-opencode-slim/<agentName>.md` (user, preset-agnostic) A preset block without `prompt` falls back to the file prompt (if one exists), not to a root `agents.<name>.prompt`. The project-level paths (1 and 2) work universally and are the recommended location for shared prompts. User-level paths (3 and 4) can collide with a plugin install symlink if `~/.config/opencode/oh-my-opencode-slim/` is symlinked to the plugin source. > **⚠️ Known limitation (#899):** Prompt files take precedence over inline > prompts everywhere — not just in presets, but also in root `agents`. > If you set an inline `prompt` in a preset or in root `agents` and a > prompt file exists for that agent, the inline prompt is silently dropped > in favor of the file. Until #899 is fixed, the file-based shared prompt > pattern above is the safe path: keep the prompt in the file, and put > only `model`/`variant` in the config. Do not mix an inline `prompt` > with a prompt file for the same agent. ### Custom Agents Unknown keys under `agents` are treated as custom subagents. A custom agent needs its own `model`, a normal `prompt`, and optionally an `orchestratorPrompt` that teaches the orchestrator exactly when to delegate to it.jsonc { "agents": {
"janitor": { "model": "github-copilot/gpt-5.6", "prompt": "You are Janitor. Audit codebase entropy, dead code, docs drift, naming inconsistencies, and unnecessary complexity. Prefer analysis and plans over direct edits.", "orchestratorPrompt": "@janitor\n- Role: Maintenance specialist for codebase cleanup and entropy reduction\n- **Delegate when:** after large refactors • cleanup/technical-debt review • dead code or docs drift is suspected\n- **Don't delegate when:** feature implementation • urgent debugging • UI/UX work" }} }
Notes: - Custom agent names must be safe identifiers such as `janitor` or `security-reviewer` - Custom agents without a `model` are skipped with a warning - Disabled custom agents are not registered or injected into the orchestrator prompt > **Tip:** Keep `orchestratorPrompt` concise — the orchestrator reads it every turn. > Include: when to delegate, when NOT to delegate, and the agent's role in one paragraph. ### Agent Permissions The `permission` field provides deterministic, tool-level permission restrictions on custom agents, built-in agent overrides, and presets. Unlike prompt instructions ("do not edit files"), these rules are enforced by the OpenCode SDK at the tool-call level. The field accepts either: 1. **Shorthand string** — `"ask"`, `"allow"`, or `"deny"` applied to all tools 2. **Object** — keys are tool names, values are `"ask" | "allow" | "deny"` or (for rule keys) a pattern-to-action map **Example: read-only `planner` agent:**jsonc { "agents": {
"planner": { "model": "openai/gpt-5.5", "variant": "high", "skills": [], "mcps": ["context7", "gh_grep"], "permission": { "edit": "deny", "bash": { "*": "ask", "git status*": "allow", "git diff*": "allow", "grep *": "allow" }, "webfetch": "allow", "websearch": "allow", // opencode's built-in websearch tool, not a plugin MCP "task": "deny" }, "prompt": "You are Planner. Create implementation plans only. Do not implement code." }} }
**Example: `security-reviewer` agent:**jsonc { "agents": {
"security-reviewer": { "model": "anthropic/claude-sonnet-4-5", "permission": { "edit": "deny", "bash": "deny", "webfetch": "allow" }, "prompt": "You are a security reviewer. Inspect code and report findings. Do not patch anything." }} }
#### Permission keys | Key | Value type | Description | |-----|------------|-------------| | `read` | string or object | File reading | | `edit` | string or object | File editing | | `glob` | string or object | File pattern matching | | `grep` | string or object | Content search | | `list` | string or object | Directory listing | | `bash` | string or object | Shell command execution | | `task` | string or object | Subagent task delegation | | `external_directory` | string or object | Access to directories outside the workspace | | `lsp` | string or object | Language server protocol operations | | `skill` | string or object | Skill execution | | `todowrite` | string only | Todo list writing | | `question` | string only | Asking the user questions | | `webfetch` | string only | Web content fetching | | `websearch` | string only | Web search | | `codesearch` | string only | Code search | | `doom_loop` | string only | Doom loop prevention | Keys marked "string or object" accept pattern-based rules (e.g. `bash: { "git status*": "allow", "*": "ask" }`). Keys marked "string only" accept a single `"ask"`, `"allow"`, or `"deny"` value. Unknown tool names (including MCP-derived keys) pass through without error. #### Merge semantics When a user supplies `permission` and also uses the `skills` or `mcps` arrays on the same agent, the plugin merges them: 1. **User-supplied `permission` is the base layer.** 2. **Plugin-generated rules from the `skills` array override `permission.skill`** — the `skills` array is authoritative for skill gating. 3. **Plugin-generated rules from the `mcps` array set `permission.<mcp>_*` keys** — the `mcps` array is authoritative for MCP gating. 4. **User-supplied keys for standard tools** (`edit`, `bash`, `webfetch`, `task`, etc.) survive the merge untouched. Use the `skills`/`mcps` arrays for skill and MCP gating. Use `permission` for everything else (file access, bash, web, task delegation). ### Multiplexer The multiplexer hosts child agent sessions in terminal panes. See [Multiplexer Integration](multiplexer-integration.md) for backend setup, layout configuration, and troubleshooting. ### Desktop Companion App The desktop companion app provides a visual status overlay showing running and active agents. For quick installation instructions, binary paths, config defaults, and release information, see the full **[Desktop Companion Guide](companion.md)**. Once installed, configure it in your `oh-my-opencode-slim` settings:jsonc { "companion": {
"enabled": true, "position": "bottom-right", // optional: bottom-right, bottom-left, top-right, top-left "size": "medium" // optional: small, medium, large} } ```