Skip to content

Providers settings overhaul: runtime cards + fully-managed OpenCode subscriptions, Kimi, custom providers#852

Merged
arul28 merged 8 commits into
mainfrom
ade/something-discuss-fist-dont-chnaegs-ecd10215
Jul 18, 2026
Merged

Providers settings overhaul: runtime cards + fully-managed OpenCode subscriptions, Kimi, custom providers#852
arul28 merged 8 commits into
mainfrom
ade/something-discuss-fist-dont-chnaegs-ecd10215

Conversation

@arul28

@arul28 arul28 commented Jul 17, 2026

Copy link
Copy Markdown
Owner

Summary

  • Restructure Settings → Providers into Coding Agent cards (Claude Code / Codex / Cursor / Droid, each stating its auth story) plus one "OpenCode — Universal Model Access" group containing everything OpenCode-powered.
  • Subscription connections through OpenCode, fully ADE-managed: dynamic rows from OpenCode's auth API (GitHub Copilot, ChatGPT Plus/Pro, GitLab, Poe, DigitalOcean), device-code modal, credentials seeded into ADE's isolated managed OpenCode dir via the server auth API (/provider/auth, /oauth/authorize, PUT /auth/{id}) — never the user's personal opencode state. Claude Pro/Max via OpenCode intentionally excluded (Anthropic ToS).
  • Kimi/Moonshot first-class: Moonshot AI key card (MOONSHOT_API_KEY, id moonshotai) + Kimi for Coding membership entry (kimi-for-coding).
  • Long-tail fix: provider inventory now probes on settings mount and persists stale-while-revalidate, so all ~160 OpenCode providers render on first open (searchable chip cloud) instead of never.
  • Custom providers & model slugs: ai.customProviders / ai.customModelSlugs config injected into the managed OpenCode config; replace-semantics merge so removals persist.
  • models.dev freshness: 6h refresh now re-applies pricing/registry enrichment; last-synced shown in the group header with manual refresh.
  • Diagnostics: typed OpenCode launch failures (not-installed / Gatekeeper-quarantined with xattr fix / bad signature / port conflict / timeout).
  • Model picker: configuration-gated models render dimmed with a sign-in affordance deep-linking to settings.
  • Parity: web-client adapter (incl. flow-scoped OAuth event drain over personalChats.streamEvents), TUI Kimi aliases, adeActions registry (web/remote reachability); iOS verified no-change.

Test plan

  • /quality dual-review (no Blockers; all safe fixes applied + re-review ALL-CLEAR) and /test passes run pre-ship.
  • Suites green: opencode services, ai services, config, settings renderer, ModelPicker, webclient adapter, agentChatService (673), tuiClient (925); desktop shard 1/8 (1,080); tsc clean both packages.
  • Live-verified against opencode v1.14.50: OAuth authorize returns real device codes (Copilot + ChatGPT headless), moonshotai/kimi-for-coding present in provider catalog.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added OpenCode authentication, OAuth connection, provider key management, and model catalog refresh.
    • Added support for custom providers and custom model slugs.
    • Added persistent provider discovery and clearer OpenCode launch diagnostics.
    • Improved provider settings with search, status indicators, copy actions, and subscription connections.
    • Added MoonshotAI/Kimi provider recognition across model pickers and settings.
    • Models requiring configuration are now visibly disabled and guide users to sign-in.
  • Bug Fixes

    • Provider information remains available from cached data when live discovery fails.
    • Improved handling of custom provider configuration updates and removals.
  • Documentation

    • Updated coding-agent, OpenCode setup, and troubleshooting guidance.

Greptile Summary

This PR overhauls provider settings and OpenCode-powered model access. The main changes are:

  • New provider settings cards for coding agents and OpenCode-managed subscriptions.
  • OpenCode OAuth, provider key, inventory, and models.dev refresh APIs.
  • Kimi/Moonshot provider support across settings, model pickers, and TUI metadata.
  • Custom provider and custom model slug configuration for managed OpenCode.
  • Stale-while-revalidate provider inventory and clearer OpenCode runtime diagnostics.

Confidence Score: 4/5

One contained bug affects runtime-bound OpenCode auth flows.

Most service and UI changes are coherent, but the preload bridge passes OpenCode auth arguments in a shape the ADE action service does not consume.

apps/desktop/src/preload/preload.ts

T-Rex T-Rex Logs

What T-Rex did

  • T-Rex reproduced unwrap runtime action args behavior by running a focused Vitest harness against the real ADE action registry path for opencodeOAuthStart, confirming that a nested payload with an args object fails at the non-empty string validation before reaching the OpenCode auth service mock, while a direct payload passes validation and reaches the mock.
  • T-Rex reviewed the provider UI test setup and results, noting that three test files passed and 55 tests passed in total, the Vite server reported ready, and the UI capture harness and artifacts were prepared but encountered Playwright timeouts during real renderer capture.

View all artifacts

T-Rex Ran code and verified through T-Rex

Important Files Changed

Filename Overview
apps/desktop/src/preload/preload.ts Adds OpenCode auth APIs to the preload bridge, but wraps runtime action arguments so remote/local-runtime OpenCode auth calls fail before reaching the service.
apps/desktop/src/main/services/adeActions/registry.ts Exposes OpenCode auth and models.dev refresh actions and relays OAuth status events through runtime event buffers.
apps/desktop/src/main/services/opencode/openCodeAuthService.ts Adds managed OpenCode auth method listing, OAuth polling, provider key seeding, durable key deletion, and status listeners.
apps/desktop/src/main/services/opencode/openCodeInventory.ts Persists successful OpenCode provider inventory and reloads it for stale-while-revalidate settings display.
apps/desktop/src/renderer/components/settings/ProvidersSection.tsx Overhauls provider settings UI with runtime cards, OpenCode subscription/key flows, provider search, custom providers, and refresh controls.
apps/desktop/src/main/services/ai/modelsDevService.ts Moves models.dev enrichment into the service so initial, periodic, and manual refreshes update pricing and registry metadata.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant Settings as Providers Settings UI
participant Preload as Electron Preload / Web Adapter
participant Actions as ADE AI Actions
participant Auth as OpenCode Auth Service
participant Server as Managed OpenCode Server
participant Store as ADE API Key Store

Settings->>Preload: opencodeOAuthStart / setProviderKey
Preload->>Actions: "ai.opencode* runtime action"
Actions->>Auth: startOAuth / setProviderKey
Auth->>Server: "/provider/auth, /oauth/authorize, /auth/{id}"
Auth-->>Settings: OAuth status events
Auth->>Store: mirror or delete durable API key
Auth->>Server: provider.list polling / inventory refresh
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant Settings as Providers Settings UI
participant Preload as Electron Preload / Web Adapter
participant Actions as ADE AI Actions
participant Auth as OpenCode Auth Service
participant Server as Managed OpenCode Server
participant Store as ADE API Key Store

Settings->>Preload: opencodeOAuthStart / setProviderKey
Preload->>Actions: "ai.opencode* runtime action"
Actions->>Auth: startOAuth / setProviderKey
Auth->>Server: "/provider/auth, /oauth/authorize, /auth/{id}"
Auth-->>Settings: OAuth status events
Auth->>Store: mirror or delete durable API key
Auth->>Server: provider.list polling / inventory refresh
Loading

Comments Outside Diff (1)

  1. apps/desktop/src/main/services/opencode/openCodeInventory.ts, line 147-151 (link)

    P1 Include custom provider config
    fingerprintOpenCodeConfig omits ai.customProviders and ai.customModelSlugs, but buildOpenCodeMergedConfig injects both into the OpenCode provider map. A non-forced inventory read within the TTL can reuse a cache result built before the custom provider edit, so newly added or removed custom models stay stale until the TTL expires or a forced refresh happens.

    Artifacts

    Repro: focused Vitest test for stale custom provider inventory cache reuse

    • Contains supporting evidence from the run (text/typescript; charset=utf-8).

    Repro: verbose Vitest output showing stale second result and provider.list called once

    • Keeps the command output available without making the summary code-heavy.

    View artifacts

    T-Rex Ran code and verified through T-Rex

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: apps/desktop/src/main/services/opencode/openCodeInventory.ts
    Line: 147-151
    
    Comment:
    **Include custom provider config**
    `fingerprintOpenCodeConfig` omits `ai.customProviders` and `ai.customModelSlugs`, but `buildOpenCodeMergedConfig` injects both into the OpenCode provider map. A non-forced inventory read within the TTL can reuse a cache result built before the custom provider edit, so newly added or removed custom models stay stale until the TTL expires or a forced refresh happens.
    
    How can I resolve this? If you propose a fix, please make it concise.

    Fix in Claude Code

Fix All in Claude Code

Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
apps/desktop/src/preload/preload.ts:3930-3931
**Unwrap runtime action args**
The runtime action bridge already passes this object as `toolArgs.args`, and `buildAiDomainService.opencodeOAuthStart` reads `args.providerId` directly. Sending `{ args }` makes remote/local runtime-bound settings calls arrive as `{ args: { providerId, ... } }`, so `requireNonEmptyString(args?.providerId, "providerId")` throws and OpenCode OAuth cannot start outside the direct IPC fallback. Pass `args` directly here and in the adjacent OpenCode key/cancel calls.

Reviews (5): Last reviewed commit: "ship: iteration 5 — fix key persistence,..." | Re-trigger Greptile

Greptile also left 1 inline comment on this PR.

arul28 and others added 3 commits July 17, 2026 18:15
…, Kimi, custom providers

Restructure Settings → Providers into Coding Agent cards plus a single
"OpenCode — Universal Model Access" group. Seed subscription OAuth
(GitHub Copilot, ChatGPT, GitLab, Poe, DigitalOcean) into ADE's isolated
managed OpenCode dir via the server auth API with a device-code modal;
persist provider inventory stale-while-revalidate so the long tail
renders on first open; add first-class Moonshot AI key + Kimi for Coding
membership entries; inject custom providers and model slugs into the
managed OpenCode config; re-apply models.dev enrichment on refresh with
last-synced visibility; type OpenCode launch failures (incl. Gatekeeper
quarantine fix); dim configuration-gated models in the picker with a
settings deep-link; wire web-client adapter + TUI Kimi parity.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replace semantics for ai.customProviders/customModelSlugs (union made
removals impossible); surface both lists on the AI status payload and
seed the settings form from config so saves are authoritative full-list
writes; fall back to persisted OpenCode inventory when a forced probe
fails; cancel an active OAuth flow on modal unmount; dedupe the web
adapter's OpenCode types against shared/types (fixing a latent
array-shape divergence) and the modelsDev refresh duplication; drop a
dead inventory parameter.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Pin the quality-gate fixes as named regressions (mergeAiConfig replace
semantics; stale persisted-inventory fallback on failed forced probes),
fold the inventory-persistence test fragment into the module suite, and
bring internal docs (settings/config-schema/chat/architecture) in line
with the shipped providers overhaul.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 17, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
ade Ignored Ignored Preview Jul 18, 2026 2:17am

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

OpenCode access and provider configuration

Layer / File(s) Summary
Configuration, catalog, and inventory state
apps/desktop/src/shared/types/config.ts, apps/desktop/src/main/services/config/..., apps/desktop/src/main/services/ai/..., apps/desktop/src/main/services/opencode/openCodeInventory.ts
Adds OpenCode auth/configuration types, custom provider and model-slug persistence, models.dev freshness tracking, and persisted provider inventory fallback.
OpenCode authentication and runtime services
apps/desktop/src/main/services/opencode/*
Implements OAuth polling, provider-key synchronization, custom provider injection, macOS quarantine detection, and typed server launch diagnostics.
Desktop action and API transport
apps/desktop/src/main/services/adeActions/registry.ts, apps/desktop/src/main/services/ipc/registerIpc.ts, apps/desktop/src/preload/*, apps/desktop/src/renderer/webclient/adapter/*
Exposes OpenCode authentication, status events, provider-key updates, and models.dev refresh through desktop actions, IPC, preload, and the web adapter.
Provider settings and OAuth interface
apps/desktop/src/renderer/components/settings/*
Reworks provider settings with OpenCode subscriptions, API keys, catalog freshness, custom providers, model slugs, Kimi credentials, and OAuth connection UI.
Provider identity and model selection
apps/ade-cli/src/tuiClient/*, apps/desktop/src/main/services/ai/{apiKeyStore,authDetector}.ts, apps/desktop/src/renderer/components/shared/*
Adds Kimi/Moonshot aliases and logos, Moonshot API-key verification, and configuration-gated model picker behavior.
Provider setup documentation
configuration/ai-providers.mdx
Reorganizes setup, OpenCode usage, advanced configuration, model requirements, and troubleshooting guidance.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

  • arul28/ADE#144 — Both modify OpenCode inventory probing and persistence-related paths.
  • arul28/ADE#219 — Both change AI status and OpenCode inventory refresh behavior.

Suggested labels: desktop, docs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 30.38% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is specific and matches the main change set: a providers settings overhaul with OpenCode subscriptions, Kimi, and custom providers.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ade/something-discuss-fist-dont-chnaegs-ecd10215

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.

@arul28 arul28 changed the title opencode-providers-and-subscriptions -> Primary Providers settings overhaul: runtime cards + fully-managed OpenCode subscriptions, Kimi, custom providers Jul 17, 2026
@arul28

arul28 commented Jul 17, 2026

Copy link
Copy Markdown
Owner Author

@copilot review but do not make fixes

Comment thread apps/desktop/src/main/services/ipc/registerIpc.ts
…e in openCodeAuthService), address Greptile #3606770990 (ai.getStatus drops customProviders/customModelSlugs)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@arul28

arul28 commented Jul 17, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e07297d739

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +875 to +878
if (!id || slugs.length === 0) {
setError("A custom provider needs an id and at least one model slug.");
return;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Require a base URL before saving custom providers

When adding a custom provider from Advanced, an entry with an id and model slugs but an empty Base URL passes this validation and then gets a success notice. That provider is unusable because addCustomProviders() skips custom providers with missing baseURL, so the saved entry never appears in the inventory/model picker and the user gets no actionable error. Include draft.baseUrl.trim() in the required fields before calling updateConfig.

Useful? React with 👍 / 👎.

…e saving custom providers)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@arul28

arul28 commented Jul 17, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c077777400

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/desktop/src/preload/preload.ts Outdated
_event: Electron.IpcRendererEvent,
payload: OpenCodeOAuthStatusEvent,
) => cb(payload);
ipcRenderer.on(IPC.aiOpencodeOAuthStatus, listener);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Route remote OAuth status events to the modal

When the active project is a remote runtime, opencodeOAuthStart is routed through callProjectRuntimeActionOr, and the remote service relays status changes into the runtime event buffer (registry.ts pushes kind: "opencodeOAuthStatus"). This subscriber only listens to the local Electron IPC channel, so in desktop sessions backed by a remote project the connect modal never sees connected, timeout, or failed and remains stuck in the waiting state even though the remote flow completed.

Useful? React with 👍 / 👎.

Comment on lines +139 to +143
const result = await window.ade.ai.opencodeOAuthStart({
providerId,
methodIndex,
inputs: Object.keys(filteredInputs).length ? filteredInputs : undefined,
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Honor cancels while OAuth start is pending

If the user clicks Cancel while the modal is still in the "Opening…" state, this awaited start call may not have registered an active backend flow yet, so opencodeOAuthCancel is a no-op; once the await later resolves, the component is already closing but the backend poller/browser flow remains active until it connects or times out. This affects slow OpenCode server launch or authorize calls and leaves a cancelled sign-in running in the background.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 10

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
apps/desktop/src/main/services/opencode/openCodeInventory.ts (1)

240-243: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Do not route aliases to model IDs OpenCode did not advertise.

Without the removed available-ID set, an advertised claude-sonnet-4-6 entry becomes a descriptor routed to claude-sonnet-5, even when that target is absent from provider.models. Restore the availability check or preserve mid as the OpenCode routing ID while canonicalizing only ADE's display identity.

Also applies to: 436-436

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/desktop/src/main/services/opencode/openCodeInventory.ts` around lines
240 - 243, Update normalizeOpenCodeProviderModel to prevent aliases from routing
to unavailable OpenCode model IDs: restore the advertised model-ID availability
check, or retain mid as the routing ID while only canonicalizing ADE’s display
identity. Ensure entries such as claude-sonnet-4-6 continue routing to the
provider model actually advertised in provider.models.
apps/desktop/src/renderer/components/settings/ProvidersSection.tsx (2)

719-733: 🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

Delete the OpenCode credential as well as the ADE-stored key.

Keys saved through this screen are also persisted through setOpencodeProviderKey, but deletion only calls deleteApiKey. The credential can therefore remain active in OpenCode after the user selects Delete.

Add a service/IPC/preload operation that clears the OpenCode provider credential and invoke it before reporting removal.

As per coding guidelines, “Keep IPC contracts, preload types, shared types, and renderer usage in sync whenever an interface changes.”

As per path instructions, prefer fixing the underlying service or shared type rather than adding a renderer-only workaround.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/desktop/src/renderer/components/settings/ProvidersSection.tsx` around
lines 719 - 733, Extend the existing service/IPC/preload contract used by
setOpencodeProviderKey with an operation to clear the provider credential,
keeping shared types and preload exposure synchronized. Update deleteApiKey to
invoke this OpenCode credential-clearing operation before setNotice reports
removal, while preserving the existing ADE key deletion and refresh flow.

Sources: Coding guidelines, Path instructions


697-713: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Handle OpenCode key-registration failures before reporting success.

setOpencodeProviderKey returns { ok, error }, but Line 700 ignores the result. The web adapter’s offline fallback returns ok: false, so the UI currently reports that the key was saved even when OpenCode rejected it.

Proposed fix
 if (options?.alsoOpenCode) {
-  try {
-    await window.ade.ai.setOpencodeProviderKey({ providerId: provider, key: trimmed });
-  } catch {
-    // OpenCode registration is best-effort; the stored key still applies.
-  }
+  const result = await window.ade.ai.setOpencodeProviderKey({
+    providerId: provider,
+    key: trimmed,
+  });
+  if (!result.ok) {
+    throw new Error(result.error || "OpenCode rejected the provider key.");
+  }
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/desktop/src/renderer/components/settings/ProvidersSection.tsx` around
lines 697 - 713, Update the OpenCode registration flow in the key-save handler
around setOpencodeProviderKey to inspect its { ok, error } result, including the
offline fallback failure. If registration fails, surface the returned error and
stop before invalidating caches, clearing verification state, showing the
success notice, canceling editing, or refreshing status; only execute those
success actions when registration succeeds.
🧹 Nitpick comments (1)
configuration/ai-providers.mdx (1)

54-56: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Capitalize "ID".

For correctness in technical writing, "ID" is typically capitalized.

💡 Proposed change
   <Step title="More providers">
-    Beyond the first-class rows, OpenCode reaches **160+ more providers**. Search the chip cloud, then paste a key against any provider by its id. The catalog is cached between sessions, so it renders the moment Settings opens.
+    Beyond the first-class rows, OpenCode reaches **160+ more providers**. Search the chip cloud, then paste a key against any provider by its ID. The catalog is cached between sessions, so it renders the moment Settings opens.
   </Step>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@configuration/ai-providers.mdx` around lines 54 - 56, Update the “More
providers” Step text in the provider documentation to capitalize “id” as “ID,”
preserving the surrounding wording and formatting.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/desktop/src/main/services/adeActions/registry.ts`:
- Around line 2052-2067: Update ensureOpenCodeOAuthStatusRelayBridge to retain
the unsubscribe function returned by addOpenCodeOAuthStatusListener and register
it with runtime.dispose. Ensure teardown removes the listener and prevents
further eventBuffer pushes into the disposed runtime, while preserving the
existing WeakSet guard and error handling.

In `@apps/desktop/src/main/services/opencode/openCodeAuthService.ts`:
- Around line 273-278: Update the key-storage flow around hooks.storeApiKey so
durable persistence is required for success: validate providerId and key before
storing, and when the mirror fails, do not return { ok: true }; instead roll
back the accepted server key where supported or return an explicit
failure/partial-failure result while preserving the existing warning log.
- Around line 71-85: Replace the OAuth polling setInterval flow with a
serialized loop that awaits each provider.list request before delaying for the
next two-second poll. Add a per-request AbortController/deadline, pass its
signal through the client request, and abort timed-out requests; retain the
existing five-minute overall polling limit and cleanup behavior. Update
listConnectedProviders and the polling logic together so no overlapping or
indefinitely pending requests remain.
- Around line 87-97: Validate the URL in openExternal before calling Electron’s
shell.openExternal, allowing only https URLs and explicitly permitted loopback
http URLs (localhost or loopback IPs). Reject file, custom-protocol, and other
schemes without attempting to open them, while preserving the existing
runtime-optional Electron handling and caller-visible fallback behavior.

In `@apps/desktop/src/main/services/opencode/openCodeInventory.ts`:
- Around line 85-94: Validate the parsed value in readPersistedInventoryFile
before memoizing or returning it. Check each inventory entry against the
expected persisted shape, including that its providers value is an array, and
discard incompatible entries or data so loadPersistedOpenCodeInventory always
receives valid inventory.

In `@apps/desktop/src/main/services/opencode/openCodeServerManager.ts`:
- Around line 539-557: Update openCodeServerManager.ts lines 539-557 so
bad-signature is returned only when explicit code-signature or
developer-verification evidence exists and probeOpenCodeBinaryQuarantine returns
a conclusive result; classify generic kills and unknown probe results as
unknown. Update openCodeBinaryManager.ts lines 188-203 so a successful
quarantine-attribute read returns quarantined, confirmed attribute absence
returns clean, and all other probe failures return unknown.

In `@apps/desktop/src/renderer/components/settings/OAuthConnectModal.tsx`:
- Around line 62-73: Update the OAuth method selection flow around methodIndex
and the inputs state so prompt defaults are rebuilt whenever the selected method
changes. When method changes, initialize each select prompt to its first option
value and other prompts to an empty string, ensuring submission uses the same
defaults displayed by the UI; preserve any intended existing input behavior
where applicable.
- Around line 139-146: In the OAuth start flow around setStartResult, call
openUrlInAdeBrowser with result.url immediately after receiving the response so
the client renderer opens the returned OAuth URL. Do not await this helper,
since it returns void; preserve the existing flowActiveRef and waiting-phase
updates.

In
`@apps/desktop/src/renderer/components/shared/ModelPicker/ModelPickerContent.tsx`:
- Around line 113-120: The auth-only filtering path must exclude models that
require configuration, even when their provider family is otherwise ready.
Update filterAvailable() to reject modelRequiresConfiguration(m) when authOnly
is enabled, and add a regression case covering this configured-state mismatch
while preserving existing non-auth-only behavior.

In `@apps/desktop/src/renderer/webclient/adapter/__tests__/adapter.test.ts`:
- Around line 919-928: Update the test fixtures in the OpenCode command-results
setup to match the shared response contracts: make authMethods.methods use the
expected array shape, and set refreshModelsDev.lastFetchedAt to a numeric
timestamp or null rather than an ISO string. Preserve the existing test behavior
while sourcing values consistent with the shared OpenCode types.

---

Outside diff comments:
In `@apps/desktop/src/main/services/opencode/openCodeInventory.ts`:
- Around line 240-243: Update normalizeOpenCodeProviderModel to prevent aliases
from routing to unavailable OpenCode model IDs: restore the advertised model-ID
availability check, or retain mid as the routing ID while only canonicalizing
ADE’s display identity. Ensure entries such as claude-sonnet-4-6 continue
routing to the provider model actually advertised in provider.models.

In `@apps/desktop/src/renderer/components/settings/ProvidersSection.tsx`:
- Around line 719-733: Extend the existing service/IPC/preload contract used by
setOpencodeProviderKey with an operation to clear the provider credential,
keeping shared types and preload exposure synchronized. Update deleteApiKey to
invoke this OpenCode credential-clearing operation before setNotice reports
removal, while preserving the existing ADE key deletion and refresh flow.
- Around line 697-713: Update the OpenCode registration flow in the key-save
handler around setOpencodeProviderKey to inspect its { ok, error } result,
including the offline fallback failure. If registration fails, surface the
returned error and stop before invalidating caches, clearing verification state,
showing the success notice, canceling editing, or refreshing status; only
execute those success actions when registration succeeds.

---

Nitpick comments:
In `@configuration/ai-providers.mdx`:
- Around line 54-56: Update the “More providers” Step text in the provider
documentation to capitalize “id” as “ID,” preserving the surrounding wording and
formatting.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 604a8189-5f80-45fa-990a-ae9a08219ce8

📥 Commits

Reviewing files that changed from the base of the PR and between 3dbab90 and c077777.

⛔ Files ignored due to path filters (4)
  • docs/ARCHITECTURE.md is excluded by !docs/**
  • docs/features/chat/README.md is excluded by !docs/**
  • docs/features/onboarding-and-settings/README.md is excluded by !docs/**
  • docs/features/onboarding-and-settings/configuration-schema.md is excluded by !docs/**
📒 Files selected for processing (37)
  • apps/ade-cli/src/tuiClient/components/ModelPicker/ModelPickerPane.tsx
  • apps/ade-cli/src/tuiClient/providerMetadata.test.ts
  • apps/ade-cli/src/tuiClient/providerMetadata.ts
  • apps/desktop/src/main/services/adeActions/registry.ts
  • apps/desktop/src/main/services/ai/aiIntegrationService.test.ts
  • apps/desktop/src/main/services/ai/aiIntegrationService.ts
  • apps/desktop/src/main/services/ai/aiSettingsStatus.ts
  • apps/desktop/src/main/services/ai/apiKeyStore.ts
  • apps/desktop/src/main/services/ai/authDetector.ts
  • apps/desktop/src/main/services/ai/modelsDevService.test.ts
  • apps/desktop/src/main/services/ai/modelsDevService.ts
  • apps/desktop/src/main/services/config/projectConfigService.test.ts
  • apps/desktop/src/main/services/config/projectConfigService.ts
  • apps/desktop/src/main/services/ipc/registerIpc.ts
  • apps/desktop/src/main/services/opencode/openCodeAuthService.test.ts
  • apps/desktop/src/main/services/opencode/openCodeAuthService.ts
  • apps/desktop/src/main/services/opencode/openCodeBinaryManager.ts
  • apps/desktop/src/main/services/opencode/openCodeInventory.test.ts
  • apps/desktop/src/main/services/opencode/openCodeInventory.ts
  • apps/desktop/src/main/services/opencode/openCodeRuntime.test.ts
  • apps/desktop/src/main/services/opencode/openCodeRuntime.ts
  • apps/desktop/src/main/services/opencode/openCodeServerManager.test.ts
  • apps/desktop/src/main/services/opencode/openCodeServerManager.ts
  • apps/desktop/src/preload/global.d.ts
  • apps/desktop/src/preload/preload.ts
  • apps/desktop/src/renderer/browserMock.ts
  • apps/desktop/src/renderer/components/settings/OAuthConnectModal.tsx
  • apps/desktop/src/renderer/components/settings/ProvidersSection.test.tsx
  • apps/desktop/src/renderer/components/settings/ProvidersSection.tsx
  • apps/desktop/src/renderer/components/shared/ModelPicker/ModelPicker.test.tsx
  • apps/desktop/src/renderer/components/shared/ModelPicker/ModelPickerContent.tsx
  • apps/desktop/src/renderer/components/shared/ProviderLogos.tsx
  • apps/desktop/src/renderer/webclient/adapter/__tests__/adapter.test.ts
  • apps/desktop/src/renderer/webclient/adapter/misc.ts
  • apps/desktop/src/shared/ipc.ts
  • apps/desktop/src/shared/types/config.ts
  • configuration/ai-providers.mdx

Comment thread apps/desktop/src/main/services/adeActions/registry.ts
Comment thread apps/desktop/src/main/services/opencode/openCodeAuthService.ts
Comment thread apps/desktop/src/main/services/opencode/openCodeAuthService.ts Outdated
Comment thread apps/desktop/src/main/services/opencode/openCodeAuthService.ts
Comment thread apps/desktop/src/main/services/opencode/openCodeInventory.ts
Comment on lines +539 to +557
if (
process.platform === "darwin"
&& (
code === "EPERM"
|| lower.includes("operation not permitted")
|| lower.includes("killed")
|| lower.includes("code signature")
|| lower.includes("developer cannot be verified")
)
) {
const quarantine = probeOpenCodeBinaryQuarantine(binaryPath);
if (quarantine === "quarantined") {
return {
kind: "quarantined",
binaryPath,
fixCommand: `xattr -d com.apple.quarantine "${binaryPath}"`,
};
}
return { kind: "bad-signature", binaryPath };

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Keep inconclusive macOS launch evidence classified as unknown.

The quarantine probe and launch classifier currently collapse uncertain outcomes into a definitive bad-signature diagnosis.

  • apps/desktop/src/main/services/opencode/openCodeServerManager.ts#L539-L557: require explicit signature evidence and a conclusive probe before returning bad-signature; classify generic kills or unknown probes as unknown.
  • apps/desktop/src/main/services/opencode/openCodeBinaryManager.ts#L188-L203: return quarantined on a successful attribute read, clean only for confirmed absence, and unknown for other failures.
🧰 Tools
🪛 ast-grep (0.44.1)

[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { spawn, spawnSync, type ChildProcess } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)

📍 Affects 2 files
  • apps/desktop/src/main/services/opencode/openCodeServerManager.ts#L539-L557 (this comment)
  • apps/desktop/src/main/services/opencode/openCodeBinaryManager.ts#L188-L203
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/desktop/src/main/services/opencode/openCodeServerManager.ts` around
lines 539 - 557, Update openCodeServerManager.ts lines 539-557 so bad-signature
is returned only when explicit code-signature or developer-verification evidence
exists and probeOpenCodeBinaryQuarantine returns a conclusive result; classify
generic kills and unknown probe results as unknown. Update
openCodeBinaryManager.ts lines 188-203 so a successful quarantine-attribute read
returns quarantined, confirmed attribute absence returns clean, and all other
probe failures return unknown.

Comment thread apps/desktop/src/renderer/components/settings/OAuthConnectModal.tsx Outdated
Comment thread apps/desktop/src/renderer/components/settings/OAuthConnectModal.tsx
Comment thread apps/desktop/src/renderer/webclient/adapter/__tests__/adapter.test.ts Outdated
@arul28

arul28 commented Jul 18, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 720e412b5b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

void window.ade.ai.opencodeOAuthCancel({ providerId }).catch(() => undefined);
return;
}
openUrlInAdeBrowser(result.url);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Enforce the OAuth URL allow-list in the renderer

When OpenCode returns a URL, startOAuth already tries to open it through isAllowedOAuthExternalUrl, which rejects non-HTTPS/non-local HTTP URLs; however this renderer call opens the returned result.url again through the generic ADE browser path. In the case where a provider/OpenCode bug returns something like http://example.com/... or file://..., the main-process allow-list is bypassed and the renderer still opens it, so the OAuth URL should be validated with the same policy here or opened only by the backend.

Useful? React with 👍 / 👎.

if (!res.ok) {
return { ok: false, error: `OpenCode DELETE /auth failed (${res.status}).` };
}
return { ok: true };

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Remove the durable key when clearing OpenCode auth

setProviderKey mirrors accepted provider keys into ADE's API key store, and buildProviderConfig() re-injects those stored keys into every new managed OpenCode server. This clear path only DELETEs the credential from the currently leased server and returns success, so any caller of the new ai.clearOpencodeProviderKey action outside the desktop two-step menu will see the key come back on the next server launch/status refresh unless the durable store is deleted here as well.

Useful? React with 👍 / 👎.

Comment thread apps/desktop/src/main/services/opencode/openCodeAuthService.ts
@arul28

arul28 commented Jul 18, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0e65e74a10

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

const trimmed = editValue.trim();
if (!trimmed) return;

setError(null);
setNotice(null);
try {
await window.ade.ai.storeApiKey(provider, trimmed);
if (options?.alsoOpenCode) {
const result = await window.ade.ai.setOpencodeProviderKey({ providerId: provider, key: trimmed });

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Delay storing keys until OpenCode accepts them

When alsoOpenCode is true, the key has already been written to ADE's durable key store before this OpenCode registration call runs. If this call returns ok: false or throws, the catch path leaves the editor open and shows an error, but the key remains stored and can be injected into the next managed OpenCode launch anyway. Since setOpencodeProviderKey already mirrors the key after a successful PUT /auth/{id}, call it instead of the earlier store write for OpenCode-backed saves, or roll the store write back on failure.

Useful? React with 👍 / 👎.

await window.ade.ai.opencodeOAuthCancel({ providerId }).catch(() => undefined);
throw new Error("OpenCode returned an unsafe OAuth URL.");
}
openUrlInAdeBrowser(result.url);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Avoid opening OAuth URLs twice

For desktop OAuth flows that return a URL, openCodeAuthService.startOAuth already opens the same URL via Electron shell.openExternal before resolving. Calling openUrlInAdeBrowser here then opens a second auth page in ADE's browser, so a normal sign-in produces two browser surfaces for the same flow. Choose either the main-process auto-open or the renderer browser open, but not both.

Useful? React with 👍 / 👎.

Comment thread apps/desktop/src/renderer/webclient/adapter/misc.ts Outdated
@arul28

arul28 commented Jul 18, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fdcc350a4b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

try {
const connected = await hooks.listConnectedProviders(lease.url, deps.projectRoot, controller.signal);
if (activeFlows.get(providerId) !== flow) return;
if (connected.includes(providerId)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Accept OAuth provider IDs returned by the flow

When an OAuth method connects a provider under a different id than the one used to start the flow, this poll never observes success because it only checks connected.includes(providerId). OpenCode auth methods can do this for enterprise variants such as GitHub Copilot Enterprise, where the completed credential is saved under a separate provider id; the user finishes login, but ADE keeps waiting until timeout and never refreshes inventory. Please detect the connected provider id produced by the flow, or compare against the connected set delta from before the flow started, instead of only the original id.

Useful? React with 👍 / 👎.

opencodeProviders: opencodeInventory.providers,
opencodeProvidersStale: opencodeInventory.stale,
modelsDevLastFetchedAt: getModelsDevLastFetchedAt(),
customProviders: effectiveConfig?.ai?.customProviders,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Use scoped custom-provider lists for saves

When .ade/local.json contains ai.customProviders, this effective value is the local list because config merging gives local custom providers replacement precedence. The Settings save path uses this status field as the full-list write source for ai.updateConfig, which writes shared config, so adding a provider can disappear on the next refresh while also copying local-only provider definitions into .ade/config.json. Please expose the shared list/scope separately or save back to local when local owns this field.

Useful? React with 👍 / 👎.

Comment on lines +3930 to +3931
callProjectRuntimeActionOr("ai", "opencodeOAuthStart", { args }, () =>
ipcRenderer.invoke(IPC.aiOpencodeOAuthStart, args),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Unwrap runtime action args
The runtime action bridge already passes this object as toolArgs.args, and buildAiDomainService.opencodeOAuthStart reads args.providerId directly. Sending { args } makes remote/local runtime-bound settings calls arrive as { args: { providerId, ... } }, so requireNonEmptyString(args?.providerId, "providerId") throws and OpenCode OAuth cannot start outside the direct IPC fallback. Pass args directly here and in the adjacent OpenCode key/cancel calls.

Artifacts

Repro: focused Vitest harness for nested versus direct OpenCode OAuth args

  • Contains supporting evidence from the run (text/typescript; charset=utf-8).

Repro: verbose Vitest output showing nested args providerId validation error and direct args service path success

  • Keeps the command output available without making the summary code-heavy.

View artifacts

T-Rex Ran code and verified through T-Rex

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src/preload/preload.ts
Line: 3930-3931

Comment:
**Unwrap runtime action args**
The runtime action bridge already passes this object as `toolArgs.args`, and `buildAiDomainService.opencodeOAuthStart` reads `args.providerId` directly. Sending `{ args }` makes remote/local runtime-bound settings calls arrive as `{ args: { providerId, ... } }`, so `requireNonEmptyString(args?.providerId, "providerId")` throws and OpenCode OAuth cannot start outside the direct IPC fallback. Pass `args` directly here and in the adjacent OpenCode key/cancel calls.

How can I resolve this? If you propose a fix, please make it concise.

Fix in Claude Code

@arul28
arul28 merged commit fdf734f into main Jul 18, 2026
35 checks passed
@arul28
arul28 deleted the ade/something-discuss-fist-dont-chnaegs-ecd10215 branch July 18, 2026 03:47
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.

1 participant