Skip to content

Latest commit

 

History

History
223 lines (179 loc) · 9.7 KB

File metadata and controls

223 lines (179 loc) · 9.7 KB

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

{
  "$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

{
  "$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.

Field Type Notes
description string
model string
fallback_models fallback models see fallback models
variant string
temperature number 0..2
top_p number 0..1
maxTokens number camelCase for parity
thinking { type: "enabled" | "disabled", budgetTokens?: number }
reasoningEffort none | minimal | low | medium | high | xhigh | max camelCase for parity
textVerbosity low | medium | high camelCase for parity
tools record<string, boolean> per-tool allow/deny
prompt_append string
max_prompt_tokens positive int
is_unstable_agent boolean
disable boolean

agents

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

Field Type Notes
description string
prompt string
model string
models string[]
tools record<string, boolean>
execution_mode in-process | process overrides task.default_execution_mode for this agent
background boolean
max_depth int >= 0
allowed_subagents string[]
temperature number 0..2
disable boolean

task

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

Field Type Default
default_execution_mode in-process | process in-process
default_concurrency positive int 5
provider_concurrency record<string, positive int> unset
model_concurrency record<string, positive int> unset
max_depth int >= 0 1
residency_max_children positive int 8
ttl_ms positive int 86400000 (24h)
state_dir string unset (defaults to <project>/.omo/senpi-task)
wait.min_ms positive int 5000
wait.default_ms positive int 60000
wait.max_ms positive int 600000
team.max_members int 1..8 8
team.max_parallel_members int 1..8 4
team.max_wall_clock_minutes positive int 120

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:

Field Type Notes
version literal 1 default 1
name string matching ^[a-z0-9-]+$ optional
description string
createdAt positive int epoch ms
leadAgentId string required when members has more than one entry
teamAllowedPaths string[]
sessionPermission string
members 1..8 members discriminated 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

// .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

  • 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.