Back to Oh My Openagent

omo.json Configuration Reference

docs/reference/omo-json.md

4.16.19.9 KB
Original Source

omo.json Configuration Reference

omo.json (or omo.jsonc) is the harness-neutral configuration surface owned by @oh-my-opencode/omo-config-core. Today it is read by the Senpi adapter's task component only; the schema, loader, and writer are shared code so other harnesses can adopt it later (see Coexistence and ROADMAP.md).

Files may be JSONC: // comments and trailing commas are allowed. Every schema object is .strict(), so unknown keys are rejected and reported as a diagnostic rather than silently ignored.

File locations and precedence

The loader resolves layers in resolveOmoConfigPaths and folds them lowest-to-highest, so the last layer merged wins (packages/omo-config-core/src/loader/paths.ts, loader.ts).

  1. User layer (lowest precedence). omo.jsonc, falling back to omo.json, under:
    • %APPDATA%\omo on Windows,
    • else $XDG_CONFIG_HOME/omo,
    • else ~/.config/omo.
  2. Project layers. .omo/omo.jsonc (then .omo/omo.json) in every directory from the current working directory up to $HOME. Farther ancestors are merged first; the nearest project file has the highest precedence and beats the user layer.

Merge rules (loader/merge.ts):

  • Plain objects deep-merge recursively.
  • Scalars and arrays replace the lower layer wholesale.
  • __proto__, prototype, and constructor keys are stripped from both merge keys and nested values (prototype-pollution guard).

Safety and failure handling:

  • A symlinked project .omo directory or a symlinked project config file is skipped as a load source (loader/paths.ts).
  • A missing, unreadable, or invalid layer becomes an entry in the result's diagnostics and is skipped; loading continues.
  • If the merged config fails final validation, the loader returns the all-default config plus one validation diagnostic instead of throwing (loader/loader.ts).

$schema

The root schema accepts an optional $schema string key (packages/omo-config-core/src/schema/config.ts:8,16); both the per-layer parse and the final merged parse (packages/omo-config-core/src/loader/loader.ts:76,116) carry it through and otherwise ignore it, so an editor pointer is safe to add.

A generated JSON schema artifact ships at assets/omo.schema.json, produced from OmoConfigSchema by the root build:omo-schema script (script/build-omo-schema.ts, script/build-omo-schema-document.ts); run bun run build:omo-schema to regenerate it. Point your editor at the raw dev-branch URL:

https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/omo.schema.json

Example

json
{
  "$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/omo.schema.json",
  "categories": {
    "deep": {
      "description": "Deep analysis",
      "model": "anthropic/claude",
      "reasoningEffort": "high"
    }
  },
  "agents": {
    "reviewer": {
      "description": "Reviews code",
      "model": "openai/gpt-5",
      "execution_mode": "in-process"
    }
  },
  "task": {
    "default_execution_mode": "in-process",
    "default_concurrency": 5
  },
  "teams": {
    "builders": {
      "description": "Build team",
      "members": [
        { "name": "quick-one", "kind": "category", "category": "quick", "prompt": "Help" }
      ]
    }
  }
}

Top-level schema

jsonc
{
  "$schema": "…",        // optional editor pointer
  "categories": {},   // record<string, CategoryConfig>
  "agents": {},       // record<string, AgentDef>
  "task": {},         // task engine settings
  "teams": {}         // record<string, TeamSpec>
}

Source: packages/omo-config-core/src/schema/config.ts.

categories

A record of category name to config (schema/category.ts). Category keys intentionally keep the OpenCode key set, including the camelCase exceptions maxTokens, reasoningEffort, textVerbosity, and thinking.budgetTokens; every other key is snake_case.

FieldTypeNotes
descriptionstring
modelstring
fallback_modelsfallback modelssee fallback models
variantstring
temperaturenumber 0..2
top_pnumber 0..1
maxTokensnumbercamelCase for parity
thinking{ type: "enabled" | "disabled", budgetTokens?: number }
reasoningEffortnone | minimal | low | medium | high | xhigh | maxcamelCase for parity
textVerbositylow | medium | highcamelCase for parity
toolsrecord<string, boolean>per-tool allow/deny
prompt_appendstring
max_prompt_tokenspositive int
is_unstable_agentboolean
disableboolean

agents

A record of agent name to definition (schema/agent.ts).

FieldTypeNotes
descriptionstring
promptstring
modelstring
modelsstring[]
toolsrecord<string, boolean>
execution_modein-process | processoverrides task.default_execution_mode for this agent
backgroundboolean
max_depthint >= 0
allowed_subagentsstring[]
temperaturenumber 0..2
disableboolean

task

Task engine settings; every field has a default, so the whole object is optional (schema/task.ts).

FieldTypeDefault
default_execution_modein-process | processin-process
default_concurrencypositive int5
provider_concurrencyrecord<string, positive int>unset
model_concurrencyrecord<string, positive int>unset
max_depthint >= 01
residency_max_childrenpositive int8
ttl_mspositive int86400000 (24h)
state_dirstringunset (defaults to <project>/.omo/senpi-task)
wait.min_mspositive int5000
wait.default_mspositive int60000
wait.max_mspositive int600000
team.max_membersint 1..88
team.max_parallel_membersint 1..84
team.max_wall_clock_minutespositive int120

state_dir defaults to <project_dir>/.omo/senpi-task when unset (packages/senpi-task/src/store/state-dir.ts). Completion delivery is not configurable: every child completion is batched with any other ready notifications and steered into the parent's running turn at the next tool-call boundary; see the completion routing table in packages/senpi-task/AGENTS.md.

teams

A record of team name to spec (schema/team.ts). Each spec:

FieldTypeNotes
versionliteral 1default 1
namestring matching ^[a-z0-9-]+$optional
descriptionstring
createdAtpositive intepoch ms
leadAgentIdstringrequired when members has more than one entry
teamAllowedPathsstring[]
sessionPermissionstring
members1..8 membersdiscriminated on kind

Each member shares a base (name matching ^[a-z0-9-]+$, optional cwd, worktreePath, subscriptions, color, isActive default true, backendType default in-process) and one of two kinds:

  • kind: "category" requires category and prompt.
  • kind: "subagent_type" requires subagent_type; prompt is optional.

Fallback models

fallback_models (on a category) and per-model fallback entries accept a union (schema/fallback-models.ts): a single model string, an array of model strings, an array of objects, or a mixed array. Each object is { model, variant?, reasoningEffort?, temperature?, top_p?, maxTokens?, thinking? }.

Example

jsonc
// .omo/omo.jsonc
{
  "task": {
    "default_execution_mode": "in-process",
    "default_concurrency": 4,
    "wait": { "default_ms": 90000 }
  },
  "categories": {
    "deep": {
      "model": "anthropic/claude-opus-4-8",
      "reasoningEffort": "high",
      "fallback_models": ["anthropic/claude-sonnet-4-5"]
    }
  },
  "agents": {
    "researcher": {
      "description": "Read-only investigator",
      "execution_mode": "process",
      "tools": { "task": false }
    }
  },
  "teams": {
    "reviewers": {
      "leadAgentId": "lead",
      "members": [
        { "kind": "category", "name": "quick", "category": "deep", "prompt": "Review the diff." }
      ]
    }
  }
}

Coexistence: omo.json vs oh-my-openagent.json

omo.json and the OpenCode-family config (oh-my-openagent.json / oh-my-opencode.json) have zero interaction today. They are separate files read by separate loaders:

  • The OpenCode plugin reads the walked oh-my-openagent.json[c] chain (see docs/reference/configuration.md).
  • The Senpi task component reads omo.json only, through @oh-my-opencode/omo-config-core.

There is no automatic migration or field bridging between the two. When a project contains BOTH an OpenCode-family config and an omo.json that contributed categories/agents, the Senpi task component emits a one-time warning on first session start noting that senpi reads omo.json only and ignores the OpenCode config for tasks (packages/omo-senpi/src/components/task/coexistence.ts).

This is deliberate: omo.json landed senpi-first. Adopting it in the OpenCode edition, and any migration path from oh-my-openagent.json, is a later phase tracked in ROADMAP.md.

Follow-ups

  • No generated assets/omo.schema.json artifact or published $schema URL exists yet; the $schema key is accepted but points at nothing shipped in this repo.
  • member.backendType: "tmux" and non-project (user-global) team storage are schema-level only and are not exercised by the current Senpi runtime; use in-process members in project .omo/ teams.
  • OpenCode-edition adoption of omo.json and a oh-my-openagent.json migration path are not implemented.