Skip to content

Add support for omp (Oh My Pi) as a primary harness - #2503

Open
EvanAgee wants to merge 3 commits into
kunchenguid:mainfrom
EvanAgee:feat/omp-adapter
Open

Add support for omp (Oh My Pi) as a primary harness#2503
EvanAgee wants to merge 3 commits into
kunchenguid:mainfrom
EvanAgee:feat/omp-adapter

Conversation

@EvanAgee

@EvanAgee EvanAgee commented Aug 16, 2026

Copy link
Copy Markdown

What this does

Firstmate was misidentifying omp (Oh My Pi) as Claude Code. omp sets both OMPCODE=1 and CLAUDECODE=1 on its child processes (verified omp 17.3.3 and 17.3.5), and firstmate checked CLAUDECODE before any omp-specific marker. So every omp session got the Claude command surface — wrong hooks, wrong launch flags, wrong protocol doc — and the Pi-family extension supervision never engaged.

The fix

Detection (bin/fm-harness.sh): check OMPCODE=1 before CLAUDECODE=1, with the same precedence logic cursor uses (unambiguous marker first). Add omp to the ancestry fallback. omp is now correctly identified as omp, not claude. Precedence is now: cursor > grok > omp > claude > pi.

Watcher extension (.pi/extensions/fm-primary-omp-watch.ts): ported from the Pi watcher. The key change: omp fires session_switch (reason: new/resume/fork/handoff) for same-process replacements where Pi fired session_shutdown + session_start. Everything else is 1:1 — arm child lifecycle, sendUserMessage({deliverAs:"followUp"}) wake, retry/recovery, lock ownership, the fm_watch_arm_omp tool and /fm-watch-arm-omp command.

Turn-end guard (.pi/extensions/fm-primary-omp-turnend-guard.ts): ported from the Pi guard. The key change: omp's session_stop event replaces Pi's agent_settled as the "turn about to settle" hook. session_stop carries stop_hook_active (matching what fm-turnend-guard.sh expects) and can natively request a continuation turn. The tool_call seatbelt (arm-pretool + cd-pretool), session_compact digest injection, and session-start digest are 1:1.

Spawn (bin/fm-spawn.sh): omp launch branch passes -e for both extensions explicitly (omp does not auto-discover project .pi/extensions), plus a per-task .omp-ext.ts sidecar using omp event names (agent_start/agent_end with willContinue guard instead of Pi's agent_settled + isIdle).

Lock resolver (bin/fm-session-lock-lib.sh): resolves the session lock correctly for omp.

Docs: docs/supervision-protocols/omp.md mirrors pi.md with omp event semantics. README adapter list updated.

Verification

Live-verified against real omp 17.3.5

  • Extension load + lifecycle. Both extensions load and register in a real omp -p session. The full event sequence fires: session_start -> agent_start -> turn_start -> turn_end -> session_stop(stop_hook_active=false) -> agent_end -> session_shutdown. Both load markers (.omp-watch-extension-loaded, .omp-turnend-extension-loaded) write. Session lock and wake queue established.
  • Watcher arm (4A). Model called fm_watch_arm_omp tool; watcher armed with fresh beacon (beacon_age=0); .watch-cycle-exits.log shows origin=started exit_code=0 successor=started; .last-watcher-beat written.
  • Seatbelt (4C). Model attempted bash .../fm-watch-arm.sh (a protected command). The tool_call handler returned {block: true}; omp honored it — tool_execution_end shows isError: true with the deny reason [watcher-nested]. The command did NOT execute. The cd-guard deny ([persistent-cd]) is proven at the checker level and uses the identical block path.
  • Turn-end guard continuation (4D). With supervision needed and no healthy watcher, the guard ran fm-turnend-guard.sh (exit 2), and the extension injected a turn-end-guard: TURN WOULD END BLIND followUp via sendUserMessage({deliverAs:"followUp"}), driving a continuation turn. With no supervision needed, the guard exits 0 and the session settles cleanly (proven in 4A).
  • Auto-retry interlock (4F). Across multiple runs with inter-turn willContinue continuations, session_stop fires only at the final settle — never between turns — so the guard cannot fire during a retry. (Auto-retry is non-deterministic; the auto_retry_start/auto_retry_end events were observed in an earlier run but not captured alongside session_stop in the same run.)

Static-verified

  • Detection matrix (8/8). tests/fm-omp-harness.test.sh — all 8 cases green (env markers plus crew resolution, config-isolated).
  • Extension typecheck. tests/fm-omp-primary-types.test.sh — both extensions pass strict no-emit typecheck against omp 17.3.5.
  • Shell lint. All omp-changed shell scripts shellcheck-clean.
  • Regression. Sibling adapter tests (cursor, grok, kimi, secondmate, pi-types) all pass — no regressions.

Not yet live-verified (honest gaps)

These three cases from the verification plan (docs/omp-adapter-verification.md) could not be driven in this environment:

  • session_switch generation retirement (4B). session_switch fires only on interactive same-process /resume, /new, /fork — not reproducible in -p print mode. The generation start/stop logic is typechecked and the start/shutdown endpoints are live-proven, but the switch-retirement path itself was not driven live.
  • Compaction digest (4E). omp exposes no config key to force a session_compact event deterministically. The handler reuses injectSessionstart (proven live in 4A/4D), but the compact event path was not live-triggered.
  • E2E crew run through fm-spawn (6). fm-spawn hardcodes the firstmate tmux session name (bin/backends/tmux.sh:69, no override), which is the live fleet session on this machine. The spawn omp branch and launch string are verified by code inspection and typecheck, but a live crew run was not driven to avoid interfering with the running fleet.

The deeper automated test files (fm-omp-watch-extension, fm-omp-turnend-guard, fm-omp-primary-live-e2e) are tracked as follow-up in the verification doc.

Test files

  • tests/fm-omp-harness.test.sh — detection matrix (mirrors cursor/grok tests), config-isolated
  • tests/fm-omp-primary-types.test.sh — strict typecheck of both extensions against the installed omp package
  • tests/fixtures/omp-event-probe.ts — test-only event probe for live verification
  • docs/omp-adapter-verification.md — full testing and verification plan

EvanAgee and others added 3 commits August 17, 2026 07:55
Firstmate was misidentifying omp as Claude Code because omp sets both
OMPCODE=1 and CLAUDECODE=1. This made firstmate use the wrong command
surface for every downstream branch.

Fix: detect omp by its own OMPCODE marker before CLAUDECODE, with the
same precedence logic cursor uses (unambiguous marker checked first).
Add omp to the ancestry fallback for marker-clean processes.

Port the Pi adapter's two extensions to omp's extension API:
- Watcher: session_switch (new/resume/fork/handoff) replaces Pi's
  combined session_shutdown+session_start; everything else is 1:1
  (arm child, sendWake, retry, lock, fm_watch_arm_omp tool)
- Guard: session_stop replaces Pi's agent_settled as the settle hook;
  tool_call seatbelt, session_compact digest, and session_start/switch
  digest injection are 1:1

Add omp launch branch to fm-spawn.sh: explicit -e for both extensions
(omp does not auto-discover project .pi/extensions), per-task .omp-ext.ts
sidecar using omp event names, binary resolution, and placeholder
substitutions.

Add docs/supervision-protocols/omp.md, README adapter list, detection
test, event probe fixture, and clear OMPCODE in the shared test lib so
existing tests don't inherit the parent omp process's marker.
fm-session-lock-lib.sh did not know about omp. The harness regex
(FM_HARNESS_RE) omitted omp, the harness names list omitted omp, and
the bare-interpreter case (which handles bun, omp's runtime) did not
extract the script path to find the harness name.

When the model ran bin/fm-session-start.sh under omp, the lock resolver
could not find the omp process in the ancestry and failed with 'cannot
locate harness process in ancestry'. The session lock stayed stale and
fm_watch_arm_omp refused to arm.

Fix: add omp to FM_HARNESS_RE and FM_HARNESS_NAMES, add bun to the
bare-interpreter case, and extract the script path from the interpreter
args so fm_harness_path_name can find the omp component.
The omp detection test's crew-resolution case read the operator's real
config/crew-harness, so it returned the pinned adapter instead of testing
the 'default mirrors own' path. Point detect_with at an empty config dir
so the matrix tests detection logic, not local config.

Add tests/fm-omp-primary-types.test.sh mirroring the Pi types test: it
strict-typechecks both omp extensions (fm-primary-omp-watch.ts,
fm-primary-omp-turnend-guard.ts) against the installed @oh-my-pi/pi-coding-agent
package, so a type break is caught in CI without a live omp run.
@0xb1ob

0xb1ob commented Aug 17, 2026

Copy link
Copy Markdown

There are 8 other PRs about adding omp support. No response from the repo maintainer.

@EvanAgee

Copy link
Copy Markdown
Author

Thanks for the heads up. Have you tried any of them personally? Any of them that allow things to run smoothly in firstmate?

@0xb1ob

0xb1ob commented Aug 17, 2026

Copy link
Copy Markdown

Thanks for the heads up. Have you tried any of them personally? Any of them that allow things to run smoothly in firstmate?

I'm trying this one since yesterday https://github.com/Boxyboxy/firstmate.git branch: feat/omp-adaptor. Seems to be working so far.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants