Skip to content

feat(providers): add Oh My Pi support - #7763

Closed
watzon wants to merge 1 commit into
pingdotgg:mainfrom
watzon:feat/omp-provider
Closed

feat(providers): add Oh My Pi support#7763
watzon wants to merge 1 commit into
pingdotgg:mainfrom
watzon:feat/omp-provider

Conversation

@watzon

@watzon watzon commented Aug 21, 2026

Copy link
Copy Markdown

What Changed

This PR adds Oh My Pi (OMP) as a built-in provider across the typed contracts, server, web client, desktop wrapper, and mobile client.

  • Discover the OMP model catalog and preserve each upstream provider as subProvider.
  • Start, resume, cancel, and supervise OMP ACP sessions through the existing provider runtime.
  • Map T3 permission modes, forward image attachments, and use OMP for generated titles and source-control text.
  • Show labels such as Oh My Pi · Moonshot in web and mobile model pickers.
  • Probe OMP without loading extensions. Extension-only models remain available through Custom models.
  • Document installation, behavior, and current OMP 17.4.0 limits.

Why

T3 Code cannot currently use an OMP subscription. OMP can expose the same model name through several upstream providers. A model name without its upstream provider is ambiguous and can select a different service, price, or account than the user intended.

The implementation keeps provider-specific behavior at the adapter boundary and reuses the existing ACP and provider conventions. The contribution guide points feature proposals to Ideas, but GitHub currently reports that Discussions are disabled for this repository.

UI Changes

Before, duplicate Kimi K2.6 entries only showed Oh My Pi.

Before: duplicate OMP models without upstream provider labels

After, each entry shows its upstream provider.

After: OMP models labeled Fireworks, Moonshot, OpenCode Go, and OpenRouter

Verification

  • 105 focused tests passed across contracts, OMP discovery, ACP translation, session handling, text generation, web settings, and mobile model options.
  • Contracts, server, web, and mobile type checks passed.
  • Targeted lint, formatting, and git diff --check passed.
  • A real-client pass against OMP 17.4.0 confirmed provider health, model discovery, provider labels, and the new-thread flow.

Current OMP Limits

  • T3 does not show Plan mode because OMP ACP does not expose that mode.
  • Provider rollback reports unsupported because OMP ACP does not expose rollback.
  • Some supervised shell or destructive edit calls can show both OMP approval prompts. OMP 17.4.0 applies an ACP client gate and a native approval gate.

Checklist

  • This PR is focused on one provider. It is larger than 1,000 lines because provider parity spans contracts, server behavior, web, mobile, docs, and focused tests.
  • I explained what changed and why.
  • I included before and after screenshots for the UI change.
  • This change has no motion or animation, so a video is not applicable.

Built with GPT-5.6 Sol in T3 Code through the Codex harness.


Note

Medium Risk
Adds a new agent runtime that spawns omp acp and maps T3 permission modes to OMP approval, including auto-approve of edit gates. The provider is off by default, but session, permission, and process-spawn paths are security-sensitive.

Overview
Adds Oh My Pi (omp) as a built-in provider so T3 can drive an existing OMP install over ACP. It is off by default; users enable it in settings after omp setup.

The server discovers the OMP model catalog (min version 17.4.0), keeps each upstream as subProvider (e.g. Moonshot vs OpenRouter), and runs sessions through a new ACP adapter. T3 permission modes map to OMP approval (always-ask / write / yolo); launch-arg approval flags cannot override that. Text generation uses a locked-down ACP spawn (no tools/extensions). Plan mode is hidden and provider rollback is unsupported.

Web and mobile pickers, icons, drafts, and settings include OMP. Model rows show Oh My Pi · {upstream}. ACP runtime also parses usage and available-command updates used by OMP.

Reviewed by Cursor Bugbot for commit 17532ea. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Add Oh My Pi (omp) provider across server, web, mobile, and contracts

  • Adds a full built-in omp driver: OmpDriver.ts, OmpAdapter.ts, OmpProvider.ts, and ACP support in OmpAcpSupport.ts with session management, model catalog parsing, version gating, and maintenance resolvers (npm, Homebrew, native omp update)
  • Adds OmpTextGeneration.ts for commit messages, PR content, branch names, and thread titles via an ACP runtime with 180s timeout and structured JSON output
  • Registers omp in contracts (settings.ts, model.ts), builtInDrivers.ts, web provider picker/settings, mobile model options/icons, and docs
  • Extends ACP runtime model with UsageUpdated and AvailableCommandsUpdated events; Cursor and Grok adapters updated to ignore them
  • omp is disabled by default; provider picker shows a new badge
  • Behavioral Change: parseSessionUpdateEvent now emits UsageUpdated/AvailableCommandsUpdated events — existing adapters that did not handle these now explicitly no-op them (Cursor, Grok); any out-of-tree ACP consumers must handle or ignore the new event variants
📊 Macroscope summarized 17532ea. 35 files reviewed, 9 issues evaluated, 0 issues filtered, 8 comments posted

🗂️ Filtered Issues

@coderabbitai

coderabbitai Bot commented Aug 21, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 68ce5226-a973-4a4b-b921-cad63fc01e16

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XXL 1,000+ changed lines (additions + deletions). labels Aug 21, 2026
Semaphore.make(1).pipe(
Effect.map((semaphore) => {
const next = new Map(current);
next.set(threadId, semaphore);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium Layers/OmpAdapter.ts:215

threadLocksRef retains a semaphore and the threadId key for every thread ever passed to getThreadSemaphore, so stopping a session does not release that per-thread state and a long-lived server grows its memory usage without bound. Remove unused lock entries when a thread is stopped, while preserving entries still needed by concurrent operations.

🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/provider/Layers/OmpAdapter.ts around line 215:

`threadLocksRef` retains a semaphore and the `threadId` key for every thread ever passed to `getThreadSemaphore`, so stopping a session does not release that per-thread state and a long-lived server grows its memory usage without bound. Remove unused lock entries when a thread is stopped, while preserving entries still needed by concurrent operations.

Comment on lines +214 to +215
const properties = request.requestedSchema.properties ?? {};
const entries = Object.entries(properties).filter(([key]) => !key.endsWith("__other"));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 High acp/OmpAcpSupport.ts:214

ompElicitationQuestions emits a UserInputQuestion for every schema property, so optional ACP fields are treated as mandatory by the web/mobile answer builders and users cannot submit the form without inventing values or cancelling. Filter the emitted entries by requestedSchema.required (or otherwise expose optionality to clients).

-  const properties = request.requestedSchema.properties ?? {};
-  const entries = Object.entries(properties).filter(([key]) => !key.endsWith("__other"));
+  const properties = request.requestedSchema.properties ?? {};
+  const required = new Set(request.requestedSchema.required ?? []);
+  const entries = Object.entries(properties).filter(
+    ([key]) => !key.endsWith("__other") && required.has(key),
+  );
🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/provider/acp/OmpAcpSupport.ts around lines 214-215:

`ompElicitationQuestions` emits a `UserInputQuestion` for every schema property, so optional ACP fields are treated as mandatory by the web/mobile answer builders and users cannot submit the form without inventing values or cancelling. Filter the emitted entries by `requestedSchema.required` (or otherwise expose optionality to clients).


function isOmpNativeCommandPath(commandPath: string): boolean {
const normalized = normalizeCommandPath(commandPath);
return normalized.endsWith("/omp") || normalized.endsWith("/omp.exe");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium Drivers/OmpDriver.ts:47

isOmpNativeCommandPath classifies every resolved executable whose basename is omp or omp.exe as native, so /opt/homebrew/bin/omp and .../node_modules/.bin/omp select omp update instead of the Homebrew or npm/pnpm/bun update action. This prevents package-managed OMP installations from being upgraded or reinstalled through their package manager; restrict native detection to genuine native-install paths or ensure package-manager detection takes precedence.

🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/provider/Drivers/OmpDriver.ts around line 47:

`isOmpNativeCommandPath` classifies every resolved executable whose basename is `omp` or `omp.exe` as native, so `/opt/homebrew/bin/omp` and `.../node_modules/.bin/omp` select `omp update` instead of the Homebrew or npm/pnpm/bun update action. This prevents package-managed OMP installations from being upgraded or reinstalled through their package manager; restrict native detection to genuine native-install paths or ensure package-manager detection takes precedence.

}),
),
).pipe(
Effect.catch((cause) =>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 High Layers/OmpAdapter.ts:689

A single notification-processing failure terminates ctx.notificationFiber while the session remains ready, so all subsequent ACP notifications are silently dropped. Because Effect.catch wraps Stream.runDrain, an error from one mapEffect callback (such as makeEventStamp() failing) exits the whole drain instead of recovering per event. Move recovery inside the per-event processing or mark the session failed when the consumer exits.

🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/provider/Layers/OmpAdapter.ts around line 689:

A single notification-processing failure terminates `ctx.notificationFiber` while the session remains `ready`, so all subsequent ACP notifications are silently dropped. Because `Effect.catch` wraps `Stream.runDrain`, an error from one `mapEffect` callback (such as `makeEventStamp()` failing) exits the whole drain instead of recovering per event. Move recovery inside the per-event processing or mark the session failed when the consumer exits.

}): Effect.Effect<void, E> {
return Effect.gen(function* () {
const model = input.model?.trim();
if (model && model !== "default") {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 High acp/OmpAcpSupport.ts:586

Selecting "default" after a concrete model leaves the ACP runtime on the previous model, while sendTurn reports "default", so the recorded model and actual model diverge. The model !== "default" guard skips setModel; remove that special case so switching back to the provider default resets the runtime model.

🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/provider/acp/OmpAcpSupport.ts around line 586:

Selecting `"default"` after a concrete model leaves the ACP runtime on the previous model, while `sendTurn` reports `"default"`, so the recorded model and actual model diverge. The `model !== "default"` guard skips `setModel`; remove that special case so switching back to the provider default resets the runtime model.

{ label: "No", description: "Do not confirm this action." },
]
: choices.length > 0
? choices.map((choice) => ({

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 High acp/OmpAcpSupport.ts:225

Selecting an elicitation option whose title differs from its const drops or misroutes the answer instead of sending the selected const value. ompElicitationQuestions exposes choice.title as the UI answer, but buildOmpElicitationContent validates against choice.const; preserve or restore the title-to-const mapping before normalization and __other handling.

🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/provider/acp/OmpAcpSupport.ts around line 225:

Selecting an elicitation option whose `title` differs from its `const` drops or misroutes the answer instead of sending the selected `const` value. `ompElicitationQuestions` exposes `choice.title` as the UI answer, but `buildOmpElicitationContent` validates against `choice.const`; preserve or restore the `title`-to-`const` mapping before normalization and `__other` handling.

mapError: ({ cause, method }) =>
mapAcpToAdapterError(PROVIDER, input.threadId, method, cause),
});
ctx.activeTurnId = turnId;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium Layers/OmpAdapter.ts:752

sendTurn publishes turn.started and leaves ctx.activeTurnId set even when the turn is rejected for empty input, an invalid attachment, an attachment read failure, or a failed ctx.acp.prompt; no matching turn.completed is emitted, so the session remains stuck on a failed turn. Move activation and turn.started until after prompt validation, and clear or complete the active turn when later processing fails.

🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/provider/Layers/OmpAdapter.ts around line 752:

`sendTurn` publishes `turn.started` and leaves `ctx.activeTurnId` set even when the turn is rejected for empty input, an invalid attachment, an attachment read failure, or a failed `ctx.acp.prompt`; no matching `turn.completed` is emitted, so the session remains stuck on a failed turn. Move activation and `turn.started` until after prompt validation, and clear or complete the active turn when later processing fails.

if (exact) return exact;
}
return normalizedAliases
.map((alias) => modes.find((mode) => normalizeModeSearchText(mode).includes(alias)))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 High acp/OmpAcpSupport.ts:85

requestedOmpModeId can select a plan mode instead of an implementation mode when a plan mode’s description mentions an alias such as code, switching the session into plan mode unexpectedly. findModeByAliases uses unrestricted substring matching over the normalized concatenated text; match whole tokens and exclude plan modes when resolving implementation modes.

🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/provider/acp/OmpAcpSupport.ts around line 85:

`requestedOmpModeId` can select a plan mode instead of an implementation mode when a plan mode’s description mentions an alias such as `code`, switching the session into plan mode unexpectedly. `findModeByAliases` uses unrestricted substring matching over the normalized concatenated text; match whole tokens and exclude plan modes when resolving implementation modes.

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want fixes drafted automatically? Bugbot Autofix can create code changes for findings. A team admin can enable Autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 17532ea. Configure here.

}
if (normalized !== undefined) content[question.key] = normalized;
}
return content;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Elicitation answers ignore choice consts

Medium Severity

OMP elicitation options expose oneOf/anyOf titles to the UI, and T3 returns those labels as answers, but buildOmpElicitationContent / normalizeElicitationAnswer only match against const values. When title and const differ, a valid pick can be treated as a custom __other answer or dropped from multi-select payloads, so OMP receives the wrong form content.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 17532ea. Configure here.

@macroscopeapp

macroscopeapp Bot commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This PR adds a complete new provider integration (Oh My Pi) with ~4000 lines of new code across driver, adapter, ACP support, text generation, and UI components. Multiple High severity findings identify bugs in elicitation handling, notification processing, and model selection that need to be addressed. New provider integrations with this scope warrant careful review.

Not approved because:

  • 8 blocking correctness issues found at or above your repo's Minimum Blocking Severity

Adjust the Minimum Blocking Severity for this repo — including turning it Off — in Settings. You can add or adjust custom eligibility rules. Learn more.

@HernandoR

Copy link
Copy Markdown

potential duplicate with #6160

@watzon watzon closed this Aug 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL 1,000+ changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants