Skip to content

Local model chat: shared helpers, banner merge, permission reset#132

Merged
arul28 merged 2 commits intolocal-model-fixesfrom
cursor/-bc-eec1a734-3fc8-4296-88d0-07e2c5ad7e7e-4925
Apr 5, 2026
Merged

Local model chat: shared helpers, banner merge, permission reset#132
arul28 merged 2 commits intolocal-model-fixesfrom
cursor/-bc-eec1a734-3fc8-4296-88d0-07e2c5ad7e7e-4925

Conversation

@arul28
Copy link
Copy Markdown
Owner

@arul28 arul28 commented Apr 4, 2026

Summary

Follow-up to the local AI runtime work: addresses the audit items for local model chat UX and code hygiene.

Changes

  • Shared helpers — Export LOCAL_PROVIDER_LABELS, parseLocalProviderFromModelId, getLocalModelIdTail, and isLocalProviderFamily from modelRegistry; dedupe labels in agentChatService / aiIntegrationService.
  • getModelDescriptorForPermissionMode — When getModelById is undefined (e.g. ollama/auto), still returns a guarded local descriptor so harness / unified permission logic matches main (addresses Codex P1 on PR review).
  • Chat runtime banners — Merged CLI + local Runtime status card when both apply.
  • Unified permission mode — Single setUnifiedPermissionMode in applyModelSelectionSnapshot (CodeRabbit nitpick). Reset + recommendation combined as (nextRec ?? surfaceDefault).
  • UnifiedModelSelector — Unknown local placeholder uses correct sdkModelId tail.

Post-review (commit be3a62e6)

  • Wire getModelDescriptorForPermissionMode through prevModelDescRef, buildModelSelectionSnapshot, and createSession harness path.
  • Unit tests in modelRegistry.test.ts for ollama/auto vs known local ids.

Validation

  • npm --prefix apps/desktop run typecheck / lint
  • vitestmodelRegistry.test.ts, AgentChatPane.test.ts, modelOptions.test.ts, ProvidersSection.test.tsx
  • npm --prefix apps/mcp-server run typecheck
Open in Web Open in Cursor 

Summary by CodeRabbit

  • New Features

    • Improved runtime status display with merged banner system for local and CLI runtime information.
    • Enhanced model switching logic with intelligent permission mode handling.
  • Bug Fixes

    • Refined local runtime connection success messages for better clarity.
  • Refactor

    • Consolidated local provider utilities and labels into shared model registry for consistent handling across the application.

Export LOCAL_PROVIDER_LABELS and local model id parsers from modelRegistry;
dedupe labels in agentChatService and aiIntegrationService. Merge CLI and
local runtime banners in AgentChatPane, reset unified permission mode when
harness recommendations change, and fix local placeholder sdkModelId. Settings
and model options use the shared helpers.

Co-authored-by: Arul Sharma <arul28@users.noreply.github.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 4, 2026

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

Project Deployment Actions Updated (UTC)
ade Ready Ready Preview, Comment Apr 5, 2026 2:31am

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 4, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

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

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: ea14efee-eb0a-4be1-a7c0-3eb14dddf389

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

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This change consolidates local model provider parsing and labeling logic from multiple desktop components into a shared modelRegistry module by exporting previously private constants and adding new helper functions. Components and services now import LOCAL_PROVIDER_LABELS, parseLocalProviderFromModelId(), and getLocalModelIdTail() instead of maintaining local implementations. AgentChatPane also gains unified permission mode handling for model switches and a refactored runtime status banner system.

Changes

Cohort / File(s) Summary
Shared Registry Export Expansion
apps/desktop/src/shared/modelRegistry.ts
Exports LOCAL_PROVIDER_LABELS constant and isLocalProviderFamily() function; adds new exported helpers parseLocalProviderFromModelId() and getLocalModelIdTail() for standardized local provider parsing and tail extraction.
Service Layer Consolidation
apps/desktop/src/main/services/ai/aiIntegrationService.ts, apps/desktop/src/main/services/chat/agentChatService.ts
Remove locally-defined LOCAL_PROVIDER_LABELS constants and import from shared modelRegistry instead.
Renderer Component Refactoring
apps/desktop/src/renderer/components/chat/AgentChatPane.tsx
Switches to shared provider parsing helpers; adds unified permission mode reset logic when switching models; reworks runtime status UI from separate banners into merged LocalRuntimeNoticeBlock system supporting cli-only, local-only, and merged display states.
Settings & Model Selection UI
apps/desktop/src/renderer/components/settings/ProvidersSection.tsx, apps/desktop/src/renderer/components/shared/UnifiedModelSelector.tsx
Refactor local model label formatting and placeholder construction to use shared parsing utilities (parseLocalProviderFromModelId, getLocalModelIdTail, LOCAL_PROVIDER_LABELS) instead of local implementations.
Model Options Utilities
apps/desktop/src/renderer/lib/modelOptions.ts
Removes local LOCAL_PROVIDER_LABELS map and parsing logic; delegates provider identification and tail extraction to shared helpers; updates provider-type checks to use isLocalProviderFamily().

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested labels

desktop

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the three main objectives of the PR: exporting shared helpers for local model handling, merging runtime status banners into a single unified card, and implementing permission mode resets on model switches.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cursor/-bc-eec1a734-3fc8-4296-88d0-07e2c5ad7e7e-4925

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 and usage tips.

@arul28
Copy link
Copy Markdown
Owner Author

arul28 commented Apr 5, 2026

@coderabbitai review

@arul28
Copy link
Copy Markdown
Owner Author

arul28 commented Apr 5, 2026

@codex review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 5, 2026

✅ 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.

@arul28
Copy link
Copy Markdown
Owner Author

arul28 commented Apr 5, 2026

@copilot review

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 5, 2026

@copilot review

Code Review

The changes are clean and well-scoped. Here's my assessment:

✅ Shared helpers (modelRegistry.ts)

LOCAL_PROVIDER_LABELS, parseLocalProviderFromModelId, getLocalModelIdTail, and isLocalProviderFamily are all correctly extracted and exported. Eliminates 4+ duplicate definitions across the codebase. The JSDoc comments on the new exports are helpful.

✅ Banner merge (AgentChatPane.tsx)

The mergedRuntimeBanner useMemo cleanly covers all three cases: cli-only, local-only, and merged. The LocalRuntimeNoticeBlock component with variant="card" | "inline" is a tidy way to share the local notice markup without duplication.

One minor observation: cliRuntimeBlocked, cliRuntimeTitle, and cliRuntimeBody are computed as bare variables (non-memoized) outside mergedRuntimeBanner. They're simple enough that this is fine, but if activeProviderConnection is a frequently-changing object they'll recalculate on every render. Not a problem in practice.

✅ Permission reset logic

shouldResetUnifiedPermissionForModelSwitch correctly handles all four transitions:

  • null → null → no reset
  • null → recommendation → reset + apply recommendation
  • recommendation → null → reset only (clears stuck plan mode)
  • recommendation → different recommendation → reset + apply new

In applyModelSelectionSnapshot, the reset runs before the recommendation apply, so the order is correct. The initialNativeControls.unifiedPermissionMode dependency was also correctly added to the useCallback.

updateSession payload

The new guard snapshot.nextProvider === "unified" && nextUnifiedForPayload != null correctly scopes the summarizeNativeControls path to unified-provider sessions only. Non-unified providers (claude/codex/cursor) now properly fall through to buildNativeControlPayload.

prevModelDescRef initialization

Starts as undefined, which is handled gracefully since recommendedUnifiedPermissionModeForModel(undefined) returns null. The useEffect seeds it correctly after first render before any real user interaction.

sdkModelId fix

Using shortId (model tail) instead of the full provider/modelId string for the SDK model ID is correct — the SDK sends this directly to the provider endpoint, which doesn't expect the provider prefix.

No correctness bugs found. CI is passing.

Copy link
Copy Markdown

@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.

🧹 Nitpick comments (1)
apps/desktop/src/renderer/components/chat/AgentChatPane.tsx (1)

1816-1830: Sequential setState calls are correct but could be consolidated.

The two setUnifiedPermissionMode calls in lines 1824-1829 are batched by React, so only the final value applies. The current structure makes the two-step intent clear (reset first, then apply recommendation), but could be simplified.

♻️ Optional consolidation
-    if (snapshot.resetUnifiedPermissionToDefault) {
-      setUnifiedPermissionMode(initialNativeControls.unifiedPermissionMode);
-    }
-    if (snapshot.nextUnifiedPermissionMode) {
+    const effectiveMode = snapshot.resetUnifiedPermissionToDefault
+      ? (snapshot.nextUnifiedPermissionMode ?? initialNativeControls.unifiedPermissionMode)
+      : snapshot.nextUnifiedPermissionMode;
+    if (effectiveMode) {
       setUnifiedPermissionMode(snapshot.nextUnifiedPermissionMode);
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src/renderer/components/chat/AgentChatPane.tsx` around lines
1816 - 1830, The applyModelSelectionSnapshot callback uses two sequential
setUnifiedPermissionMode calls; consolidate them by computing the intended
unifiedPermissionMode first (e.g., if snapshot.resetUnifiedPermissionToDefault
then initialNativeControls.unifiedPermissionMode else
snapshot.nextUnifiedPermissionMode if present else leave unchanged) and then
call setUnifiedPermissionMode exactly once after setModelId and
setReasoningEffort; update the applyModelSelectionSnapshot function to determine
the final value and invoke setUnifiedPermissionMode(singleValue) instead of
calling it twice.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@apps/desktop/src/renderer/components/chat/AgentChatPane.tsx`:
- Around line 1816-1830: The applyModelSelectionSnapshot callback uses two
sequential setUnifiedPermissionMode calls; consolidate them by computing the
intended unifiedPermissionMode first (e.g., if
snapshot.resetUnifiedPermissionToDefault then
initialNativeControls.unifiedPermissionMode else
snapshot.nextUnifiedPermissionMode if present else leave unchanged) and then
call setUnifiedPermissionMode exactly once after setModelId and
setReasoningEffort; update the applyModelSelectionSnapshot function to determine
the final value and invoke setUnifiedPermissionMode(singleValue) instead of
calling it twice.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 54e2d831-0e43-4133-b681-27b58d0f3bdc

📥 Commits

Reviewing files that changed from the base of the PR and between 9f5bcd3 and 55ebb03.

📒 Files selected for processing (7)
  • apps/desktop/src/main/services/ai/aiIntegrationService.ts
  • apps/desktop/src/main/services/chat/agentChatService.ts
  • apps/desktop/src/renderer/components/chat/AgentChatPane.tsx
  • apps/desktop/src/renderer/components/settings/ProvidersSection.tsx
  • apps/desktop/src/renderer/components/shared/UnifiedModelSelector.tsx
  • apps/desktop/src/renderer/lib/modelOptions.ts
  • apps/desktop/src/shared/modelRegistry.ts

Copy link
Copy Markdown

@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.

Reviewed commit: 55ebb03faf

ℹ️ 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".

nextReasoningEffort,
nextUnifiedPermissionMode: recommendedUnifiedPermissionModeForModel(nextDesc),
nextUnifiedPermissionMode: nextRec,
resetUnifiedPermissionToDefault: shouldResetUnifiedPermissionForModelSwitch(previousDesc, nextDesc),
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 Badge Preserve plan mode for discovered local model switches

resetUnifiedPermissionToDefault is computed from nextDesc, which comes from getModelById(nextModelId). For runtime-discovered local IDs that are not present in the renderer registry (they are represented as selector placeholders), nextDesc is null, so a switch from a guarded local model (plan) to one of these discovered models is treated as plan -> null and gets reset to the surface default. That downgrades the session out of plan mode and the downgraded mode is then sent in updateSession, so users can lose guarded local safety just by selecting a discovered local model.

Useful? React with 👍 / 👎.

Add getModelDescriptorForPermissionMode for harness decisions when
getModelById is undefined (e.g. ollama/auto). Use it in AgentChatPane for
prevModelRef, model switch snapshots, and session create. Consolidate
applyModelSelectionSnapshot to a single setUnifiedPermissionMode call.

Co-authored-by: Arul Sharma <arul28@users.noreply.github.com>
@arul28 arul28 marked this pull request as ready for review April 5, 2026 02:35
@arul28 arul28 merged commit e01c26f into local-model-fixes Apr 5, 2026
4 checks passed
@arul28 arul28 deleted the cursor/-bc-eec1a734-3fc8-4296-88d0-07e2c5ad7e7e-4925 branch April 5, 2026 02:35
This was referenced Apr 5, 2026
arul28 added a commit that referenced this pull request Apr 6, 2026
* Polish local model chat UI and share provider helpers

Export LOCAL_PROVIDER_LABELS and local model id parsers from modelRegistry;
dedupe labels in agentChatService and aiIntegrationService. Merge CLI and
local runtime banners in AgentChatPane, reset unified permission mode when
harness recommendations change, and fix local placeholder sdkModelId. Settings
and model options use the shared helpers.

Co-authored-by: Arul Sharma <arul28@users.noreply.github.com>

* Address PR review: local permission descriptors and unified mode setter

Add getModelDescriptorForPermissionMode for harness decisions when
getModelById is undefined (e.g. ollama/auto). Use it in AgentChatPane for
prevModelRef, model switch snapshots, and session create. Consolidate
applyModelSelectionSnapshot to a single setUnifiedPermissionMode call.

Co-authored-by: Arul Sharma <arul28@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Arul Sharma <arul28@users.noreply.github.com>
arul28 added a commit that referenced this pull request Apr 6, 2026
* Polish local model chat UI and share provider helpers

Export LOCAL_PROVIDER_LABELS and local model id parsers from modelRegistry;
dedupe labels in agentChatService and aiIntegrationService. Merge CLI and
local runtime banners in AgentChatPane, reset unified permission mode when
harness recommendations change, and fix local placeholder sdkModelId. Settings
and model options use the shared helpers.

Co-authored-by: Arul Sharma <arul28@users.noreply.github.com>

* Address PR review: local permission descriptors and unified mode setter

Add getModelDescriptorForPermissionMode for harness decisions when
getModelById is undefined (e.g. ollama/auto). Use it in AgentChatPane for
prevModelRef, model switch snapshots, and session create. Consolidate
applyModelSelectionSnapshot to a single setUnifiedPermissionMode call.

Co-authored-by: Arul Sharma <arul28@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Arul Sharma <arul28@users.noreply.github.com>
arul28 added a commit that referenced this pull request Apr 7, 2026
* Polish local model chat UI and share provider helpers

Export LOCAL_PROVIDER_LABELS and local model id parsers from modelRegistry;
dedupe labels in agentChatService and aiIntegrationService. Merge CLI and
local runtime banners in AgentChatPane, reset unified permission mode when
harness recommendations change, and fix local placeholder sdkModelId. Settings
and model options use the shared helpers.

Co-authored-by: Arul Sharma <arul28@users.noreply.github.com>

* Address PR review: local permission descriptors and unified mode setter

Add getModelDescriptorForPermissionMode for harness decisions when
getModelById is undefined (e.g. ollama/auto). Use it in AgentChatPane for
prevModelRef, model switch snapshots, and session create. Consolidate
applyModelSelectionSnapshot to a single setUnifiedPermissionMode call.

Co-authored-by: Arul Sharma <arul28@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Arul Sharma <arul28@users.noreply.github.com>
arul28 added a commit that referenced this pull request Apr 8, 2026
* Polish local model chat UI and share provider helpers

Export LOCAL_PROVIDER_LABELS and local model id parsers from modelRegistry;
dedupe labels in agentChatService and aiIntegrationService. Merge CLI and
local runtime banners in AgentChatPane, reset unified permission mode when
harness recommendations change, and fix local placeholder sdkModelId. Settings
and model options use the shared helpers.

Co-authored-by: Arul Sharma <arul28@users.noreply.github.com>

* Address PR review: local permission descriptors and unified mode setter

Add getModelDescriptorForPermissionMode for harness decisions when
getModelById is undefined (e.g. ollama/auto). Use it in AgentChatPane for
prevModelRef, model switch snapshots, and session create. Consolidate
applyModelSelectionSnapshot to a single setUnifiedPermissionMode call.

Co-authored-by: Arul Sharma <arul28@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Arul Sharma <arul28@users.noreply.github.com>
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.

3 participants