Driving omp with an agent
You (the agent) are the ORCHESTRATOR. The model running inside omp is the BUILDER. You never write
the deliverable yourself — you set up the work, launch bounded builder turns, verify what comes
back, fix small mechanical slips, and keep an audit trail. This division is what makes results
credible: the builder's work stays the builder's work.
Provenance: distilled from ~90 real omp turns across 10 models and 5 providers (OpenRouter,
DashScope, DeepSeek official, a stealth-model gateway, local vLLM), on the omp build current as
of 2026-08-25. omp evolves — sanity-check flags against omp --help before relying on them.
1. What omp is
omp is a CLI coding-agent harness: point it at any OpenAI-compatible model, give it a prompt, and
it runs an agentic loop (read/write/bash/grep/LSP tools) inside the current working directory.
Key property: cwd is the contract. At launch omp loads <cwd>/.omp/AGENTS.md (the builder's
role + project brief), <cwd>/.omp/RULES.md (non-negotiables), and <cwd>/.omp/config.yml.
Always launch from the lane's directory.
2. From a user goal to a run plan
When the user says "use omp to drive <model(s)> toward ":
- Scaffold each lane yourself — one directory per model. Seed the skeleton (entry files,
vendored deps, lint/check tooling, reference material). Never make the builder scaffold; wasted
turns and divergent layouts.
- Write
.omp/AGENTS.md — the builder's role, the goal, repo layout, how its work will be
verified, hard constraints (e.g. "no CDNs", "≤250 lines/file", "do not start servers").
- Decompose the goal into numbered turn prompts (
prompts/turns/01-*.md …). Each turn:
- one focused, ≤80-minute task;
- an explicit ACCEPTANCE section you can check mechanically (exact API names, file outputs,
numeric specs) — this is your verification script;
- "where this prompt gives explicit numbers, the prompt wins" (builders drift from their own
design docs);
- ends with "append a checkpoint to NOTES.md". Each
-p launch is a FRESH context: repo state
- NOTES.md is the builder's only memory.
- Define an interface contract if lanes must be comparable or testable (exact global APIs,
event names, file layout). Identical contracts are what make cross-model comparison honest.
- Forewarn known traps in the prompt (see §8) — one warning line prevents entire bug classes.
3. Provider configuration (~/.omp/agent/models.yml)
providers:
myprov: # used as --model myprov/<model-id>
baseUrl: https://openrouter.ai/api/v1 # any OpenAI-compatible endpoint, incl. http://127.0.0.1:8080/v1
api: openai-completions
apiKey: sk-...
compat:
supportsReasoningEffort: true
maxTokensField: max_tokens
streamIdleTimeoutMs: 900000
supportsStore: false
supportsDeveloperRole: false
models:
- id: some-vendor/some-model # slashes in ids are fine; --model splits on FIRST slash
name: Some Model
reasoning: true
input: [text, image] # [text] for blind models; gates @image attachments
contextWindow: 262144
maxTokens: 32768
cost: {input: 3, output: 15, cacheRead: 0, cacheWrite: 0} # ALL FOUR cost keys REQUIRED
- Local models (vLLM/llama.cpp/llama-swap) work identically via their local baseUrl.
- Never print or commit apiKey values. Edit models.yml with anchored, unique replacements.
- Parameters omp can't send (e.g. vLLM
chat_template_kwargs.reasoning_effort): run the tiny
body-rewriting proxy in templates/effort_proxy.py and point baseUrl at it.
- Probe the raw endpoint before wiring it in (curl/python, not omp): tiny completion (key +
model id live?),
image_url data-URI (vision? → sets input:), pricing/modalities (OpenRouter:
GET /api/v1/models lists architecture.input_modalities + pricing per model). omp never sends
video — if a model accepts video_url, do video analysis as a one-shot direct API call and save
the result where turns can read it.
4. The canonical turn launch
cd <LANE_DIR> && omp -p \
--model <provider>/<model-id> \
--approval-mode yolo \
--max-time 4800 \
@prompts/turns/03-weather.md @ref/target.jpg \
"Implement the attached turn exactly; images are reference material; where the prompt gives \
explicit numbers the prompt wins; finish with checks + a NOTES.md checkpoint" \
</dev/null > build/turn-logs/03.log 2>&1; echo "EXIT $?" >> build/turn-logs/03.log
-p non-interactive; --approval-mode yolo auto-approves tools (required unattended).
--max-time <s>: hard cap; at expiry omp prints Deadline exceeded, exits 1. 3600 for fast
models, 4800 for reasoning-heavy ones.
@file: text inlined; images sent as vision iff the model's input: lists image. 1–2 images/turn.
</dev/null is mandatory in scripts (omp can block on stdin without it).
- The
echo "EXIT $?" sentinel is your completion signal: run the whole line as a background task
and poll grep -q "EXIT" <log> every ~60s. Never a bare long sleep.
--thinking off|minimal|low|medium|high|xhigh|max|auto (default auto ≈ the API's own default).
Leave at default for "stock settings" comparability; set deliberately otherwise.
5. Failure modes (all observed repeatedly — memorize this table)
| Signal |
Meaning |
Your move |
EXIT 0 |
turn finished |
verify (§6) |
EXIT 1 + Deadline exceeded |
capped; work usually MOSTLY landed |
salvage: check files, run checks, write the missing NOTES checkpoint yourself (labelled orchestrator-written); relaunch only if essentials missing |
EXIT 1 + provider error |
upstream died mid-generation |
inspect what landed; fragmentary → relaunch with a CONTINUE preamble ("repo state is: <files + their APIs>; do not rewrite; remaining scope: ") |
log stuck Working..., no EXIT, session jsonl stale >20 min |
silent process death (external kill; the stream-idle timer can't fire on a dead process) |
treat as dead; salvage or CONTINUE — landed code was substantially complete every time we saw this |
EXIT 0, zero work |
provider returned an empty completion |
relaunch as-is |
Liveness check for a running turn: newest file mtime in
~/.omp/agent/sessions/--<cwd-with-dashes>--/ (e.g. D:\dev\myproj\lane-a →
--D--dev-myproj-lane-a--). The log's Working... heartbeat is NOT a health signal; the session
jsonl's growth is.
6. Verification discipline
- Verify independently, mechanically, every turn. Run the acceptance checks yourself (static
checks, contract probes, screenshots if visual). "It exited 0" and the builder's self-report are
not verification — though well-behaved models' self-reports usually match.
- Per-lane verification harnesses, never shared ones: give each lane its own headless browser /
test runner. A shared instance serializes all lanes and wedges under contention.
- Hand-patch policy: you may fix small MECHANICAL slips yourself (a dropped wiring line, a
units/scale constant, a wrong normal, a CSS specificity clash). Log every patch in the lane's
NOTES.md with symptom + root cause. Anything needing real design goes back to the builder as a
relaunched focused turn. Keep the tally — it's your per-model quality metric.
- Instrument, don't eyeball: sample pixel RGBs instead of judging a dark thumbnail; time-series a
property (
getComputedStyle, light.intensity) instead of trusting one screenshot of a
transient. Under CPU load, headless timing drifts — trigger effects in ISOLATION before calling
a timing bug.
- Statics that lie:
node --check and eslint do NOT catch duplicate ESM import bindings (fatal at
link time). After any entrypoint edit run
node --input-type=module -e "await import('./src/main.js')" — a SyntaxError before
browser-global ReferenceErrors means a broken page.
7. Multi-lane orchestration
- Lanes parallelize perfectly (separate cwds/providers). Six concurrent turns ran with zero
interference for us.
- Git in a shared repo:
git add -A <LANE_DIR> then git commit -m "..." -- <LANE_DIR>. The
pathspec on the COMMIT is what saves you — a bare git commit commits the whole shared index,
including another lane's staged-but-uncommitted files (this happened to us).
- Spend gates: before every launch, query the billing endpoint (OpenRouter
GET /api/v1/key +
/api/v1/credits; DeepSeek GET /user/balance — the API key reads its own spend). Below a
floor (~2 turns' cost): don't launch, write a "PAUSED (credits)" note, stop at a clean turn
boundary. Resume = just launch the paused turn.
- Token accounting: sum
completion_tokens from the usage records in the lane's session
jsonls. Shapes vary by provider; treat sums as ±10% and present with ~.
- Delegating lane supervision to sub-agents works, but they stall at long waits — give them
bounded in-call poll loops, and keep a cheap change-driven monitor + the authority to
batch-verify stalled lanes yourself.
8. Cross-model bug classes worth forewarning (each hit 2–7 independent models)
- Built-but-never-connected: a module/file written but never imported/registered/called — at
every granularity (a whole subsystem coordinator, one
modules.x = create() line, a physics
write-back). After integration-heavy turns, diff the full wiring against expectations.
- Dual-path event handling: hooking a wrapped API function OR real DOM events, not both — the
single most-reproduced bug in our runs (7 lanes). Require both paths in the prompt and test both.
- Magnitude miscalibration (visual domains): dark PBR albedos, light units crushed by tone
mapping, forces integrated weaker than gravity, geometry 2–3× its own spec. Structure is
usually right; magnitudes need measured verification (project to screen space, sample pixels).
- Rewiring regressions: turns that edit a shared entrypoint silently drop unrelated existing
lines (three lanes broke prior work while adding a new subsystem). Diff the entrypoint after
every turn that touches it.
- Multiplicative color darkening: texture × vertexColor × instanceColor stacking the same
dark value into near-black.
- Text-only ("blind") models are viable builders: structure and logic land; what they can't do is
judge magnitudes. Feed them a precise WRITTEN defect list (positions, sizes, colors) for polish
turns — blind FIXING from a good diagnosis works almost perfectly.
9. Quickstart recipe
User: "use omp to have build " →
- Probe the endpoint; add provider+model to models.yml (§3).
- Scaffold the lane: skeleton +
.omp/AGENTS.md + .omp/RULES.md + turn prompts with acceptance
criteria (§2). Serve/preview infra is YOURS to run, not the builder's.
- Launch turn 1 (§4) as a background task; poll for EXIT; on failure use the table (§5).
- Verify (§6); hand-patch mechanical slips (logged) or relaunch focused turns; commit per
verified turn (pathspec!).
- Repeat through the final turn; for a polish turn, attach the target reference + a fresh capture
of the current state (or a written defect list for blind models).
- Report per-turn results, the patch tally, and token/spend totals.
For "run this goal across SEVERAL models": one lane per model, identical prompts and interface
contract, same verification recipe — the deltas between lanes become your comparison.
1---2name: omp-orchestration3description: Lets an agent drive local omp (oh-my-pi) instances toward a user's goal — spin up one or many model "lanes", configure providers, decompose the goal into bounded turns, launch/monitor/salvage non-interactive omp runs, verify independently, and account tokens/spend. Use when the user says things like "use omp to have <model> build <thing>" or "run this goal across several models", when adding a provider to models.yml, or when an omp turn misbehaves.4---56# Driving omp with an agent78You (the agent) are the ORCHESTRATOR. The model running inside omp is the BUILDER. You never write9the deliverable yourself — you set up the work, launch bounded builder turns, verify what comes10back, fix small mechanical slips, and keep an audit trail. This division is what makes results11credible: the builder's work stays the builder's work.1213> Provenance: distilled from ~90 real omp turns across 10 models and 5 providers (OpenRouter,14> DashScope, DeepSeek official, a stealth-model gateway, local vLLM), on the omp build current as15> of 2026-08-25. omp evolves — sanity-check flags against `omp --help` before relying on them.1617## 1. What omp is1819`omp` is a CLI coding-agent harness: point it at any OpenAI-compatible model, give it a prompt, and20it runs an agentic loop (read/write/bash/grep/LSP tools) inside the current working directory.2122Key property: **cwd is the contract.** At launch omp loads `<cwd>/.omp/AGENTS.md` (the builder's23role + project brief), `<cwd>/.omp/RULES.md` (non-negotiables), and `<cwd>/.omp/config.yml`.24Always launch from the lane's directory.2526## 2. From a user goal to a run plan2728When the user says "use omp to drive <model(s)> toward <goal>":29301. **Scaffold each lane yourself** — one directory per model. Seed the skeleton (entry files,31 vendored deps, lint/check tooling, reference material). Never make the builder scaffold; wasted32 turns and divergent layouts.332. **Write `.omp/AGENTS.md`** — the builder's role, the goal, repo layout, how its work will be34 verified, hard constraints (e.g. "no CDNs", "≤250 lines/file", "do not start servers").353. **Decompose the goal into numbered turn prompts** (`prompts/turns/01-*.md` …). Each turn:36 - one focused, ≤80-minute task;37 - an explicit ACCEPTANCE section you can check mechanically (exact API names, file outputs,38 numeric specs) — this is your verification script;39 - "where this prompt gives explicit numbers, the prompt wins" (builders drift from their own40 design docs);41 - ends with "append a checkpoint to NOTES.md". Each `-p` launch is a FRESH context: repo state42 + NOTES.md is the builder's only memory.434. **Define an interface contract** if lanes must be comparable or testable (exact global APIs,44 event names, file layout). Identical contracts are what make cross-model comparison honest.455. **Forewarn known traps** in the prompt (see §8) — one warning line prevents entire bug classes.4647## 3. Provider configuration (`~/.omp/agent/models.yml`)4849```yaml50providers:51 myprov: # used as --model myprov/<model-id>52 baseUrl: https://openrouter.ai/api/v1 # any OpenAI-compatible endpoint, incl. http://127.0.0.1:8080/v153 api: openai-completions54 apiKey: sk-...55 compat:56 supportsReasoningEffort: true57 maxTokensField: max_tokens58 streamIdleTimeoutMs: 90000059 supportsStore: false60 supportsDeveloperRole: false61 models:62 - id: some-vendor/some-model # slashes in ids are fine; --model splits on FIRST slash63 name: Some Model64 reasoning: true65 input: [text, image] # [text] for blind models; gates @image attachments66 contextWindow: 26214467 maxTokens: 3276868 cost: {input: 3, output: 15, cacheRead: 0, cacheWrite: 0} # ALL FOUR cost keys REQUIRED69```7071- Local models (vLLM/llama.cpp/llama-swap) work identically via their local baseUrl.72- Never print or commit apiKey values. Edit models.yml with anchored, unique replacements.73- Parameters omp can't send (e.g. vLLM `chat_template_kwargs.reasoning_effort`): run the tiny74 body-rewriting proxy in `templates/effort_proxy.py` and point baseUrl at it.75- **Probe the raw endpoint before wiring it in** (curl/python, not omp): tiny completion (key +76 model id live?), `image_url` data-URI (vision? → sets `input:`), pricing/modalities (OpenRouter:77 `GET /api/v1/models` lists `architecture.input_modalities` + pricing per model). omp never sends78 video — if a model accepts `video_url`, do video analysis as a one-shot direct API call and save79 the result where turns can read it.8081## 4. The canonical turn launch8283```bash84cd <LANE_DIR> && omp -p \85 --model <provider>/<model-id> \86 --approval-mode yolo \87 --max-time 4800 \88 @prompts/turns/03-weather.md @ref/target.jpg \89 "Implement the attached turn exactly; images are reference material; where the prompt gives \90 explicit numbers the prompt wins; finish with checks + a NOTES.md checkpoint" \91 </dev/null > build/turn-logs/03.log 2>&1; echo "EXIT $?" >> build/turn-logs/03.log92```9394- `-p` non-interactive; `--approval-mode yolo` auto-approves tools (required unattended).95- `--max-time <s>`: hard cap; at expiry omp prints `Deadline exceeded`, exits 1. 3600 for fast96 models, 4800 for reasoning-heavy ones.97- `@file`: text inlined; images sent as vision iff the model's `input:` lists image. 1–2 images/turn.98- `</dev/null` is **mandatory** in scripts (omp can block on stdin without it).99- The `echo "EXIT $?"` sentinel is your completion signal: run the whole line as a background task100 and poll `grep -q "EXIT" <log>` every ~60s. Never a bare long sleep.101- `--thinking off|minimal|low|medium|high|xhigh|max|auto` (default auto ≈ the API's own default).102 Leave at default for "stock settings" comparability; set deliberately otherwise.103104## 5. Failure modes (all observed repeatedly — memorize this table)105106| Signal | Meaning | Your move |107|---|---|---|108| `EXIT 0` | turn finished | verify (§6) |109| `EXIT 1` + `Deadline exceeded` | capped; work usually MOSTLY landed | salvage: check files, run checks, write the missing NOTES checkpoint yourself (labelled orchestrator-written); relaunch only if essentials missing |110| `EXIT 1` + provider error | upstream died mid-generation | inspect what landed; fragmentary → relaunch with a **CONTINUE preamble** ("repo state is: <files + their APIs>; do not rewrite; remaining scope: <list>") |111| log stuck `Working...`, no EXIT, session jsonl stale >20 min | **silent process death** (external kill; the stream-idle timer can't fire on a dead process) | treat as dead; salvage or CONTINUE — landed code was substantially complete every time we saw this |112| `EXIT 0`, zero work | provider returned an empty completion | relaunch as-is |113114**Liveness check** for a running turn: newest file mtime in115`~/.omp/agent/sessions/--<cwd-with-dashes>--/` (e.g. `D:\dev\myproj\lane-a` →116`--D--dev-myproj-lane-a--`). The log's `Working...` heartbeat is NOT a health signal; the session117jsonl's growth is.118119## 6. Verification discipline120121- **Verify independently, mechanically, every turn.** Run the acceptance checks yourself (static122 checks, contract probes, screenshots if visual). "It exited 0" and the builder's self-report are123 not verification — though well-behaved models' self-reports usually match.124- Per-lane verification harnesses, never shared ones: give each lane its own headless browser /125 test runner. A shared instance serializes all lanes and wedges under contention.126- **Hand-patch policy**: you may fix small MECHANICAL slips yourself (a dropped wiring line, a127 units/scale constant, a wrong normal, a CSS specificity clash). Log every patch in the lane's128 NOTES.md with symptom + root cause. Anything needing real design goes back to the builder as a129 relaunched focused turn. Keep the tally — it's your per-model quality metric.130- Instrument, don't eyeball: sample pixel RGBs instead of judging a dark thumbnail; time-series a131 property (`getComputedStyle`, `light.intensity`) instead of trusting one screenshot of a132 transient. Under CPU load, headless timing drifts — trigger effects in ISOLATION before calling133 a timing bug.134- Statics that lie: `node --check` and eslint do NOT catch duplicate ESM import bindings (fatal at135 link time). After any entrypoint edit run136 `node --input-type=module -e "await import('./src/main.js')"` — a SyntaxError before137 browser-global ReferenceErrors means a broken page.138139## 7. Multi-lane orchestration140141- Lanes parallelize perfectly (separate cwds/providers). Six concurrent turns ran with zero142 interference for us.143- **Git in a shared repo**: `git add -A <LANE_DIR>` then `git commit -m "..." -- <LANE_DIR>`. The144 pathspec on the COMMIT is what saves you — a bare `git commit` commits the whole shared index,145 including another lane's staged-but-uncommitted files (this happened to us).146- **Spend gates**: before every launch, query the billing endpoint (OpenRouter `GET /api/v1/key` +147 `/api/v1/credits`; DeepSeek `GET /user/balance` — the API key reads its own spend). Below a148 floor (~2 turns' cost): don't launch, write a "PAUSED (credits)" note, stop at a clean turn149 boundary. Resume = just launch the paused turn.150- **Token accounting**: sum `completion_tokens` from the usage records in the lane's session151 jsonls. Shapes vary by provider; treat sums as ±10% and present with `~`.152- Delegating lane supervision to sub-agents works, but they stall at long waits — give them153 bounded in-call poll loops, and keep a cheap change-driven monitor + the authority to154 batch-verify stalled lanes yourself.155156## 8. Cross-model bug classes worth forewarning (each hit 2–7 independent models)1571581. **Built-but-never-connected**: a module/file written but never imported/registered/called — at159 every granularity (a whole subsystem coordinator, one `modules.x = create()` line, a physics160 write-back). After integration-heavy turns, diff the full wiring against expectations.1612. **Dual-path event handling**: hooking a wrapped API function OR real DOM events, not both — the162 single most-reproduced bug in our runs (7 lanes). Require both paths in the prompt and test both.1633. **Magnitude miscalibration** (visual domains): dark PBR albedos, light units crushed by tone164 mapping, forces integrated weaker than gravity, geometry 2–3× its own spec. Structure is165 usually right; magnitudes need measured verification (project to screen space, sample pixels).1664. **Rewiring regressions**: turns that edit a shared entrypoint silently drop unrelated existing167 lines (three lanes broke prior work while adding a new subsystem). Diff the entrypoint after168 every turn that touches it.1695. **Multiplicative color darkening**: texture × vertexColor × instanceColor stacking the same170 dark value into near-black.1716. Text-only ("blind") models are viable builders: structure and logic land; what they can't do is172 judge magnitudes. Feed them a precise WRITTEN defect list (positions, sizes, colors) for polish173 turns — blind FIXING from a good diagnosis works almost perfectly.174175## 9. Quickstart recipe176177User: "use omp to have <model> build <thing>" →1781. Probe the endpoint; add provider+model to models.yml (§3).1792. Scaffold the lane: skeleton + `.omp/AGENTS.md` + `.omp/RULES.md` + turn prompts with acceptance180 criteria (§2). Serve/preview infra is YOURS to run, not the builder's.1813. Launch turn 1 (§4) as a background task; poll for EXIT; on failure use the table (§5).1824. Verify (§6); hand-patch mechanical slips (logged) or relaunch focused turns; commit per183 verified turn (pathspec!).1845. Repeat through the final turn; for a polish turn, attach the target reference + a fresh capture185 of the current state (or a written defect list for blind models).1866. Report per-turn results, the patch tally, and token/spend totals.187188For "run this goal across SEVERAL models": one lane per model, identical prompts and interface189contract, same verification recipe — the deltas between lanes become your comparison.