Skip to content

Conversation

@shuv1337
Copy link
Collaborator

@shuv1337 shuv1337 commented Nov 21, 2025

Summary by CodeRabbit

  • New Features

    • Added provider connection flow with OAuth and API key authentication support.
    • Integrated terminal clipboard functionality with OSC 52 protocol support.
    • Added provider management API endpoints.
  • Bug Fixes

    • Improved error messaging and state cleanup handling.
  • Chores

    • Bumped package versions to 1.0.85.

✏️ Tip: You can customize this high-level summary in your review settings.

rekram1-node and others added 9 commits November 20, 2025 22:33
Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
Co-authored-by: rekram1-node <rekram1-node@users.noreply.github.com>
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
Co-authored-by: GitHub Action <action@github.com>
@coderabbitai
Copy link

coderabbitai bot commented Nov 21, 2025

Walkthrough

Version bumped from 1.0.83 to 1.0.85 across 18 packages. New provider authentication system added with OAuth and API key support. UI enhanced with provider connection dialog, updated prompts, and refined select styling. Sync context extended with provider metadata. New server endpoints for provider management and OAuth flows.

Changes

Cohort / File(s) Summary
Version Bumps (Routine)
packages/console/app/package.json, packages/console/core/package.json, packages/console/function/package.json, packages/console/mail/package.json, packages/desktop/package.json, packages/function/package.json, packages/opencode/package.json, packages/plugin/package.json, packages/sdk/js/package.json, packages/slack/package.json, packages/ui/package.json, packages/util/package.json, packages/web/package.json, sdks/vscode/package.json
Version field updated from 1.0.83 to 1.0.85 across all packages. No functional changes to dependencies or exports.
Zed Extension Metadata
packages/extensions/zed/extension.toml
Version and all platform-specific archive URLs (darwin-arm64, darwin-x64, linux-arm64, linux-x64, windows-x64) updated from v1.0.83 to v1.0.85.
Provider Authentication System
packages/opencode/src/provider/auth.ts
New ProviderAuth namespace with methods for OAuth and API key authentication. Exports Method and Authorization types, authorize/callback/api functions, and error types (OauthMissing, OauthCodeMissing, OauthCallbackFailed). Manages provider auth state and pending OAuth flows.
Provider Connection Dialog UI
packages/opencode/src/cli/cmd/tui/component/dialog-provider.tsx
New DialogProvider component with createDialogProviderOptions helper. Renders OAuth, code-based auth, and API key entry flows. Handles multi-method provider authentication with error handling and state transitions.
Dialog Model Updates
packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx
Refactored to display recent items and popular providers. Connected status drives model descriptions and availability. Integrated keybind config to trigger provider connection dialog.
Dialog Component Enhancements
packages/opencode/src/cli/cmd/tui/ui/dialog-prompt.tsx, packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx
DialogPrompt adds optional description and placeholder props; refactored dialog sizing and confirmation hints. DialogSelect increases padding, updates marker glyph from ● to ◆, adds current-state-aware styling, and adjusts category header and keybind line rendering.
Core Dialog Infrastructure
packages/opencode/src/cli/cmd/tui/ui/dialog.tsx
Added clipboard and toast utilities. New onMouseUp handler for selected text with OSC 52 terminal copy sequence and toast notification feedback.
Border and Prompt UI
packages/opencode/src/cli/cmd/tui/component/border.tsx, packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx
EmptyBorder constant introduced with SplitBorder refactored to spread it. Prompt component reworked with data-driven status object, dynamic status UI, agent/model display header, retry progress display, and Loader animation integration.
Main App UI
packages/opencode/src/cli/cmd/tui/app.tsx
Added Show import and DialogProviderList alias. New "Connect provider" menu item opens provider dialog. Agent tab hidden with Show component (when={false}).
Context & State Updates
packages/opencode/src/cli/cmd/tui/context/sync.tsx, packages/opencode/src/cli/cmd/tui/context/local.tsx, packages/opencode/src/cli/cmd/tui/context/sdk.tsx
Sync context adds provider_next and provider_auth fields with async bootstrap for provider data loading. Local context color() method now returns RGBA object via fromHex. SDK context removes console.log from event stream.
Session & Prompt Core
packages/opencode/src/session/prompt.ts, packages/opencode/src/cli/cmd/tui/routes/session/index.tsx
Session prompt disables AI SDK warnings and marks session as busy. Session UI refactored with options-based filename prompt API, UserMessage hover state, QUEUED badge/username repositioning, and WriteTool content string guard.
Server API Endpoints
packages/opencode/src/server/server.ts
Added POST /instance/dispose, GET /provider, GET /provider/auth, POST /provider/:id/oauth/authorize, and POST /provider/:id/oauth/callback endpoints with OpenAPI schemas and validators.
Error & Cleanup
packages/opencode/src/cli/error.ts, packages/opencode/src/project/instance.ts, packages/opencode/src/project/state.ts
ConfigDirectoryTypoError message updated to suggest renaming or removal. Instance.dispose clears cache entry. State disposal now clears full inner entries map instead of single entry.
Plugin & Provider Types
packages/plugin/src/index.ts, packages/opencode/src/provider/provider.ts, packages/opencode/src/provider/transform.ts
AuthHook and AuthOuathResult types exported. Hooks.auth refactored to reference AuthHook type. Unused imports (path, Global) removed from provider.ts. Transform options now enable thinkingConfig for gemini-3 models in opencode.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant UI as TUI<br/>(dialog-provider)
    participant Auth as ProviderAuth
    participant SDK as Opencode SDK
    participant Remote as Provider<br/>(e.g., GitHub)

    User->>UI: Select "Connect provider"
    UI->>UI: Render provider list
    User->>UI: Choose provider & method<br/>(OAuth or API key)
    
    alt OAuth Auto Flow
        UI->>Auth: authorize(providerID, method)
        Auth->>Remote: Initiate OAuth flow
        Remote-->>User: Display auth page
        User->>Remote: Authenticate & authorize
        Remote-->>Auth: OAuth callback
        Auth->>SDK: Store credentials
        SDK-->>UI: Success
        UI->>UI: Transition to DialogModel
    else OAuth Code Flow
        UI->>Auth: authorize(providerID, method)
        Auth-->>UI: Return auth URL + instructions
        UI->>UI: Display instructions & code input
        User->>User: Complete external auth
        User->>UI: Enter authorization code
        UI->>Auth: callback(providerID, method, code)
        Auth->>SDK: Store credentials
        SDK-->>UI: Success
        UI->>UI: Transition to DialogModel
    else API Key
        UI->>UI: Prompt for API key
        User->>UI: Enter API key
        UI->>Auth: api(providerID, key)
        Auth->>SDK: Store API key
        SDK-->>UI: Success
        UI->>UI: Transition to DialogModel
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Areas requiring extra attention:

  • Provider authentication flow (dialog-provider.tsx, auth.ts, sync.tsx): New multi-method OAuth/API system with state management and callback handling; verify error paths and OAuth state consistency
  • Prompt/Dialog refactoring (prompt/index.tsx, dialog-prompt.tsx): Significant UI restructuring with status-driven rendering and new state shape; ensure all status transitions render correctly
  • Sync context provider data loading (sync.tsx): New async bootstrap logic fetching provider metadata; verify timing and error handling during initialization
  • Server API integration (server.ts): New endpoints for provider auth; confirm request/response schemas align with client expectations
  • Session status marking (prompt.ts): New "busy" flag setting; ensure it doesn't interfere with existing session state machine
  • Border and spacing adjustments (dialog-select.tsx, border.tsx): Multiple padding/alignment changes; visually verify UI layout consistency

Poem

🐰 A version hops forward, OAuth flows bloom bright,
Connect your providers with options in sight,
Dialogs stack neatly, the border's just right,
Status now driving the prompt's perfect light,
Bootstrap brings data—the future looks right! 🌟

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive The title 'Merge Dev into Integration' is vague and generic, describing only the mechanical act of merging branches without conveying meaningful information about the actual changes (version bumps and feature additions). Use a more descriptive title that captures the primary changes, such as 'Release v1.0.85: version bumps and provider authentication flow' or 'Add provider auth system and bump versions to v1.0.85'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch dev

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

@shuv1337
Copy link
Collaborator Author

@CodeRabbit

@coderabbitai
Copy link

coderabbitai bot commented Nov 21, 2025

@kcrommett: Hello! I'm here to help. What would you like me to do? Here are some options:

  • **** - Review the changes in this PR
  • Summary - Provide a summary of the changes (if not already generated)
  • Answer questions - Ask me about specific files or changes
  • Generate tests - Help create unit tests for changes
  • Explain code - Clarify how certain parts work
  • Other - Any other code-related assistance

Just let me know what you need!

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

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

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.

ℹ️ 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 +205 to +209
onConfirm={async (value) => {
if (!value) return
sdk.client.auth.set({
path: {
id: props.providerID,

Choose a reason for hiding this comment

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

P1 Badge Await auth save before disposing and refreshing

The API-key connect path invokes sdk.client.auth.set(...) without awaiting the request, then immediately disposes the instance and reloads state. If the server rejects the credentials or the call fails, the promise rejection is unhandled and the UI still proceeds as if the provider were connected. Await the auth request and handle errors before tearing down and bootstrapping so connection state accurately reflects whether the key was accepted.

Useful? React with 👍 / 👎.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (8)
packages/opencode/src/session/prompt.ts (1)

52-53: Consider a more targeted approach to suppress AI SDK warnings.

Setting a global flag via globalThis affects the entire runtime and could hide legitimate warnings elsewhere. The @ts-ignore directive also bypasses type safety.

Consider one of these alternatives:

  • Configure the AI SDK properly through its configuration API (if available)
  • Suppress warnings at specific call sites where needed
  • Use a logging filter to exclude these warnings at the logging layer

If a global suppression is truly necessary, document why this broad approach is required and add a proper TypeScript type declaration instead of @ts-ignore:

-// @ts-ignore
-globalThis.AI_SDK_LOG_WARNINGS = false
+// Suppress AI SDK warnings globally to prevent noise in logs
+// See: https://github.com/vercel/ai/blob/main/packages/ai/src/logger/log-warnings.ts
+declare global {
+  var AI_SDK_LOG_WARNINGS: boolean
+}
+globalThis.AI_SDK_LOG_WARNINGS = false
packages/opencode/src/cli/cmd/tui/routes/session/index.tsx (1)

1011-1050: Retry status branch is effectively disabled

The condition when={props.last && status().type !== "idle" && false} will never be true, so the retry/status UI block is dead code. If this is intentional, consider removing or feature‑flagging the block to avoid confusion; if it’s temporary, a short TODO would help.

packages/opencode/src/cli/cmd/tui/app.tsx (1)

463-474: Agent tab UI is permanently disabled

Wrapping the agent tab block in <Show when={false}> effectively hides it while keeping the code around. If this is a long‑term disable, consider removing it or gating it behind a real feature flag/config value instead of a literal false to reduce dead code.

packages/opencode/src/cli/cmd/tui/ui/dialog.tsx (1)

6-7: Selection-aware dialog behavior and copy overlay are reasonable; consider aligning checks with App

The dialog now (a) avoids closing when renderer.getSelection() is truthy and (b) adds an overlay in DialogProvider that mirrors the App-level OSC52 + Clipboard copy behavior for selected text inside dialogs. Functionally this is good, but two small follow-ups would make it more robust:

  • In Dialog, you’re checking renderer.getSelection() rather than getSelection()?.getSelectedText()?.length > 0 like in App and the new overlay. If getSelection() can be non-null even when empty, this might block background clicks from closing the dialog. Aligning the condition with the text-length check would keep behavior consistent.
  • The OSC52 + copy + toast logic is now duplicated between App and DialogProvider. If this grows further, extracting a small helper (or hook) to share the behavior would reduce drift.

Also applies to: 17-23, 35-37, 132-134, 137-152

packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx (1)

24-41: Dedupe against recent models by ID instead of deep equality

Right now duplicates are filtered with:

filter((x) => !showRecent() || !local.model.recent().find((y) => isDeepEqual(y, x.value)))

This assumes elements from local.model.recent() are deeply equal to x.value (which is { providerID, modelID }). If recents ever carry extra metadata (timestamps, labels, etc.), isDeepEqual may stop matching and you’ll show duplicates.

Consider comparing by IDs only, e.g.:

filter(
  (x) =>
    !showRecent() ||
    !local.model.recent().some(
      (y) => y.providerID === x.value.providerID && y.modelID === x.value.modelID,
    ),
)

This is more robust to type evolution.

Also applies to: 86-87

packages/opencode/src/cli/cmd/tui/ui/dialog-prompt.tsx (1)

9-10: Invoke description accessor instead of passing the function

description is typed as () => JSX.Element but is rendered as {props.description}. In typical Solid patterns, you’d call the accessor so the description actually renders as JSX rather than a function value.

A safer pattern:

<box gap={1}>
  {props.description?.()}
  <textarea
    ...
  />
</box>

This avoids accidentally rendering a function or undefined and matches how other accessors are used.

Also applies to: 41-43

packages/opencode/src/cli/cmd/tui/component/dialog-provider.tsx (1)

37-45: Guard for empty auth-method lists and consider minimal error handling

The overall provider-connect flow (method selection → oauth/api handling → dispose/bootstrap → DialogModel) looks solid. A couple of small robustness points:

  • When sync.data.provider_auth[provider.id] is present but happens to be an empty array, methods.length is 0, index remains 0, and const method = methods[index] yields undefined, so neither the oauth nor api branches run. A simple early return would make this safer:

    const methods = sync.data.provider_auth[provider.id] ?? [{ type: "api", label: "API key" }]
    if (methods.length === 0) return
  • In AutoMethod, CodeMethod, and ApiMethod, SDK calls are awaited but not caught. If the client or network throws (rather than returning { error }), the dialog could get stuck. Wrapping those blocks in a try/catch that surfaces a minimal error message (or at least clears the dialog) would make the flow more resilient.

These are non-blocking but would harden the UX around provider auth.

Also applies to: 61-85, 111-127, 161-178, 205-219

packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx (1)

801-852: Retry banner and Loader logic are correct but could be factored for clarity

The bottom status bar—Loader plus the inline retry countdown and message—is wired correctly to status().type and uses onMount/onCleanup to manage its intervals safely. It’s quite a bit of reactive logic embedded in JSX, though.

For readability and easier testing, consider extracting the retry-specific block (the IIFE that defines retry, message, and seconds) into a small child component (e.g., <RetryStatus status={status} />). That would keep Prompt’s render tree simpler without changing behavior.

Also applies to: 880-913

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3538e54 and a67b616.

⛔ Files ignored due to path filters (4)
  • bun.lock is excluded by !**/*.lock
  • flake.lock is excluded by !**/*.lock
  • packages/sdk/js/src/gen/sdk.gen.ts is excluded by !**/gen/**
  • packages/sdk/js/src/gen/types.gen.ts is excluded by !**/gen/**
📒 Files selected for processing (36)
  • packages/console/app/package.json (1 hunks)
  • packages/console/core/package.json (1 hunks)
  • packages/console/function/package.json (1 hunks)
  • packages/console/mail/package.json (1 hunks)
  • packages/desktop/package.json (1 hunks)
  • packages/extensions/zed/extension.toml (2 hunks)
  • packages/function/package.json (1 hunks)
  • packages/opencode/package.json (1 hunks)
  • packages/opencode/src/cli/cmd/tui/app.tsx (3 hunks)
  • packages/opencode/src/cli/cmd/tui/component/border.tsx (1 hunks)
  • packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx (3 hunks)
  • packages/opencode/src/cli/cmd/tui/component/dialog-provider.tsx (1 hunks)
  • packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx (7 hunks)
  • packages/opencode/src/cli/cmd/tui/context/local.tsx (2 hunks)
  • packages/opencode/src/cli/cmd/tui/context/sdk.tsx (0 hunks)
  • packages/opencode/src/cli/cmd/tui/context/sync.tsx (6 hunks)
  • packages/opencode/src/cli/cmd/tui/routes/session/index.tsx (4 hunks)
  • packages/opencode/src/cli/cmd/tui/ui/dialog-prompt.tsx (3 hunks)
  • packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx (8 hunks)
  • packages/opencode/src/cli/cmd/tui/ui/dialog.tsx (4 hunks)
  • packages/opencode/src/cli/error.ts (1 hunks)
  • packages/opencode/src/project/instance.ts (1 hunks)
  • packages/opencode/src/project/state.ts (1 hunks)
  • packages/opencode/src/provider/auth.ts (1 hunks)
  • packages/opencode/src/provider/provider.ts (0 hunks)
  • packages/opencode/src/provider/transform.ts (1 hunks)
  • packages/opencode/src/server/server.ts (3 hunks)
  • packages/opencode/src/session/prompt.ts (2 hunks)
  • packages/plugin/package.json (1 hunks)
  • packages/plugin/src/index.ts (1 hunks)
  • packages/sdk/js/package.json (1 hunks)
  • packages/slack/package.json (1 hunks)
  • packages/ui/package.json (1 hunks)
  • packages/util/package.json (1 hunks)
  • packages/web/package.json (1 hunks)
  • sdks/vscode/package.json (1 hunks)
💤 Files with no reviewable changes (2)
  • packages/opencode/src/cli/cmd/tui/context/sdk.tsx
  • packages/opencode/src/provider/provider.ts
🔇 Additional comments (34)
packages/opencode/package.json (1)

3-3: LGTM! Standard version bump.

Version updated to 1.0.85 as part of the coordinated monorepo release.

packages/desktop/package.json (1)

3-3: LGTM! Version aligned with monorepo release.

Version updated to 1.0.85, consistent with the coordinated release across all packages.

packages/web/package.json (1)

4-4: LGTM! Version synchronized with release.

Version updated to 1.0.85 as part of the monorepo-wide release cycle.

sdks/vscode/package.json (1)

5-5: LGTM! VSCode extension version synchronized.

Version updated to 1.0.85, aligned with the monorepo release.

packages/sdk/js/package.json (1)

4-4: LGTM! SDK version updated.

Version updated to 1.0.85, consistent with the coordinated monorepo release.

packages/plugin/package.json (1)

4-4: LGTM! Plugin version synchronized.

Version updated to 1.0.85, aligned with the monorepo-wide release.

packages/opencode/src/project/state.ts (1)

60-60: Disposal tests do not exist - manual verification required.

The disposal logic is architecturally sound: disposal tasks are created with captured references before entries.clear() is called, ensuring async operations complete safely. However, no test files exist for State.dispose() or the State class in general (search returned zero test files in the project).

The review explicitly requests verification that this semantic change is covered by tests. Since no tests currently exist for disposal behavior, you should:

  • Confirm the semantic change from deleting a specific entry to clearing all entries is intentional
  • Determine if test coverage should be added before merging
  • Manually validate the disposal logic handles concurrent state access correctly during the disposal window
packages/slack/package.json (1)

3-3: LGTM! Version bump is consistent with the monorepo release.

The version update from 1.0.83 to 1.0.85 aligns with the broader version synchronization across packages in this PR.

packages/console/function/package.json (1)

3-3: LGTM! Version synchronized with monorepo release.

packages/opencode/src/project/instance.ts (1)

51-55: LGTM! Cache cleanup correctly added to instance disposal.

The addition of cache.delete(Instance.directory) ensures the cache entry is removed when an instance is disposed directly (not through disposeAll). In the disposeAll flow, these individual deletions are redundant with the final cache.clear(), but they cause no harm.

packages/opencode/src/cli/error.ts (1)

14-16: LGTM! Error message is more actionable.

The updated message provides clearer remediation steps by instructing users to rename or remove the invalid directory, rather than suggesting alternative usage.

packages/function/package.json (1)

3-3: LGTM! Version bump aligned with monorepo release.

packages/console/core/package.json (1)

4-4: LGTM! Version synchronized with monorepo release.

packages/util/package.json (1)

3-3: LGTM! Version bump consistent with the monorepo release.

packages/console/mail/package.json (1)

3-3: LGTM! Version synchronized with monorepo release.

packages/opencode/src/provider/transform.ts (1)

150-154: Verify the pattern matching for gemini-3 models.

The condition uses modelID.includes("gemini-3") which will match any model ID containing "gemini-3" as a substring (e.g., "gemini-30", "my-gemini-3-custom", "gemini-3.5").

If you need exact matching for specific model variants, consider a more precise pattern:

-if (providerID === "google" || (providerID === "opencode" && modelID.includes("gemini-3"))) {
+if (providerID === "google" || (providerID === "opencode" && /^gemini-3($|\.)/.test(modelID))) {

Or if the current substring matching is intentional to cover all gemini-3 variants, please add a comment explaining this behavior.

packages/ui/package.json (1)

3-3: LGTM!

Version bump is consistent with the broader PR pattern of updating multiple packages to 1.0.85.

packages/plugin/src/index.ts (2)

29-136: LGTM!

The extraction of authentication types into standalone exports (AuthHook and AuthOuathResult) improves code organization and makes the authentication contract explicit and reusable. The use of discriminated unions for OAuth vs API methods and auto vs code callbacks is a good design pattern.


144-144: LGTM!

The refactor to reference the new AuthHook type maintains the same functionality while improving type reusability.

packages/opencode/src/server/server.ts (2)

310-330: LGTM!

The instance disposal endpoint follows the established patterns with proper OpenAPI documentation and straightforward implementation.


1188-1319: LGTM! Provider management endpoints follow established patterns.

The new provider authentication and OAuth endpoints are well-structured with:

  • Consistent OpenAPI documentation
  • Proper request validation using Zod schemas
  • Error handling following existing patterns
  • Clear endpoint naming and organization

These endpoints integrate with the new ProviderAuth module to enable provider connection workflows.

packages/opencode/src/cli/cmd/tui/component/border.tsx (2)

1-13: LGTM!

Introducing EmptyBorder as a reusable constant is a good refactor that reduces duplication and makes it easier to create custom border configurations.


18-18: LGTM!

Using the spread operator with EmptyBorder reduces duplication and maintains the same functionality while making the code more maintainable.

packages/extensions/zed/extension.toml (1)

4-4: Release v1.0.85 artifacts verified and available.

The v1.0.85 release has been published with all required platform-specific archives present, including darwin-arm64, darwin-x64, linux-arm64, linux-x64, and windows-x64 variants. The version bump and archive URLs are valid.

packages/opencode/src/cli/cmd/tui/context/local.tsx (1)

93-98: No changes needed—the code's return types are consistent.

The theme properties (secondary, accent, success, warning, primary, error) are all typed as RGBA. The colors() memo returns an array of these RGBA values. Therefore, both return statements in the color() function return RGBA objects: line 95 via RGBA.fromHex() and line 97 via element access from the colors() array. There is no type inconsistency.

Likely an incorrect or invalid review comment.

packages/opencode/src/cli/cmd/tui/routes/session/index.tsx (3)

603-606: DialogPrompt.show options usage looks consistent with new API

Switching to DialogPrompt.show(dialog, "Export filename", { value: … }) matches the updated signature and preserves the previous default filename behavior.


907-909: UserMessage layout/hover and file badges look correct

The refactor to wrap content in a hoverable inner box, add SplitBorder.customBorderChars, per‑MIME badges, and username/QUEUED display is logically sound and doesn’t introduce obvious state or event‑handling issues.

Also applies to: 910-923, 924-942, 944-953


1314-1317: Guarding .split on write tool content avoids runtime errors

The createMemo that now checks typeof props.input.content === "string" before calling .split("\n") prevents crashes when non‑string content slips through and keeps downstream consumers (numbers()) safe.

packages/opencode/src/cli/cmd/tui/context/sync.tsx (1)

15-16: Provider bootstrap extensions integrate cleanly

Adding provider_next / provider_auth into the sync store and wiring them into bootstrap (blocking config.providers + provider.list, non‑blocking provider.auth) looks consistent with the existing pattern, including the guarded "partial" status transition and exposing bootstrap on the public API.

Also applies to: 33-35, 63-69, 245-285, 287-289, 343-344

packages/opencode/src/cli/cmd/tui/app.tsx (1)

5-5: Provider connect command wiring looks good

Importing Show and aliasing DialogProvider as DialogProviderList avoids naming clashes, and the new "Connect provider" command cleanly reuses the existing dialog.replace(() => <Dialog… />) pattern.

Also applies to: 9-9, 297-304

packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx (2)

205-207: Current-option state wiring and padding look correct

current is memoized per option and consistently passed down to Option, and the conditional paddingLeft={current() ? 1 : 3} aligns with using a dedicated current marker inside Option. No functional issues stand out here.

Also applies to: 220-231


160-189: Layout/padding and keybind/footer rendering changes are coherent

The increased outer padding, tighter scrollbox padding, category header indent, new ◆ marker, added text padding, and the bolded keybind titles all line up visually and semantically with the select’s state (active/current) without affecting behavior.

Also applies to: 197-201, 221-223, 240-248, 274-275, 283-287

packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx (1)

15-21: Connected/showRecent/keybind behavior is consistent

Using connected() to gate showRecent(), the “Popular providers” block, and the keybind title (“Connect provider” vs “More providers”) is internally consistent, and the flow into DialogProvider via dialog.replace looks correct.

Also applies to: 91-103, 106-116

packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx (1)

52-57: Status-aware highlight and header integration look good

Deriving status from sync.data.session_status and using highlight to drive the prompt border, agent/shell label, and global cursor color ties the visual state cleanly to session/agent state. The shell-mode toggling and agent/model header rendering all look consistent with that design.

Also applies to: 542-550, 576-591, 766-778

"build": "./script/generate-sitemap.ts && vinxi build && ../../opencode/script/schema.ts ./.output/public/config.json",
"start": "vinxi start",
"version": "1.0.83"
"version": "1.0.85"
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

Version updated, but placement is unusual.

The version is set to 1.0.85 within the scripts section rather than at the root level of package.json. This is non-standard - typically the version field should be a top-level property. Verify this is intentional for this console app package.

Run the following script to check if this is a pattern used elsewhere or an anomaly:


🏁 Script executed:

#!/bin/bash
# Check for version field placement across package.json files

echo "=== Checking version field locations in package.json files ==="
fd -t f '^package\.json$' -x sh -c 'echo "File: {}"; cat {} | jq -r "if .scripts.version then \"  Version in scripts: \" + .scripts.version else \"  Version at root: \" + (.version // \"none\") end"'

Length of output: 1194


Move version field to root level in packages/console/app/package.json.

Verification confirms this is an anomaly—all other 17 package.json files in the codebase place the version field at the root level. The version field should be moved from the scripts section to the package root to maintain consistency with the established project convention.

🤖 Prompt for AI Agents
In packages/console/app/package.json around line 10, the "version": "1.0.85"
entry is incorrectly located inside the "scripts" section; move that "version"
field out of "scripts" and place it at the top-level of the package.json (same
level as "name", "private" etc.), then remove the version key from the scripts
object so scripts contains only script entries and the package root contains the
version.

Comment on lines +66 to 71
DialogPrompt.show = (dialog: DialogContext, title: string, options?: Omit<DialogPromptProps, "title">) => {
return new Promise<string | null>((resolve) => {
dialog.replace(
() => (
<DialogPrompt
title={title}
value={value}
onConfirm={(value) => resolve(value)}
onCancel={() => resolve(null)}
/>
<DialogPrompt title={title} {...options} onConfirm={(value) => resolve(value)} onCancel={() => resolve(null)} />
),
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

DialogPrompt.show ignores caller onConfirm/onCancel and never closes the dialog

Two concerns here:

  1. Caller handlers are silently dropped
    options is typed as Omit<DialogPromptProps, "title">, which includes onConfirm and onCancel, but they are never invoked. You overwrite them with:

    <DialogPrompt
      title={title}
      {...options}
      onConfirm={(value) => resolve(value)}
      onCancel={() => resolve(null)}
    />

    Any options.onConfirm / options.onCancel provided by callers will be ignored, which is surprising given the type.

  2. The dialog is not dismissed on confirm/cancel
    onConfirm/onCancel only resolve the Promise; they do not call dialog.clear() or otherwise close the dialog. Unless every call site now explicitly closes the dialog after await DialogPrompt.show(...), existing usages will leave the prompt onscreen after “confirm”.

If the intent is to keep the old “show-and-close” semantics while allowing extra options, consider:

DialogPrompt.show = (dialog: DialogContext, title: string, options: Omit<DialogPromptProps, "title"> = {}) => {
  return new Promise<string | null>((resolve) => {
    const handleConfirm = (value: string) => {
      options.onConfirm?.(value)
      resolve(value)
      dialog.clear()
    }
    const handleCancel = () => {
      options.onCancel?.()
      resolve(null)
      dialog.clear()
    }

    dialog.replace(
      () => (
        <DialogPrompt
          title={title}
          {...options}
          onConfirm={handleConfirm}
          onCancel={handleCancel}
        />
      ),
      handleCancel,
    )
  })
}

Alternatively, if callers are now responsible for closing the dialog, it’d be less misleading to remove onConfirm/onCancel from the options type to avoid suggesting they’ll be honored.


🏁 Script executed:

#!/bin/bash
# Audit all call sites of DialogPrompt.show to ensure they still behave correctly
# after the semantic change (no auto-close, handlers ignored).

rg -n "DialogPrompt\.show\(" -S

Length of output: 148



---

🏁 Script executed:

```shell
#!/bin/bash
# Examine the call site context at line 604
cat packages/opencode/src/cli/cmd/tui/routes/session/index.tsx | sed -n '600,620p'

Length of output: 754


🏁 Script executed:

#!/bin/bash
# Find the DialogPrompt implementation
find . -name "dialog-prompt.tsx" -type f

Length of output: 56


🏁 Script executed:

#!/bin/bash
# Get the full implementation of DialogPrompt.show and surrounding code
cat packages/opencode/src/cli/cmd/tui/ui/dialog-prompt.tsx | sed -n '50,90p'

Length of output: 798


🏁 Script executed:

#!/bin/bash
# Search for dialog.clear() calls to understand dialog lifecycle
rg -n "dialog\.clear\(\)" -A 2 -B 2

Length of output: 21311



---

🏁 Script executed:

```shell
#!/bin/bash
# Find DialogPromptProps type definition
cat packages/opencode/src/cli/cmd/tui/ui/dialog-prompt.tsx | head -50

Length of output: 1442


🏁 Script executed:

#!/bin/bash
# Examine broader context around the call at line 604
# to see if dialog.clear() is called after the await
cat packages/opencode/src/cli/cmd/tui/routes/session/index.tsx | sed -n '600,640p'

Length of output: 1348


🏁 Script executed:

#!/bin/bash
# Search for any cleanup after DialogPrompt.show() call
rg -n "DialogPrompt\.show\(" -A 20 -B 2

Length of output: 2268



---

🏁 Script executed:

```shell
#!/bin/bash
# Check if there are any other usages of DialogPrompt (not .show method)
# to understand if the component is used elsewhere
rg -n "DialogPrompt" packages/opencode/src/cli/cmd/tui/ui/dialog-prompt.tsx -A 5 -B 5 | head -100

Length of output: 1052


🏁 Script executed:

#!/bin/bash
# Check git log to see if DialogPrompt.show was recently added or modified
cd packages/opencode && git log --oneline -20 --follow -- src/cli/cmd/tui/ui/dialog-prompt.tsx 2>/dev/null || echo "Git history not available"

Length of output: 150


DialogPrompt.show has misleading API: handlers are ignored and dialog is not auto-closed

Verification confirms both concerns:

  1. Handlers are silently dropped
    DialogPromptProps includes optional onConfirm and onCancel, but DialogPrompt.show unconditionally overwrites them (line 70). Any caller-provided handlers are ignored, making the type signature misleading.

  2. Dialog lacks auto-close semantics
    Unlike DialogConfirm and DialogAlert which call dialog.clear() inside their handlers, DialogPrompt.show does not. The only call site (line 604) survives because it explicitly calls dialog.clear() at line 629, but this is fragile and inconsistent with the established dialog pattern.

The suggested fix (invoke options handlers before resolving, and call dialog.clear()) aligns the semantics with other dialog components and removes the misleading type contract.

🤖 Prompt for AI Agents
In packages/opencode/src/cli/cmd/tui/ui/dialog-prompt.tsx around lines 66 to 71,
DialogPrompt.show currently overwrites any caller-provided onConfirm/onCancel
handlers and never clears the dialog; modify the implementation so it calls the
provided handlers (if present) before resolving and then calls dialog.clear() to
close the dialog, i.e., invoke options.onConfirm(value) when confirming and
options.onCancel() when cancelling (safely guarded), then call dialog.clear()
and finally resolve the promise with the value (or null).

Comment on lines +10 to +41
export namespace ProviderAuth {
const state = Instance.state(async () => {
const methods = pipe(
await Plugin.list(),
filter((x) => x.auth?.provider !== undefined),
map((x) => [x.auth!.provider, x.auth!] as const),
fromEntries(),
)
return { methods, pending: {} as Record<string, AuthOuathResult> }
})

export const Method = z
.object({
type: z.union([z.literal("oauth"), z.literal("api")]),
label: z.string(),
})
.meta({
ref: "ProviderAuthMethod",
})
export type Method = z.infer<typeof Method>

export async function methods() {
const s = await state().then((x) => x.methods)
return mapValues(s, (x) =>
x.methods.map(
(y): Method => ({
type: y.type,
label: y.label,
}),
),
)
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Harden provider auth against invalid input and clean up pending state

Two areas are fragile here:

  1. authorize(): unchecked provider/method lookups

auth is obtained as s.methods[input.providerID] and const method = auth.methods[input.method] without guards. For an unknown providerID or an out‑of‑range method index, this will throw a generic TypeError instead of returning a structured error.

Consider validating both lookups and returning a named error (or at least a controlled failure) instead of crashing, e.g.:

   export const authorize = fn(
@@
-    async (input): Promise<Authorization | undefined> => {
-      const auth = await state().then((s) => s.methods[input.providerID])
-      const method = auth.methods[input.method]
-      if (method.type === "oauth") {
+    async (input): Promise<Authorization | undefined> => {
+      const s = await state()
+      const auth = s.methods[input.providerID]
+      if (!auth) {
+        throw new OauthMissing({ providerID: input.providerID })
+      }
+      const method = auth.methods[input.method]
+      if (!method) {
+        throw new OauthMissing({ providerID: input.providerID })
+      }
+      if (method.type === "oauth") {
         const result = await method.authorize()
-        await state().then((s) => (s.pending[input.providerID] = result))
+        s.pending[input.providerID] = result
         return {
           url: result.url,
           method: result.method,
           instructions: result.instructions,
         }
       }
     },
   )
  1. callback(): pending entries are never cleared

After a callback (success or failure), pending[input.providerID] remains set. That allows reusing the same pending entry across multiple callbacks and keeps unnecessary state around.

You can ensure single‑use semantics and avoid stale state by deleting the entry in a finally block:

   export const callback = fn(
@@
-    async (input) => {
-      const match = await state().then((s) => s.pending[input.providerID])
-      if (!match) throw new OauthMissing({ providerID: input.providerID })
-      let result
-
-      if (match.method === "code") {
-        if (!input.code) throw new OauthCodeMissing({ providerID: input.providerID })
-        result = await match.callback(input.code)
-      }
-
-      if (match.method === "auto") {
-        result = await match.callback()
-      }
-
-      if (result?.type === "success") {
-        if ("key" in result) {
-          await Auth.set(input.providerID, {
-            type: "api",
-            key: result.key,
-          })
-        }
-        if ("refresh" in result) {
-          await Auth.set(input.providerID, {
-            type: "oauth",
-            access: result.access,
-            refresh: result.refresh,
-            expires: result.expires,
-          })
-        }
-        return
-      }
-
-      throw new OauthCallbackFailed({})
-    },
+    async (input) => {
+      const s = await state()
+      const match = s.pending[input.providerID]
+      if (!match) throw new OauthMissing({ providerID: input.providerID })
+      let result
+
+      try {
+        if (match.method === "code") {
+          if (!input.code) throw new OauthCodeMissing({ providerID: input.providerID })
+          result = await match.callback(input.code)
+        }
+
+        if (match.method === "auto") {
+          result = await match.callback()
+        }
+
+        if (result?.type === "success") {
+          if ("key" in result) {
+            await Auth.set(input.providerID, {
+              type: "api",
+              key: result.key,
+            })
+          }
+          if ("refresh" in result) {
+            await Auth.set(input.providerID, {
+              type: "oauth",
+              access: result.access,
+              refresh: result.refresh,
+              expires: result.expires,
+            })
+          }
+          return
+        }
+
+        throw new OauthCallbackFailed({})
+      } finally {
+        delete s.pending[input.providerID]
+      }
+    },
   )

These changes make the API more robust against bad inputs and keep the in‑memory auth state bounded and easier to reason about.

Also applies to: 43-53, 54-114, 116-127, 129-143

@shuv1337 shuv1337 merged commit 4423bc5 into integration Nov 21, 2025
7 of 9 checks passed
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.

6 participants