Repository navigation
feat(agents): oh-my-pi (omp) as a built-in agent - #360
Merged
Merged
Conversation
omp (github.com/can1357/oh-my-pi) is pi's rewritten fork. Everything shipped here was measured against a live 18.6.0 per docs/adding-an-agent.md, with the version and observation in the comments: - Registry: capture-shaped resume (`-r <uuid>` with history, `-r` alone opens omp's own picker, no mint flag), empty yolo_args because omp's own tools.approvalMode defaults to yolo (a write inside AND outside the workspace executed unasked), title signals captured off a live pty (pi mark + spinner frame, `pi >` at the composer), UNATTENDED_SPAWN_ARGS --allow-home because omp silently moves off $HOME to a temp dir otherwise. - Hooks: the pi plugin transport carries over (omp autoloads ~/.omp/agent/extensions/*.ts with pi-compatible machinery), with omp's event differences: done on agent_end guarded by willContinue, attention from tool_approval_requested plus the ask edge, subagent sessions dropped via ctx.agent.kind, and the session id REPORTED on session_start (fires at startup), which is the resume binding. A node-driven test runs the real generated extension against the measured event shapes. - Login store: ConfigDir over PI_CONFIG_DIR, measured (the credential is a SQLite row in agent.db, no keyring); probe row added. - Usage: agent_usage_omp spawns `omp usage --json` (measured structured limits), so omp reports_usage and gets the pull chip. - Docker: KNOWN_SAFE_AGENTS + BASE_BUILTINS + a Dockerfile line (the omp.sh installer drops the binary in ~/.local/bin, outside the mounted ~/.omp, so no grok shadowing). - Icon (classic serif pi, distinct from pi's filled staircase), brand color both themes, i18n reset lists brought current, README list. - Docs: agent-hooks.md (per-agent rows, the Warp-protocol rejection, the omp: Complete notify anchor), agent-accounts.md, sandbox.md. Suites: cargo test 1189 green, npm test 2618 green, make login-probe AGENT=omp ok, full make e2e 24/25 specs (git.e2e.ts is the known machine-specific failure: this checkout's path contains "github" and gh is unauthenticated for it). Co-Authored-By: Claude Code <noreply@anthropic.com>
simion
approved these changes
Oct 5, 2026
simion
left a comment
Owner
There was a problem hiding this comment.
Checked the security-relevant defaults for the omp integration: yolo_args is empty and matches pi's pattern (omp's own yolo approval mode is an upstream omp default, not something termic grants), --allow-home is run-scoped (only composed when unattended, never persisted), binary/plugin paths are static literals with no task-input interpolation, and the Seatbelt sandbox entry goes through the same sbpl_escape path as every other agent. Integration matches pi/opencode point-for-point, divergences are stated and test-pinned. No blocking issues.
Owner
|
Live in v1.13.4, released today. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
oh-my-pi (omp) is a terminal-first coding agent, rewritten from pi (badlogic's pi-mono), which termic already supports. This adds it as the eleventh built-in, measured against a live 18.6.0 per docs/adding-an-agent.md rather than written from
--help: every default below carries the version and the observation in a comment.What omp measured as, and what shipped
Resume is capture-shaped.
-r <uuid>resumes a session with its history,-r <unknown>fails fast with exit 1 ("Session not found"),-rwith no value opens omp's own picker, and nothing accepts an id at launch. So:resume_id_args ["--resume", "{UUID}"],resume_picker_args ["--resume"](all three picker facts measured: it opens, picking reports the id through the plugin, Esc leaves at exit 0 so it cannot loop), and no mint.Approvals default to yolo, so
yolo_argsis empty on purpose. omp's owntools.approvalModedefaults to"yolo": a file write inside AND outside the workspace executed unasked, in both the TUI and-p. Same reasoning as pi's empty entry, recorded so it does not read as a skipped measurement.It bounces out of
$HOME. A spawn in the home directory silently moved to a temp dir (the session recorded cwd /tmp), so unattended spawns compose--allow-home, run-scoped.Hooks ride the pi plugin transport. omp kept pi's layout: it autoloads
~/.omp/agent/extensions/termic.ts, transpiling in-process, with explicit legacy-pi compat. The extension is adapted to what actually moved:agent_end, suppressed while the event'swillContinueflag is set (omp's own docs: such an agent_end is not a terminal settle).tool_approval_requested(wrapper.ts fires it only when a tool genuinely needs a prompt, so yolo runs never false-attention) plustool_execution_startonask, the same edge omp's Warp bridge maps toquestion_asked. Answering clears it exactly, not on the next heartbeat.ctx.agent.kind(the muse trap).session_start/session_switch/agent_end: omp mints its own UUIDv7 at startup and the plugin's report is the resume binding.session_startfires at startup, so there is no capture backstop (omp's only lister is the interactive picker).OSC 9;omp: Completeper turn is anchored inBUILTIN_NOTIFY_IGNORE; itsStopped with errorbody is deliberately NOT ignored, because a died turn should ring.Usage is pulled cold.
omp usage --jsonprints structured per-provider limits (window id, durationMs, resetsAt, usedFraction).agent_usage_ompspawns it pointed at the entry'sPI_CONFIG_DIR, the same account resolution codex's command does, so omp gets the plan-usage chip andreports_usage(the pinned test is updated by name).Title signals are captured, not guessed. Live pty capture:
π ⠙ <dir>while working (braille frame after the brand mark),π > <dir>at the composer. Attention stays empty, honestly: default approvals never block, so no blocked title exists to pattern.Login store:
ConfigDir(PI_CONFIG_DIR). Measured both ways: an empty relocated root prints "No credentials found" while the real environment lists two signed-in accounts, and the credential is a SQLite row in the agent dir'sagent.db, not a keyring item. One caveat written down: omp'sPI_CODING_AGENT_DIRoutranksPI_CONFIG_DIRwhen a user sets both.make login-probe AGENT=omppasses.Docker:
KNOWN_SAFE_AGENTS+BASE_BUILTINS+ a Dockerfile line. The omp.sh installer drops the binary in$HOME/.local/bin(outside the mounted~/.omp), so the mount cannot shadow it — not grok's problem. Relocation is the claude shape: the var names the whole root, which is what Docker mounts.Rejected, with the record: omp ships a second channel,
WARP_CLI_AGENT_PROTOCOL_VERSION=1→ structured OSC 777 frames with stop / permission_request / question_asked. Zero-install detection was tempting, but it would mean a bespoke Rust parser and a second event contract beside a plugin transport that already covers the same states. docs/agent-hooks.md carries the reasoning so nobody re-investigates.Compatibility with the Cursor CLI commit
Rebased over 905152e (cursor as a built-in). Two enumerations cursor's commit left stale are brought current here, since omp's change touches them anyway: the README built-in list and the reset-all confirmation copy in both locales now name every built-in including cursor. No cursor behavior is otherwise touched.
Verification
cargo test: 1241 passing (includes the omp seeded-default test, the plugin-driven node test, and the guards that derive from the registry).npm test: 2751 passing (spawn composition for omp, footer sources, fallback agreement).make login-probe AGENT=omp: ok, no drift.make e2eon the rebased branch: 24/25 specs; git.e2e.ts fails for a local-path reason (the checkout path contains "github", so forge.rs's substring fallback resolves the fixture remote as github and reports cli-unauthed) — reproducible on main without this change.Manual verification
The maintainer ran this change by hand in the app before it was opened. Everything above the human pass came from live probes against omp 18.6.0 (startup, resume, approval modes, titles, extension events, usage JSON), captured and quoted where a claim is made.
🤖 Generated with Claude Code