docs/reference/omo-json.md
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.
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).
omo.jsonc, falling back to omo.json, under:
%APPDATA%\omo on Windows,$XDG_CONFIG_HOME/omo,~/.config/omo..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):
__proto__, prototype, and constructor keys are stripped from both merge keys and nested values (prototype-pollution guard).Safety and failure handling:
.omo directory or a symlinked project config file is skipped as a load source (loader/paths.ts).diagnostics and is skipped; loading continues.validation diagnostic instead of throwing (loader/loader.ts).$schemaThe 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
{
"$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" }
]
}
}
}
{
"$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.
categoriesA 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 |
agentsA 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 |
taskTask 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.
teamsA 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 (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? }.
// .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." }
]
}
}
}
omo.json vs oh-my-openagent.jsonomo.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:
oh-my-openagent.json[c] chain (see docs/reference/configuration.md).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.
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.omo.json and a oh-my-openagent.json migration path are not implemented.