diff --git a/docs/settings.md b/docs/settings.md index c2d7958c4..4ae1dd652 100644 --- a/docs/settings.md +++ b/docs/settings.md @@ -1,85 +1,129 @@ # Settings -`omp` resolves settings from persistent global config, project-local config, one-shot CLI overlays, and in-memory runtime overrides. Use project settings when one repository needs a different provider set, model role, tool policy, memory backend, or UI behavior than your global defaults. +`omp` resolves settings from built-in defaults, a persistent global config file, optional project-local config, one-shot CLI overlays, and in-memory runtime overrides. Reach for project settings when one repository needs a different provider set, model role, tool policy, memory backend, or UI behavior than your global defaults — without touching your machine-wide configuration. -For model/provider credentials and custom `models.yml` providers, see [Providers](./providers.md). For instruction files discovered into the agent context, see [Context files](./context-files.md). +Settings are stored as plain YAML mappings. Every key, its type, default, and enum values come from the settings schema, and you can inspect or change any of them with `omp config` or the interactive `/settings` panel. + +- For model/provider credentials, `.env` files, and the env-var table that resolves API keys, see [Providers](./providers.md). +- For custom model definitions in `models.yml`, see [Models](./models.md). +- For instruction files discovered into the agent context (`AGENTS.md`, `.omp/`, etc.), see [Context files](./context-files.md). +- For the full catalog of environment variables, see [Environment variables](./environment-variables.md). ## Where settings live | Scope | Path | Read behavior | Write behavior | |---|---|---|---| -| Global | `~/.omp/agent/config.yml` | Main persistent settings file. `PI_CODING_AGENT_DIR` changes the `~/.omp/agent` base directory. | `/settings`, `omp config set`, and runtime settings changes write here. | -| Global legacy | `~/.omp/agent/settings.json` | Migrated to `config.yml` when `config.yml` is missing. | Not written after migration. | -| Project | `/.omp/config.yml` | Read when the process current working directory contains a non-empty `.omp/` directory. | Read-only through discovery; edit the file by hand. | -| Project legacy | `/.omp/settings.json` | Still read before project `config.yml`. | Not written by settings commands. | -| CLI overlay | Any YAML file passed with `--config ` | Loaded after global and project settings for that one process. Repeat `--config` to layer files. | Never persisted. | +| Global | `~/.omp/agent/config.yml` | The main persistent settings file. Always loaded. | `/settings`, `omp config set`, and `omp config reset` write here. | +| Global legacy | `~/.omp/agent/settings.json` | Migrated into `config.yml` once, only when `config.yml` does not yet exist. | Not written after migration; the original is renamed to `settings.json.bak`. | +| Project | `/.omp/config.yml` (plus `.omp/settings.json`) | Loaded when the process working directory has a non-empty `.omp/`. | Read-only from settings commands; edit the file by hand. | +| Project legacy | `/.omp/settings.json` | Still read; project `config.yml` is merged on top of it. | Not written by settings commands. | +| CLI overlay | Any file passed with `--config ` | Loaded after global and project settings, for that one process. Repeatable. | Never persisted. | +| Runtime overrides | In-memory only | Set by dedicated CLI flags (`--model`, `--approval-mode`, …) and feature env vars. | Never persisted. | -Native project settings are intentionally local to the process cwd's `.omp/` directory. Unlike context files, native settings discovery does not walk ancestor directories looking for the nearest `.omp/`. Other discovery providers can also contribute project-level settings from their own files (`.claude/settings.json`, `.gemini/settings.json`, `.codex/config.toml`, `.cursor/settings.json`, `opencode.json`); those sources are read-only from `omp` settings commands and can be disabled by provider ID. +`PI_CODING_AGENT_DIR` relocates the `~/.omp/agent` base directory. When it is set, the global `config.yml`, the auth store (`agent.db`), and everything else under the agent directory move with it. Use `omp config path` to print the active agent directory. -### Legacy migration +Native project settings are intentionally scoped to the process working directory's `.omp/` folder — settings discovery does **not** walk ancestor directories looking for the nearest `.omp/`. Other discovery providers (Claude, Codex, Gemini, Cursor, OpenCode) can also contribute project-level settings from their own files; those are read-only from `omp` settings commands and can be turned off by provider id (see [Provider and source disabling](#provider-and-source-disabling)). -When `config.yml` is missing, startup attempts to migrate the matching legacy JSON file: +## Config file formats -- Global: `~/.omp/agent/settings.json` -> `~/.omp/agent/config.yml` -- Project/native discovery: `/.omp/settings.json` remains readable, but project settings are not rewritten by `omp` -- Generic `ConfigFile` users also support `.json` -> `.yml` migration when the `.yml` file is absent +The global `config.yml` is always YAML. The generic config loader used for other files (for example `models.yml`) accepts `.yml`, `.yaml`, `.json`, and `.jsonc`: -If both the YAML and legacy JSON files exist at the same scope, YAML wins for settings loaded from that scope. For native project settings, `settings.json` is read first and `config.yml` is read second, so project `config.yml` overrides duplicate keys from project `settings.json`. +- When a `.yml`/`.yaml` path is requested and only a sibling `.json` exists, it is migrated to YAML automatically (idempotent, once per process). +- `.json` and `.jsonc` configs are read as-is, with no migration. +- A file whose top level is not a mapping (a bare array or scalar) is treated as empty for persistent settings, and is a hard error for `--config` overlays. ## Reading and writing settings -Interactive `/settings` and `omp config` operate on the merged effective settings, but persistent writes go to the global config file: +Use the interactive `/settings` panel inside a session, or the `omp config` command from a shell. Both operate on the merged effective settings, but every persistent write lands in the **global** file only. ```bash -omp config list -omp config get theme.dark +omp config list # all settings with current effective values +omp config list --json # same, machine-readable +omp config get theme.dark # one value +omp config get theme.dark --json omp config set compaction.enabled false -omp config set tools.approvalMode write -omp config set disabledProviders '["anthropic","openai"]' -omp config reset compaction.enabled -omp config path +omp config set defaultThinkingLevel medium +omp config reset steeringMode # restore a key to its schema default +omp config path # print the active agent directory ``` -Value parsing is schema-driven: +### Subcommands -- booleans accept `true`, `false`, `yes`, `no`, `on`, `off`, `1`, `0` -- numbers parse as JavaScript numbers -- enums must match one of the implemented values -- arrays and records must be JSON on the command line -- strings are stored as provided +| Command | Effect | +|---|---| +| `omp config list` | Print every setting grouped by tab, with its current value and type. `--json` emits an object keyed by setting path with `{ value, type, description }`. | +| `omp config get ` | Print the effective value of one key. Unknown keys exit non-zero. `--json` emits `{ key, value, type, description }`. | +| `omp config set ` | Parse `` against the key's schema type and write it to the global `config.yml`. | +| `omp config reset ` | Write the key's schema **default** back to the global config (this persists the default, it does not delete the key). | +| `omp config path` | Print the active agent directory (honors `PI_CODING_AGENT_DIR`). | -`omp config set` and `/settings` do not write into `/.omp/config.yml`. To make a project-local override, create or edit that file directly. +`omp config` with no subcommand, or `--help`, prints the help and lists settings. The `--json` flag is accepted by `list`, `get`, `set`, and `reset`. + +### Value parsing + +`omp config set` parses the value string according to the target key's schema type. The string is trimmed first. + +| Type | Accepted input | Notes | +|---|---|---| +| boolean | `true`, `false`, `yes`, `no`, `on`, `off`, `1`, `0` | Case-insensitive. Anything else is rejected. | +| number | Any finite JavaScript number | `Infinity`/`NaN` are rejected. | +| enum | One of the key's allowed values | Must match exactly; the error lists the valid values. | +| array | A JSON array | e.g. `'["anthropic","openai"]'`. Must parse and be an array. | +| record | A JSON object | e.g. `'{"bash":"prompt"}'`. Must parse and be a non-array object. | +| string | Stored as given (trimmed) | Multi-word values are joined with spaces. | + +Keys must match a real schema path exactly. There is no shorthand — set `theme.dark`, not `theme`. + +### Where writes go + +`omp config set`, `omp config reset`, `/settings`, and any runtime settings change all write to the global `config.yml` under the active agent directory. They never write to `/.omp/config.yml`. To create a project-local override, edit that file directly (see [Project-local config](#project-local-config)). Saves are debounced and re-read the file under a lock, so external edits made while a session is open are preserved. ## Precedence -Effective precedence is: +From lowest to highest priority, the effective value of a setting is built as: ```text -built-in defaults <- global config <- project config <- CLI overlays <- runtime overrides +built-in defaults <- global config <- project config <- CLI overlays <- runtime overrides ``` -From highest to lowest priority: +From highest to lowest: -1. Runtime overrides and dedicated CLI flags (`--model`, `--smol`, `--slow`, `--plan`, `--approval-mode`, `--auto-approve`/`--yolo`, `--hide-thinking`, `--no-pty`, `--api-key`, protocol-mode defaults) -2. CLI config overlays (`--config `; later overlay files override earlier overlay files) -3. Project settings (`/.omp/settings.json`, then `/.omp/config.yml`) -4. Global settings (`~/.omp/agent/config.yml`) -5. Built-in defaults from the settings schema +1. **Runtime overrides** — dedicated CLI flags and feature env vars applied in memory for the current process: `--model`, `--smol`, `--slow`, `--plan`, `--approval-mode`, `--auto-approve`/`--yolo`, `--hide-thinking`, `--no-pty`, `--api-key`, and protocol-mode defaults. Never persisted. +2. **CLI config overlays** — each `--config `; later overlay files override earlier ones. +3. **Project settings** — `/.omp/settings.json` then `/.omp/config.yml` (and contributions from other discovery providers at project level). +4. **Global settings** — `~/.omp/agent/config.yml`. +5. **Built-in defaults** — from the settings schema. -Environment variables are not a single settings layer. They are read by the feature that owns the value, often as an override or fallback: +A key that is unset at every layer resolves to its schema default at read time. -- `PI_SMOL_MODEL`, `PI_SLOW_MODEL`, and `PI_PLAN_MODEL` override `modelRoles.smol`, `modelRoles.slow`, and `modelRoles.plan` for the current process. -- `PI_NO_PTY=1` disables PTY-backed bash execution; `--no-pty` sets it for the current process. -- `PI_PY` and `PI_JS` override `eval.py` and `eval.js`. -- `PI_TINY_DEVICE` and `PI_TINY_DTYPE` override `providers.tinyModelDevice` and `providers.tinyModelDtype`. -- `OMP_AUTH_BROKER_URL` and `OMP_AUTH_BROKER_TOKEN` override `auth.broker.url` and `auth.broker.token`. -- Provider API keys come from stored auth, OAuth, `models.yml`, environment variables, and `.env` files as described in [Providers](./providers.md). +### Environment overrides -Environment-derived values are not written back to `config.yml`. +Environment variables are **not** a single settings layer. Each is read by the feature that owns the value, usually as a per-machine override or fallback, and is never written back to `config.yml`. The ones that map directly onto a setting: + +| Env var | Overrides setting | Notes | +|---|---|---| +| `PI_SMOL_MODEL` | `modelRoles.smol` | Also exposed as `--smol`. | +| `PI_SLOW_MODEL` | `modelRoles.slow` | Also exposed as `--slow`. | +| `PI_PLAN_MODEL` | `modelRoles.plan` | Also exposed as `--plan`. | +| `PI_NO_PTY=1` | (disables PTY bash) | Equivalent to `--no-pty` for the process. | +| `PI_PY` | `eval.py` | `PI_PY=0` disables the Python eval backend. | +| `PI_JS` | `eval.js` | `PI_JS=0` disables the JavaScript eval backend. | +| `PI_TINY_DEVICE` | `providers.tinyModelDevice` | ONNX execution provider for local tiny models. | +| `PI_TINY_DTYPE` | `providers.tinyModelDtype` | ONNX precision for local tiny models. | +| `OMP_AUTH_BROKER_URL` | `auth.broker.url` | Env value takes precedence over config. | +| `OMP_AUTH_BROKER_TOKEN` | `auth.broker.token` | Env value takes precedence over config. | +| `PI_CODING_AGENT_DIR` | (relocates agent dir) | Moves `config.yml`, `agent.db`, and the whole agent base. | + +Provider API keys are resolved separately (stored auth, OAuth, `models.yml`, environment, and `.env` files); see [Providers](./providers.md) and the full [Environment variables](./environment-variables.md) reference. ## Merge rules -Settings files are YAML mappings. Use nested YAML objects for setting paths that contain dots: +Layers are combined with a deep merge: + +- **Objects are deep-merged** — keys present only in a lower layer are kept; keys present in a higher layer override. +- **Scalars and arrays are replaced wholesale** by the higher-precedence layer. A higher layer's array does not append to a lower layer's array. + +Use nested YAML mappings for dotted setting paths: ```yaml theme: @@ -90,9 +134,10 @@ tools: approvalMode: write approval: bash: prompt + read: allow ``` -Objects are deep-merged. Scalars and arrays are replaced by the higher-precedence layer. +### Worked example: global vs. project ```yaml # ~/.omp/agent/config.yml @@ -106,7 +151,7 @@ disabledProviders: - openai - gemini -# /.omp/config.yml +# /.omp/config.yml tools: approval: bash: allow @@ -114,21 +159,21 @@ disabledProviders: - groq ``` -Effective result in that project: +Effective settings inside ``: ```yaml tools: - approvalMode: write # kept from global + approvalMode: write # kept from global (object deep-merge) approval: bash: allow # overridden by project read: allow # kept from global disabledProviders: - - groq # project array replaces global array + - groq # project array REPLACES the global array ``` -Array replacement is important: a project `disabledProviders` array does not append to the global array. It becomes the complete array for that project layer. +Array replacement is the most common surprise: the project's `disabledProviders` does not extend the global list — it becomes the entire list for that project. The same applies to `enabledModels`, `cycleOrder`, `extensions`, and every other array-typed setting. -## Project-local config examples +## Project-local config Create `/.omp/config.yml` when a repository needs its own settings: @@ -152,7 +197,7 @@ theme: dark: titanium ``` -Keep secrets out of committed project config unless your repository policy allows them. Prefer environment variables, stored auth, an auth broker, or an untracked local overlay for credentials. +Keep secrets out of committed project config unless your repository policy allows it. Prefer environment variables, stored auth, an auth broker, or an untracked `--config` overlay for credentials. ### One-shot overlays @@ -163,21 +208,21 @@ omp --config ./local/ci-settings.yml "check this failure" omp --config ./base.yml --config ./experiment.yml "try this model" ``` -Overlay paths are resolved relative to the process cwd. Each overlay must parse as a YAML mapping; missing or malformed overlay files are hard errors. +Overlay paths are resolved relative to the process working directory (and `~` is expanded). Each overlay must parse as a YAML mapping; a missing file, invalid YAML, or a top-level array/scalar is a hard error — it does **not** silently fall back to lower-precedence settings. ## Path-scoped arrays -`enabledModels` and `disabledProviders` can contain string entries and path-scoped entries: +Two array settings — `enabledModels` and `disabledProviders` — accept path-scoped entries in addition to bare strings, so a single global config can behave differently per directory: ```yaml enabledModels: - - claude-sonnet-4-5 + - claude-sonnet-4-5 # applies everywhere - path: ~/work/high-context models: - anthropic/claude-opus-4-5 disabledProviders: - - ollama + - ollama # applies everywhere - paths: - ~/projects/sensitive - ~/clients/acme @@ -186,45 +231,29 @@ disabledProviders: - openai ``` -String entries apply everywhere. Scoped entries apply when the current working directory is exactly the configured path or is under it. `~` expands to your home directory, and relative paths are resolved before matching. +Bare string entries apply everywhere. A scoped entry applies when the current working directory **is** the configured path or is **under** it. `~` expands to your home directory and relative paths are resolved before matching. -Accepted path keys: +Accepted **path** keys (any of them, combined): `path`, `paths`, `pathPrefix`, `pathPrefixes`. -- `path` -- `paths` -- `pathPrefix` -- `pathPrefixes` +Accepted **value** keys: -Accepted value keys: +- `models` (for `enabledModels`) or `providers` (for `disabledProviders`) +- `values` or `items` (for either setting) -- `models` for `enabledModels` -- `providers` for `disabledProviders` -- `values` or `items` for either setting +Only string values are kept; malformed scoped entries are ignored. Path scoping is resolved **after** the layer merge, so it reads the final effective array. -Only string values are kept. Malformed scoped entries are ignored. +## Provider and source disabling -## Provider disabling +`disabledProviders` is a single shared id namespace that gates two different subsystems, before any credential check: -`disabledProviders` uses a shared provider ID namespace. It can disable model providers and discovery providers: - -| Entry type | Examples | Effect | +| Entry kind | Example ids | Effect | |---|---|---| -| Model provider IDs | `anthropic`, `openai`, `gemini`, `groq`, `ollama`, `openrouter` | Prevent those model providers from becoming selectable, even if credentials are available. | -| Discovery provider IDs | `native`, `claude`, `codex`, `gemini`, `github`, `opencode`, `cursor` | Prevent that config source from contributing capabilities such as context files, MCP servers, commands, prompts, tools, hooks, extensions, or settings. | +| Model providers | `anthropic`, `openai`, `gemini`, `groq`, `ollama`, `openrouter` | Removes those backends from model selection, even when credentials are available. See [Providers](./providers.md). | +| Discovery sources | `native`, `claude`, `codex`, `gemini`, `github`, `opencode`, `cursor`, `agents-md` | Stops that source from contributing context files, MCP servers, commands, skills, hooks, tools, prompts, or settings. See [Context files](./context-files.md). | -Most provider-control use cases should list model provider IDs: +Most provider-control use cases list model provider ids. Disabling the `claude` discovery source is different from disabling the `anthropic` model provider — one stops Claude-format config discovery, the other stops the Anthropic model backend. -```yaml -disabledProviders: - - anthropic - - openai - - gemini - - groq -``` - -Use discovery provider IDs only when you want to turn off an entire source. For example, disabling `claude` prevents Claude-format discovery from contributing context/config items; it is different from disabling the `anthropic` model provider. See [Context files](./context-files.md) for the discovery-provider distinction. - -Because arrays replace, put the complete desired list in project config: +Because arrays replace rather than append, a project that sets `disabledProviders` must list the complete desired set: ```yaml # ~/.omp/agent/config.yml @@ -232,19 +261,21 @@ disabledProviders: - anthropic - openai -# /.omp/config.yml +# /.omp/config.yml — inside this repo ONLY groq is disabled disabledProviders: - groq ``` -Inside ``, only `groq` is disabled by settings; `anthropic` and `openai` are no longer disabled by the global array. +The default is an empty array (nothing disabled). For the two subsystems' provider ids and ordering, see [Providers](./providers.md) and [Context files](./context-files.md). -## Common settings +## Settings catalog -The authoritative setting names are the keys shown by `omp config list --json`. Useful groups include: +Every key below is defined in the settings schema; `omp config list` shows the full set with current values. Defaults and enum values are taken from the schema. Settings that accept an env or flag override are noted; those overrides are process-local and not persisted. ### Models +`modelRoles`, `modelTags`, and `cycleOrder` work together to define the models you can switch between. Role values may carry a thinking suffix (`:minimal`, `:low`, `:medium`, `:high`, `:xhigh`). + ```yaml modelRoles: default: anthropic/claude-sonnet-4-5 @@ -252,9 +283,6 @@ modelRoles: slow: anthropic/claude-opus-4-5:high vision: gemini/gemini-3-pro-preview plan: anthropic/claude-opus-4-5 - designer: anthropic/claude-sonnet-4-5 - commit: openai/gpt-4.1-mini - task: anthropic/claude-sonnet-4-5 cycleOrder: - smol @@ -264,68 +292,230 @@ cycleOrder: modelProviderOrder: - anthropic - openai + +enabledModels: + - claude-sonnet-4-5 ``` -Built-in role names are `default`, `smol`, `slow`, `vision`, `plan`, `designer`, `commit`, and `task`. Additional custom roles can be introduced by `modelRoles`, `modelTags`, or `cycleOrder`. Role values may include a thinking suffix such as `:minimal`, `:low`, `:medium`, `:high`, or `:xhigh`. +| Key | Type | Default | Notes | +|---|---|---|---| +| `modelRoles` | record | `{}` | Map of role name -> model id. Built-in roles: `default`, `smol`, `slow`, `vision`, `plan`, `designer`, `commit`, `task`. Per-role env/flags: `--model`/`--smol`/`--slow`/`--plan`. | +| `modelTags` | record | `{}` | Custom role/tag metadata; can introduce additional roles. | +| `modelProviderOrder` | array | `[]` | Preferred provider order when a model id is ambiguous. | +| `cycleOrder` | array | `["smol","default","slow"]` | Roles cycled by the model switcher. | +| `enabledModels` | array | `[]` | Allow-list of models; supports [path-scoped entries](#path-scoped-arrays). Empty means all available models. | +| `disabledProviders` | array | `[]` | Disabled model/discovery providers; supports path-scoped entries. See [above](#provider-and-source-disabling). | +| `includeModelInPrompt` | boolean | `true` | Include the active model name in the system prompt. | -Other model settings include: +See [Models](./models.md) for the `models.yml` schema and custom-provider definitions. + +### Thinking ```yaml -defaultThinkingLevel: high # minimal, low, medium, high, xhigh, or auto +defaultThinkingLevel: high hideThinkingBlock: false -temperature: -1 # -1 means provider default -topP: -1 -topK: -1 -minP: -1 -presencePenalty: -1 -repetitionPenalty: -1 -serviceTier: none # none, auto, default, flex, scale, priority, openai-only, claude-only +thinkingBudgets: + minimal: 1024 + low: 2048 + medium: 8192 + high: 16384 + xhigh: 32768 +``` + +| Key | Type | Default | Values | +|---|---|---|---| +| `defaultThinkingLevel` | enum | `high` | `minimal`, `low`, `medium`, `high`, `xhigh`, `auto`. Override per run with `--thinking`. | +| `hideThinkingBlock` | boolean | `false` | Hide thinking blocks in output. `--hide-thinking` sets it for the run (display only). | +| `thinkingBudgets.minimal` | number | `1024` | Token budget for the `minimal` level. | +| `thinkingBudgets.low` | number | `2048` | Token budget for `low`. | +| `thinkingBudgets.medium` | number | `8192` | Token budget for `medium`. | +| `thinkingBudgets.high` | number | `16384` | Token budget for `high`. | +| `thinkingBudgets.xhigh` | number | `32768` | Token budget for `xhigh`. | + +### Sampling + +A value of `-1` means "use the provider/model default" — `omp` does not send that parameter. + +| Key | Type | Default | Notes | +|---|---|---|---| +| `temperature` | number | `-1` | Sampling temperature. | +| `topP` | number | `-1` | Nucleus sampling. | +| `topK` | number | `-1` | Top-K sampling. | +| `minP` | number | `-1` | Minimum-probability cutoff. | +| `presencePenalty` | number | `-1` | Presence penalty. | +| `repetitionPenalty` | number | `-1` | Repetition penalty. | +| `serviceTier` | enum | `none` | `none`, `auto`, `default`, `flex`, `scale`, `priority`, `openai-only`, `claude-only`. | +| `personality` | enum | `default` | `default`, `friendly`, `pragmatic`, `none`. | + +### Retry and fallback + +```yaml retry: enabled: true maxRetries: 10 + baseDelayMs: 500 + maxDelayMs: 300000 modelFallback: true + fallbackRevertPolicy: cooldown-expiry ``` +| Key | Type | Default | Notes | +|---|---|---|---| +| `retry.enabled` | boolean | `true` | Retry transient provider errors. | +| `retry.maxRetries` | number | `10` | Max retries per request. | +| `retry.baseDelayMs` | number | `500` | Initial backoff. | +| `retry.maxDelayMs` | number | `300000` | Backoff ceiling (5 min). | +| `retry.modelFallback` | boolean | `true` | Fall back to another model when one is unavailable. | +| `retry.fallbackChains` | record | `{}` | Per-model fallback chains. | +| `retry.fallbackRevertPolicy` | enum | `cooldown-expiry` | `cooldown-expiry`, `never`. | + ### Tools and approvals ```yaml tools: - approvalMode: write # always-ask, write, yolo + approvalMode: yolo # default approval: - bash: prompt # allow, prompt, or deny + bash: prompt edit: allow - discoveryMode: auto # auto, off, mcp-only, all - essentialOverride: [] - maxTimeout: 0 # seconds; 0 means no limit + discoveryMode: auto + maxTimeout: 0 + intentTracing: true +``` +| Key | Type | Default | Notes | +|---|---|---|---| +| `tools.approvalMode` | enum | `yolo` | `always-ask` (auto-approve read-only), `write` (auto-approve read + workspace-write), `yolo` (auto-approve all tiers). `--approval-mode` and `--auto-approve`/`--yolo` override per run. | +| `tools.approval` | record | `{}` | Per-tool policy keyed by tool name; each value is `allow`, `deny`, or `prompt`. e.g. `omp config set tools.approval '{"bash":"prompt"}'`. | +| `tools.discoveryMode` | enum | `auto` | `auto`, `off`, `mcp-only`, `all`. Controls dynamic tool discovery. | +| `tools.essentialOverride` | array | `[]` | Tool names kept available even when tools are narrowed. | +| `tools.maxTimeout` | number | `0` | Max tool runtime in seconds; `0` = no cap. | +| `tools.intentTracing` | boolean | `true` | Record per-call intent strings. | +| `tools.outputMaxColumns` | number | `768` | Per-line byte cap for streaming output; `0` disables. | +| `tools.artifactSpillThreshold` | number | `50` | KB of tool output above which output spills to an artifact. | +| `tools.artifactHeadBytes` | number | `20` | KB of head kept inline on spill; `0` = tail-only. | +| `tools.artifactTailBytes` | number | `20` | KB of tail kept inline on spill. | +| `tools.artifactTailLines` | number | `500` | Max tail lines kept inline on spill. | + +Individual built-in tools are toggled by their own keys, e.g. `bash.enabled`, `eval.py`, `eval.js`, `find.enabled`, `search.enabled`, `fetch.enabled`, `browser.enabled`, `astEdit.enabled`, `astGrep.enabled`, `web_search.enabled`, `inspect_image.enabled`, `renderMermaid.enabled`. + +### Shell, eval, and LSP + +```yaml bash: enabled: true + stripTrailingHeadTail: true autoBackground: enabled: false + thresholdMs: 60000 eval: py: true js: true +python: + kernelMode: session # session, per-call + interpreter: "" + lsp: enabled: true lazy: true diagnosticsOnWrite: true + diagnosticsOnEdit: false + formatOnWrite: false ``` -`--approval-mode` and `--auto-approve`/`--yolo` are runtime overrides. They affect the current process and are not persisted. +| Key | Type | Default | Notes | +|---|---|---|---| +| `bash.enabled` | boolean | `true` | Enable the bash tool. | +| `bash.stripTrailingHeadTail` | boolean | `true` | Strip trailing head/tail noise from output. | +| `bash.autoBackground.enabled` | boolean | `false` | Auto-background long-running commands. | +| `bash.autoBackground.thresholdMs` | number | `60000` | Threshold before auto-backgrounding. | +| `eval.py` | boolean | `true` | Python eval backend. `PI_PY=0` disables for the process. | +| `eval.js` | boolean | `true` | JavaScript eval backend. `PI_JS=0` disables for the process. | +| `python.kernelMode` | enum | `session` | `session` (persistent kernel) or `per-call`. | +| `python.interpreter` | string | `""` | Path to a Python interpreter; empty = auto-detect. | +| `lsp.enabled` | boolean | `true` | Language-server integration. `--no-lsp` disables for the run. | +| `lsp.lazy` | boolean | `true` | Start servers on demand. | +| `lsp.diagnosticsOnWrite` | boolean | `true` | Run diagnostics after a write. | +| `lsp.diagnosticsOnEdit` | boolean | `false` | Run diagnostics after an edit. | +| `lsp.formatOnWrite` | boolean | `false` | Format files on write. | +| `lsp.diagnosticsDeduplicate` | boolean | `true` | Collapse duplicate diagnostics. | +| `shellPath` | string | _(unset)_ | Override the shell binary used by bash. | -### UI and terminal +### Files: editing and reading + +```yaml +edit: + mode: hashline # apply_patch, hashline, patch, replace + fuzzyMatch: true + fuzzyThreshold: 0.95 + blockAutoGenerated: true + +read: + defaultLimit: 300 + toolResultPreview: false + summarize: + enabled: true + prose: false +``` + +| Key | Type | Default | Notes | +|---|---|---|---| +| `edit.mode` | enum | `hashline` | `apply_patch`, `hashline`, `patch`, `replace`. | +| `edit.fuzzyMatch` | boolean | `true` | Allow fuzzy anchor matching. | +| `edit.fuzzyThreshold` | number | `0.95` | Similarity threshold for fuzzy matching. | +| `edit.blockAutoGenerated` | boolean | `true` | Refuse to edit generated/lockfile-like files. | +| `edit.streamingAbort` | boolean | `false` | Abort on streaming edit mismatch. | +| `read.defaultLimit` | number | `300` | Default line count for `read` without a selector. | +| `read.summarize.enabled` | boolean | `true` | Structural summaries for code reads. | +| `read.summarize.prose` | boolean | `false` | Summarize prose files too. | +| `read.toolResultPreview` | boolean | `false` | Inline preview of tool results. | +| `readHashLines` | boolean | `true` | Show hashline tags in read output. | +| `readLineNumbers` | boolean | `false` | Show plain line numbers. | + +### Context, compaction, and memory + +```yaml +contextPromotion: + enabled: true + +compaction: + enabled: true + strategy: context-full # context-full, handoff, shake, snapcompact, off + thresholdPercent: -1 # -1 = default reserve-based behavior + thresholdTokens: -1 # fixed token limit when > 0 + remoteEnabled: true + +memory: + backend: off # off, local, hindsight, mnemopi +``` + +| Key | Type | Default | Notes | +|---|---|---|---| +| `contextPromotion.enabled` | boolean | `true` | Promote relevant earlier context. | +| `compaction.enabled` | boolean | `true` | Automatic conversation compaction. | +| `compaction.strategy` | enum | `context-full` | `context-full`, `handoff`, `shake`, `snapcompact`, `off`. | +| `compaction.thresholdPercent` | number | `-1` | Percent-of-context trigger; `-1` = reserve-based default. | +| `compaction.thresholdTokens` | number | `-1` | Fixed token trigger when `> 0`. | +| `compaction.reserveTokens` | number | `16384` | Tokens reserved for the next turn. | +| `compaction.keepRecentTokens` | number | `20000` | Recent tokens always preserved. | +| `compaction.remoteEnabled` | boolean | `true` | Allow remote compaction service. | +| `compaction.autoContinue` | boolean | `true` | Continue automatically after compaction. | +| `memory.backend` | enum | `off` | `off`, `local`, `hindsight`, `mnemopi`. Each backend has its own `hindsight.*` / `mnemopi.*` / `memories.*` tuning keys. | + +`compaction` has additional tuning keys (idle compaction, supersede/drop heuristics) visible in `omp config list`. See [Compaction](./compaction.md) for the full strategy reference. + +### Appearance and terminal ```yaml theme: dark: titanium light: light -symbolPreset: unicode # unicode, nerd, ascii +symbolPreset: unicode # unicode, nerd, ascii colorBlindMode: false statusLine: - preset: default # default, minimal, compact, full, nerd, ascii, custom + preset: default # default, minimal, compact, full, nerd, ascii, custom separator: powerline-thin transparent: false showHookStatus: true @@ -336,42 +526,41 @@ images: autoResize: true blockImages: false tui: - hyperlinks: auto # off, auto, always + hyperlinks: auto # off, auto, always ``` -Use `statusLine.leftSegments`, `statusLine.rightSegments`, and `statusLine.segmentOptions` only with `statusLine.preset: custom`. +| Key | Type | Default | Values | +|---|---|---|---| +| `theme.dark` | string | `titanium` | Theme used on a dark terminal background. | +| `theme.light` | string | `light` | Theme used on a light terminal background. | +| `symbolPreset` | enum | `unicode` | `unicode`, `nerd`, `ascii`. | +| `colorBlindMode` | boolean | `false` | Use blue instead of green for diff additions. | +| `showHardwareCursor` | boolean | `true` | Show the terminal hardware cursor. | +| `statusLine.preset` | enum | `default` | `default`, `minimal`, `compact`, `full`, `nerd`, `ascii`, `custom`. | +| `statusLine.separator` | enum | `powerline-thin` | `powerline`, `powerline-thin`, `slash`, `pipe`, `block`, `none`, `ascii`. | +| `statusLine.sessionAccent` | boolean | `true` | Tint the editor border with the session color. | +| `statusLine.transparent` | boolean | `false` | Use the terminal background for the status line. | +| `statusLine.showHookStatus` | boolean | `true` | Show hook status messages. | +| `terminal.showImages` | boolean | `true` | Render images inline (when the terminal supports it). | +| `images.autoResize` | boolean | `true` | Resize large images for model compatibility. | +| `images.blockImages` | boolean | `false` | Never send images to providers. | +| `tui.hyperlinks` | enum | `auto` | `off`, `auto`, `always`. | -### Context, memory, and files +For a custom status line, set `statusLine.preset: custom` and configure `statusLine.leftSegments`, `statusLine.rightSegments`, and `statusLine.segmentOptions`. -```yaml -contextPromotion: - enabled: true +### Interaction -compaction: - enabled: true - strategy: context-full # context-full, handoff, shake, snapcompact, off - thresholdPercent: -1 # -1 means default reserve-based behavior - thresholdTokens: -1 # fixed token limit when set - remoteEnabled: true +| Key | Type | Default | Values | +|---|---|---|---| +| `steeringMode` | enum | `one-at-a-time` | `all`, `one-at-a-time`. How queued steering messages are delivered. | +| `followUpMode` | enum | `one-at-a-time` | `all`, `one-at-a-time`. | +| `interruptMode` | enum | `immediate` | `immediate`, `wait`. | +| `doubleEscapeAction` | enum | `tree` | `branch`, `tree`, `none`. | +| `autoResume` | boolean | `false` | Auto-resume the most recent session in the cwd. | +| `ask.timeout` | number | `0` | Seconds before an `ask` prompt times out; `0` = no timeout. (Legacy ms values are migrated to seconds.) | +| `ask.notify` | enum | `on` | `on`, `off`. | -memory: - backend: off # off, local, hindsight, mnemopi - -read: - defaultLimit: 300 - summarize: - enabled: true - prose: false - -edit: - mode: hashline - fuzzyMatch: true - blockAutoGenerated: true -``` - -See [Context files](./context-files.md) for files that are injected as instructions. Those are separate from settings; putting `disabledProviders` in `AGENTS.md` or another context file has no effect. - -### Provider/service settings +### Providers and services ```yaml providers: @@ -386,7 +575,7 @@ providers: kimiApiFormat: anthropic provider: - appendOnlyContext: auto # auto, on, off + appendOnlyContext: auto # auto, on, off exa: enabled: true @@ -399,37 +588,97 @@ searxng: token: SEARXNG_TOKEN ``` -Provider credentials and custom model definitions belong in stored auth, environment variables, or `~/.omp/agent/models.yml`; see [Providers](./providers.md) and [Model and Provider Configuration](./models.md). +| Key | Type | Default | Values / notes | +|---|---|---|---| +| `providers.webSearch` | enum | `auto` | `auto` plus the configured search providers (`tavily`, `perplexity`, `brave`, `jina`, `kimi`, `anthropic`, `gemini`, `codex`, `zai`, `exa`, `parallel`, `kagi`, `synthetic`, `searxng`). | +| `providers.image` | enum | `auto` | `auto`, `openai`, `antigravity`, `xai`, `gemini`, `openrouter`. | +| `providers.fetch` | enum | `auto` | `auto`, `native`, `trafilatura`, `lynx`, `parallel`, `jina`. | +| `providers.tinyModel` | enum | `online` | `online` or a local model (`lfm2-350m`, `qwen3-0.6b`, `gemma-270m`, `qwen2.5-0.5b`, `lfm2-700m`). | +| `providers.tinyModelDevice` | enum | `default` | ONNX execution provider for local tiny models. Overridden by `PI_TINY_DEVICE`. | +| `providers.tinyModelDtype` | enum | `default` | ONNX precision for local tiny models. Overridden by `PI_TINY_DTYPE`. | +| `providers.openaiWebsockets` | enum | `auto` | `auto`, `off`, `on`. | +| `providers.openrouterVariant` | enum | `default` | `default`, `nitro`, `floor`, `online`, `exacto`. | +| `providers.kimiApiFormat` | enum | `anthropic` | `openai`, `anthropic`. | +| `provider.appendOnlyContext` | enum | `auto` | `auto`, `on`, `off`. | +| `exa.enabled` | boolean | `true` | Enable Exa integration. | +| `exa.enableSearch` | boolean | `true` | Exa search. | +| `exa.enableResearcher` | boolean | `false` | Exa researcher. | +| `exa.enableWebsets` | boolean | `false` | Exa websets. | +| `searxng.endpoint` | string | _(unset)_ | SearXNG instance URL. | +| `searxng.token` | string | _(unset)_ | SearXNG token; also `searxng.basicUsername`/`searxng.basicPassword`/`searxng.categories`/`searxng.language`. | +| `auth.broker.url` | string | _(unset)_ | Auth-broker URL. Overridden by `OMP_AUTH_BROKER_URL`. | +| `auth.broker.token` | string | _(unset)_ | Auth-broker token. Overridden by `OMP_AUTH_BROKER_TOKEN`. | + +Provider credentials and custom model definitions are configured separately — see [Providers](./providers.md) and [Models](./models.md). + +### Other groups + +`omp config list` exposes many more grouped settings, including: `task.*` (subagent concurrency, isolation, model overrides), `skills.*` and `commands.*` (discovery toggles), `mcp.*`, `github.*`, `async.*`, `goal.*`, `loop.*`, `todo.*`, `magicKeywords.*`, `ttsr.*` (sticky rules), `display.*`, `startup.*`, `share.*`, `collab.*`, `stt.*`/`tts.*`, `memories.*`/`hindsight.*`/`mnemopi.*` (memory backends), and `bashInterceptor.*`. Each follows the same type/default rules shown above. + +## Legacy migration + +`omp` migrates older config shapes automatically. None of these require action; they are listed so you know what changes you may see in `config.yml`. + +### Startup migration to `config.yml` + +When `~/.omp/agent/config.yml` does not exist, startup builds it once from legacy sources, then writes the result: + +1. `~/.omp/agent/settings.json` (renamed to `settings.json.bak` after a successful migration). +2. Settings persisted in `agent.db`. + +After `config.yml` exists, these legacy sources are no longer consulted. The generic config loader also performs `.json` -> `.yml` migration for other config files when only the `.json` form is present. + +### Field-level migrations + +Applied whenever raw settings are loaded (global, project, overlays, and runtime overrides): + +| Old | New | +|---|---| +| `queueMode` | `steeringMode` | +| `ask.timeout` in milliseconds (value `> 1000`) | seconds (divided by 1000) | +| flat `theme: ""` string | `theme.dark` / `theme.light` (slot chosen by luminance; built-in `light`/`dark` are dropped to use defaults) | +| `task.isolation.enabled: true/false` | `task.isolation.mode: auto/none` | +| `task.simple` | removed | +| legacy `task.isolation.mode` (`worktree`, `fuse-overlay`, `fuse-projfs`) | `rcopy`, `overlayfs`, `projfs` | +| `lastChangelogVersion` | moved to a marker file and stripped from `config.yml` | ## Troubleshooting ### A project setting is not taking effect -- Start `omp` from the directory that contains `.omp/config.yml`. Native settings discovery only checks the current cwd's `.omp/` directory. -- Ensure `.omp/` is non-empty; native discovery ignores empty config directories. -- Check that the file is valid YAML and its top level is a mapping. -- Run `omp config get ` from that cwd to see the effective value. -- Remember that runtime flags and `--config` overlays can override project config. +- Start `omp` from the directory that contains `.omp/config.yml`. Settings discovery only checks the current working directory's `.omp/`, not ancestor directories. +- Ensure `.omp/` is non-empty; empty config directories are ignored. +- Confirm the file is valid YAML and its top level is a mapping. +- Run `omp config get ` from that directory to see the effective value. +- Remember that `--config` overlays and runtime flags override project config. -### A global array seems to disappear in a project +### A global array disappeared in a project -Arrays replace; they do not append. If a project sets `disabledProviders`, `enabledModels`, `extensions`, `cycleOrder`, or another array, include the complete desired project value. +Arrays replace; they do not append. If a project sets `disabledProviders`, `enabledModels`, `cycleOrder`, `extensions`, or any other array, include the **complete** desired value in the project layer — the global array is fully replaced. ### A provider is still available after editing config -- Check whether you disabled the model provider ID (`anthropic`) or a discovery provider ID (`claude`). They are different. -- Check for project config replacing the global `disabledProviders` array. -- Check for credentials in environment variables, `.env`, OAuth, stored auth, or `models.yml`. -- Restart the session if the provider list was already initialized. +- Check whether you disabled the model provider id (e.g. `anthropic`) or a discovery source id (e.g. `claude`) — they are different namespaces with different effects. +- Check for a project (or overlay) `disabledProviders` array replacing your global one. +- Credentials can still come from environment variables, `.env`, OAuth, stored auth, or `models.yml`; disabling a provider blocks selection regardless, but verify you edited the right layer. See [Providers](./providers.md). +- Restart the session if the model list was already initialized. ### `omp config set` changed the wrong file -`omp config set` always writes the global config under the active agent directory. Use `omp config path` to print that directory. Edit `/.omp/config.yml` directly for project-local settings. +`omp config set` and `omp config reset` always write the global `config.yml` under the active agent directory. Run `omp config path` to print it. For project-local settings, edit `/.omp/config.yml` directly. -### A CLI overlay fails at startup +### `omp config reset` did not remove my key -`--config` files are process-local YAML mappings. A missing file, invalid YAML, or a top-level array/string is an error; it does not silently fall back to lower-precedence settings. +`reset` writes the schema **default** value into the global config — it persists the default rather than deleting the key. To stop overriding a project value from global config, delete the key from `~/.omp/agent/config.yml` by hand. -### An environment variable beats config +### A `--config` overlay fails at startup -Some settings intentionally allow env/runtime overrides for machine-local behavior or credentials. Unset the environment variable or remove the CLI flag if you want the persisted config value to win. +`--config` files are process-local YAML mappings. A missing file, invalid YAML, or a top-level array/scalar is a hard error — it does not silently fall back to lower-precedence settings. Fix the path or contents. + +### An environment variable beats my config + +Some settings (model roles, eval backends, tiny-model device/precision, auth broker, PTY) are overridable by env vars or CLI flags for per-machine convenience, and those take precedence over `config.yml`. Unset the variable or drop the flag to let the persisted value win. See [Environment overrides](#environment-overrides) and [Environment variables](./environment-variables.md). + +### `omp config set ` says "Unknown setting" + +Keys must match a schema path exactly, with no shorthand. Use `theme.dark`, not `theme`. Run `omp config list` to see every valid key.