Skip to content

feat(gui): provider discovery-failure badge + honest Claude helper fallback copy (#329, #331)#358

Merged
lidge-jun merged 7 commits into
devfrom
codex/260724-ux-discovery
Jul 24, 2026
Merged

feat(gui): provider discovery-failure badge + honest Claude helper fallback copy (#329, #331)#358
lidge-jun merged 7 commits into
devfrom
codex/260724-ux-discovery

Conversation

@lidge-jun

@lidge-jun lidge-jun commented Jul 23, 2026

Copy link
Copy Markdown
Owner

Summary

Closes the remaining UX gaps from #329 and #331:

Design notes

  • Discriminated union: reason: "http" carries a required httpStatus; other reasons cannot carry one (invalid states unrepresentable).
  • Network/provider failures are sanitized — raw error messages/URLs never enter state or the API (sentinel-asserted).
  • First-load ordering: /api/providers is read after /api/models to avoid a badge flash race.
  • PR feat(gui): configure Codex auto-switch threshold #337 collision-checked live: only new models.discoveryFailed* / claude.smallFastModel* locale keys added across the six dictionaries; no codexAuth.* keys, no styles.css changes (existing badge/warning classes reused).

Tests + rendered QA

Named activation tests for all five failure reasons + status-reset + additive/no-attempt path + helper copy (server: +229 lines, gui: +322 lines). Rendered QA at 1280×720 against a live isolated proxy: badge/reason/settings-link visible, /api/models/api/providers order confirmed, zero console errors.

  • typecheck / lint:gui / build:gui exit 0; focused 137 pass; full suite 3865 pass / 0 fail; privacy:scan pass.

Docs: claude-code.md guide updated in five locales.

Design/audit trail: devlog/_plan/260724_bugfix_train/040_ux_discovery_badge.md.

Closes #329. Closes #331.

Summary by CodeRabbit

  • New Features
    • Provider model discovery now records clearer outcome statuses and reasons (network, policy blocks, HTTP status, invalid responses) and exposes them via the provider model listing.
    • The Models page shows an amber “discovery failed” badge with localized details, and improved empty-state messaging when discovery fails.
    • Claude Code settings now better explain the “small fast model” option, including native fallback and possible native-provider charges.
  • Documentation
    • Updated Claude Code “Effective model environment” guidance across supported languages for the “no helper model configured” fallback behavior.
  • Tests
    • Added/expanded UI and integration tests covering discovery-failure reasons, native-fallback warnings, and management API parity.

@lidge-jun
lidge-jun requested a review from Ingwannu as a code owner July 23, 2026 18:53
@github-actions github-actions Bot added the enhancement New feature or request label Jul 23, 2026
@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The change records structured provider model-discovery outcomes, exposes them through provider APIs, and displays localized failure details in the Models UI. It also clarifies Claude Code’s unset helper-model behavior with UI warnings, tests, and multilingual documentation.

Changes

Provider discovery failure reporting

Layer / File(s) Summary
Discovery state and provider API
src/codex/model-cache.ts, src/codex/catalog/provider-fetch.ts, src/server/management/provider-routes.ts
Discovery records success and categorized failures, then exposes the status through GET /api/providers.
Models UI discovery states
gui/src/models-groups.ts, gui/src/pages/Models.tsx, gui/src/i18n/*
Discovery metadata is propagated into model groups and rendered as localized failure badges and reason messages.
Discovery regression coverage
tests/*, gui/tests/*
Tests cover failure classification, response sanitization, API parity, successful resets, static-provider behavior, and rendered UI states.

Claude helper-model transparency

Layer / File(s) Summary
Claude helper setting and copy
gui/src/pages/ClaudeCode.tsx, gui/src/i18n/*, gui/tests/claude-code-background-helper.test.tsx, docs-site/src/content/docs/*/guides/claude-code.md
The small-fast-model selector explains unset behavior and warns that Claude Code may use its native Sonnet helper model and incur native-provider charges.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ProviderDiscovery
  participant ModelCache
  participant ProviderAPI
  participant ModelsPage
  ProviderDiscovery->>ModelCache: record ok or failure reason
  ProviderAPI->>ModelCache: read discovery status
  ModelCache-->>ProviderAPI: return discovery summary
  ModelsPage->>ProviderAPI: fetch provider summaries
  ProviderAPI-->>ModelsPage: return discovery metadata
  ModelsPage-->>ModelsPage: render badge and localized reason
Loading

Possibly related PRs

  • lidge-jun/opencodex#354: Covers the same provider discovery-status path through model-cache recording, management API exposure, and Models UI rendering.

Suggested labels: enhancement

Suggested reviewers: ingwannu, wibias

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 4.00% 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately summarizes the main UX changes: provider discovery-failure badges and Claude helper fallback copy.
Linked Issues check ✅ Passed The PR addresses #329 and #331 by tracking discovery failures, exposing them in /api/providers and the Models UI, and warning when Claude falls back to native Sonnet.
Out of Scope Changes check ✅ Passed The changes stay focused on discovery-failure handling, Claude helper fallback UX, and the related localization/tests/docs needed to support them.
✨ 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 codex/260724-ux-discovery

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

@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: b620df88bc

ℹ️ 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 gui/src/pages/Models.tsx Outdated
<div style={{ padding: "6px 12px" }}>
{isNative && <p className="muted text-label" style={{ margin: "2px 0 6px" }}>{t("models.nativeHint")}</p>}
{rows.length === 0 && <EmptyProviderHint liveModels={liveModels} />}
{rows.length === 0 && <EmptyProviderHint liveModels={liveModels} discovery={discovery} />}

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 Show discovery failures when fallback rows exist

When a failed live fetch falls back to configured, default, or stale models, fetchProviderModels records the failure but returns nonempty rows; this condition then skips EmptyProviderHint, so the new badge remains invisible and the discovery failure is still log-only. Render the failure status independently of the empty-provider hint so providers using fallback rows also expose the failed discovery.

Useful? React with 👍 / 👎.

Comment thread gui/src/pages/ClaudeCode.tsx Outdated
Comment on lines +127 to +130
{value === "" && (
<p className="notice-warn" role="note" style={{ marginTop: 8 }}>
{t("claude.smallFastModelNativeWarning")}
</p>

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 Account for the effective Haiku override before warning

For configurations with tierModels.haiku set but smallFastModel empty, this warning is false: effectiveModelEnv uses tierModels.haiku ?? smallFastModel and sets both helper variables, so Claude Code does not fall back to native Sonnet. Base the warning on the effective Haiku/helper value (available via effectiveModelEnv) rather than smallFastModel alone.

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: 1

Caution

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

⚠️ Outside diff range comments (1)
gui/src/pages/Models.tsx (1)

755-800: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Discovery-failed badge only ever renders for fully empty providers — misses the exact scenario issue #329 calls out.

EmptyProviderHint (which owns the "Discovery failed" badge/reason/settings-link, lines 1136-1157) is only invoked at Line 800 when rows.length === 0. If a provider has even one manually configured/static model and live discovery is currently failing (e.g. HTTP 401 on /v1/models), the group renders normally with zero indication that discovery is broken — the header at lines 760-762 shows only the provider name, native badge, and active count, with no failure signal.

This directly undercuts the PR's own objective (#329): "the provider should remain visible, discovery failures should be surfaced clearly, and manually configured models should remain usable and selectable" — those two conditions (usable static models + failing discovery) are exactly the case this gating misses. It's also untested: gui/tests/models-empty-provider.test.tsx's "destination-blocked" and "invalid JSON" tests configure a models: ["static-fallback"] provider and assert the discovery state directly, but only exercise EmptyProviderHint in isolation — never the actual rows.length > 0 header path.

Move the badge to the always-rendered group header so it survives regardless of row count:

🛠️ Proposed fix: surface the badge in the group header, not just the empty state
            <span className="text-body font-semibold">{provider}</span>
             {isNative && <span className="muted mono text-caption" style={{ padding: "1px 6px", border: "1px solid var(--border)", borderRadius: "var(--radius-pill)" }}>{t("models.nativeGroupLabel")}</span>}
+            {liveModels && discovery?.status === "failed" && (
+              <span className="badge badge-amber" title={discoveryFailureReason(t, discovery)}>
+                {t("models.discoveryFailedBadge")}
+              </span>
+            )}
             <span className="muted mono text-label">{t("models.active", { active: activeCount, total: rows.length })}</span>

The detailed hint text inside EmptyProviderHint can stay gated on rows.length === 0 (it's the right place for the full explanation + settings link when there's nothing else to show), but the header badge should be unconditional on row count.

🤖 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 `@gui/src/pages/Models.tsx` around lines 755 - 800, Move the discovery-failure
badge from the rows.length === 0 rendering path into the always-rendered
provider group header around the provider name and active count, using the
existing discovery failure state and badge component from EmptyProviderHint.
Keep EmptyProviderHint’s detailed explanation and settings link gated to empty
providers, while ensuring the header badge appears for providers with manually
configured models as well.
🤖 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 `@gui/tests/claude-code-background-helper.test.tsx`:
- Around line 6-20: Restore the original navigator.language property descriptor
rather than only its value. In the beforeEach/afterEach hooks, snapshot
Object.getOwnPropertyDescriptor(globalThis.navigator, "language"), then restore
that descriptor verbatim after each test; if no own descriptor existed, delete
the temporary property.

---

Outside diff comments:
In `@gui/src/pages/Models.tsx`:
- Around line 755-800: Move the discovery-failure badge from the rows.length ===
0 rendering path into the always-rendered provider group header around the
provider name and active count, using the existing discovery failure state and
badge component from EmptyProviderHint. Keep EmptyProviderHint’s detailed
explanation and settings link gated to empty providers, while ensuring the
header badge appears for providers with manually configured models as well.
🪄 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: ASSERTIVE

Plan: Pro Plus

Run ID: 02b5cded-d446-451d-a9ac-233d3fa00927

📥 Commits

Reviewing files that changed from the base of the PR and between c63589c and b620df8.

📒 Files selected for processing (23)
  • docs-site/src/content/docs/guides/claude-code.md
  • docs-site/src/content/docs/ja/guides/claude-code.md
  • docs-site/src/content/docs/ko/guides/claude-code.md
  • docs-site/src/content/docs/ru/guides/claude-code.md
  • docs-site/src/content/docs/zh-cn/guides/claude-code.md
  • gui/src/i18n/de.ts
  • gui/src/i18n/en.ts
  • gui/src/i18n/ja.ts
  • gui/src/i18n/ko.ts
  • gui/src/i18n/ru.ts
  • gui/src/i18n/zh.ts
  • gui/src/models-groups.ts
  • gui/src/pages/ClaudeCode.tsx
  • gui/src/pages/Models.tsx
  • gui/tests/claude-code-background-helper.test.tsx
  • gui/tests/models-empty-provider.test.tsx
  • src/codex/catalog/provider-fetch.ts
  • src/codex/model-cache.ts
  • src/server/management/provider-routes.ts
  • tests/codex-catalog.test.ts
  • tests/cursor-hardening.test.ts
  • tests/management-provider-validation.test.ts
  • tests/models-page-groups.test.ts

Comment thread gui/tests/claude-code-background-helper.test.tsx Outdated
@Wibias

Wibias commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator

#354 also fixes #329 with a few extras worth folding in before merge:

  1. mark discovery skipped when liveModels === false (avoids a stuck failure badge)
  2. optional empty / fallback status fields
  3. web-dashboard Models doc line for failed discovery

Happy to take those as a tiny follow-up after #358 if you prefer to merge as-is. Either way we should close #354 with credit to @stantheman0128.

@stantheman0128

Copy link
Copy Markdown

Thanks for the ping @Wibias. Agree those three follow-ups (skipped when liveModels === false, optional empty/fallback status fields, Models doc line) are worth folding into #358 before merge. Happy to review or take a tiny follow-up after #358 if you prefer to land the main path first. Appreciate the credit.

@Wibias

Wibias commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator

Dual review (security + bugs/edge cases)

Bot review threads are not cleared yet (3 unresolved: 2 Codex P2 + 1 CodeRabbit test hygiene). CI is green. Below is an independent second look (security + functional), including overlap notes with #354.

Security categories checked

No evidence of malware, backdoor, hidden C2, local admission bypass, classic XSS/HTML sinks, or credential logging. Discovery status is a small discriminated union (ok / failed + enum reason + optional numeric httpStatus); network failures are sentinel-tested so raw URLs/messages do not enter state or /api/providers. React renders reasons via fixed i18n keys (text nodes). Additive management-API field only; no new auth surface.

Privacy posture for this change looks good.

Blockers

  1. Discovery-failure badge is gated on empty rows (High for [Bug]: provider vanishes when /v1/models discovery returns 401; Agent Plan needs manual model specification #329)gui/src/pages/Models.tsx
    EmptyProviderHint (and thus the amber badge) only renders when rows.length === 0. In the common failure path, fetchProviderModels still returns configured / stale / Jawcode-augmented rows after markProviderDiscoveryFailed, so the group is nonempty and the UI never shows the new status — discovery stays effectively log-only for those providers. That undercuts the PR’s stated fix for [Bug]: provider vanishes when /v1/models discovery returns 401; Agent Plan needs manual model specification #329.
    Fix: render failure status independently of the empty hint (header/badge beside the provider group whenever discovery.status === "failed" and liveModels).

  2. Native-Sonnet warning ignores effective Haiku override (Medium for [UX] Background helper 기본값이 Sonnet native 호출로 떨어질 때 안내가 없습니다 #331)gui/src/pages/ClaudeCode.tsx
    Warning uses value === "" (smallFastModel only). Runtime env uses tierModels.haiku ?? smallFastModel (effectiveModelEnv in src/claude/context-windows.ts), so tierModels.haiku set + empty smallFastModel still injects helper overrides and does not fall back to native Sonnet — yet the UI warns about native Sonnet charges. Base the warning on the effective helper/Haiku value (or hide it when effectiveModelEnv already sets the helper vars).

Should-fix before merge

  1. liveModels: false can leave a stale failed status in memory (Medium / fix(models): surface discovery failure status when /v1/models returns 401 (#329) #354)src/codex/catalog/provider-fetch.ts + model-cache.ts
    Early return when liveModels === false does not mark/clear discovery. UI currently suppresses the badge via liveModels && discovery?.status === "failed", so empty groups are OK, but /api/providers can still advertise a prior failed after discovery was intentionally disabled. Prefer an explicit skipped (or clear on disable) as in fix(models): surface discovery failure status when /v1/models returns 401 (#329) #354, so re-enabling live discovery does not flash a stale failure before the next fetch.

  2. Test navigator.language restore is incomplete (Low)gui/tests/claude-code-background-helper.test.tsx
    Matches the open CodeRabbit thread: snapshot/restore the property descriptor (or delete if none), not only the value.

Non-blocking / already solid

  • Discriminated failure reasons + HTTP status typing, successful-discovery reset tests, and sanitization assertions look careful.
  • /api/models then /api/providers ordering on the Models page is the right anti-flash approach.
  • Additive DTO field; locales for the new keys are present across the six dictionaries.

Aligns with open bot threads + #354

Findings 1–2 match the open Codex P2s. Finding 3 matches the Wibias/#354 extras already discussed on this PR (skipped when liveModels === false, richer empty/fallback status, dashboard docs line). Prefer folding those into this PR or an immediate follow-up, then close #354 with credit to @stantheman0128 — do not merge both independently.

Suggested fix order

  1. Show discovery failure independently of empty rows (regression test: failed status + nonempty fallback rows ⇒ badge visible).
  2. Gate the Claude helper warning on effective Haiku/helper env, not smallFastModel alone.
  3. Mark/clear skipped discovery when liveModels === false (or take fix(models): surface discovery failure status when /v1/models returns 401 (#329) #354’s approach).
  4. Fix the navigator descriptor restore in the helper test.

@stantheman0128

Copy link
Copy Markdown

@Wibias Thanks for the dual review. Agree with the fix order, especially showing the discovery-failure badge even when fallback rows keep the group nonempty, and folding the #354 extras (skipped when liveModels === false, clearer status fields) into this PR rather than merging both.

#354 can stay closed with credit as discussed. Happy to help on a tiny follow-up after #358 if you prefer to keep this PR lean; otherwise taking those into #358 is ideal.

Stan Shih (stantheman0128)

lidge-jun added a commit that referenced this pull request Jul 24, 2026
@lidge-jun
lidge-jun force-pushed the codex/260724-ux-discovery branch from b620df8 to 631fa1f Compare July 24, 2026 03:18
@lidge-jun
lidge-jun merged commit 4b98cac into dev Jul 24, 2026
14 of 15 checks passed

@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: 2

Caution

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

⚠️ Outside diff range comments (1)
gui/tests/models-empty-provider.test.tsx (1)

19-37: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

navigator.language restoration only preserves the value, not the full property descriptor.

afterEach at lines 33-36 does:

Object.defineProperty(globalThis.navigator, "language", {
  configurable: true,
  value: previousLanguage,
});

This only captured .language's value in the earlier beforeEach (line 23: previousLanguage = (globalThis.navigator as ...)?.language;), not Object.getOwnPropertyDescriptor(...). If the original language property was an accessor (getter) or had different writable/enumerable attributes (e.g. happy-dom/Bun's own navigator implementation, or a prior test's monkey-patch), this defineProperty call permanently replaces it with a plain writable/enumerable data property, silently changing behavior for any later test in the same process that reads navigator.language.

This is the exact same class of bug called out in the PR notes as a required pre-merge fix ("Restore the complete navigator.language property descriptor in the helper test"), and the correct pattern already exists in this very file at lines 73-75 / 133-137 (Object.getOwnPropertyDescriptor + full restore via Object.defineProperty(globalThis, key, descriptor) or delete) for document/window/localStorage/IS_REACT_ACT_ENVIRONMENT. navigator.language should follow the same pattern for consistency and safety.

🔧 Suggested fix
-let previousLanguage: unknown;
+let previousLanguageDescriptor: PropertyDescriptor | undefined;
 const originalFetch = globalThis.fetch;
 
 beforeEach(() => {
-  previousLanguage = (globalThis.navigator as { language?: unknown } | undefined)?.language;
+  previousLanguageDescriptor = Object.getOwnPropertyDescriptor(globalThis.navigator, "language");
   ...
 });
 
 afterEach(() => {
   globalThis.fetch = originalFetch;
   clearModelCache();
-  Object.defineProperty(globalThis.navigator, "language", {
-    configurable: true,
-    value: previousLanguage,
-  });
+  if (previousLanguageDescriptor) {
+    Object.defineProperty(globalThis.navigator, "language", previousLanguageDescriptor);
+  }
 });
🤖 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 `@gui/tests/models-empty-provider.test.tsx` around lines 19 - 37, Update the
navigator.language setup in beforeEach/afterEach to capture and restore the
complete property descriptor using Object.getOwnPropertyDescriptor and the
existing descriptor-restoration pattern in this file, deleting the property when
no original descriptor exists. Preserve the test’s en-US override while ensuring
accessor, writable, configurable, and enumerable attributes are restored
exactly.
🤖 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 `@gui/src/i18n/ko.ts`:
- Around line 748-750: Update the Korean translation value for
claude.smallFastModelNativeWarning to translate “helper-model overrides”
directly, removing the reference to an environment variable while preserving the
warning about native Sonnet usage and provider charges.

In `@src/codex/catalog/provider-fetch.ts`:
- Line 254: Clear the provider discovery status before returning configured from
both credential-skip paths: the Cursor and OAuth branches that return when
!apiKey. Reuse clearProviderDiscoveryStatus(name), matching the existing
liveModels === false handling, so stale discovery results are removed whenever
live discovery is skipped due to missing credentials.

---

Outside diff comments:
In `@gui/tests/models-empty-provider.test.tsx`:
- Around line 19-37: Update the navigator.language setup in beforeEach/afterEach
to capture and restore the complete property descriptor using
Object.getOwnPropertyDescriptor and the existing descriptor-restoration pattern
in this file, deleting the property when no original descriptor exists. Preserve
the test’s en-US override while ensuring accessor, writable, configurable, and
enumerable attributes are restored exactly.
🪄 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: ASSERTIVE

Plan: Pro Plus

Run ID: 9270e9a4-6f74-4f04-b8ee-0c1b5a622818

📥 Commits

Reviewing files that changed from the base of the PR and between b620df8 and 631fa1f.

📒 Files selected for processing (23)
  • docs-site/src/content/docs/guides/claude-code.md
  • docs-site/src/content/docs/ja/guides/claude-code.md
  • docs-site/src/content/docs/ko/guides/claude-code.md
  • docs-site/src/content/docs/ru/guides/claude-code.md
  • docs-site/src/content/docs/zh-cn/guides/claude-code.md
  • gui/src/i18n/de.ts
  • gui/src/i18n/en.ts
  • gui/src/i18n/ja.ts
  • gui/src/i18n/ko.ts
  • gui/src/i18n/ru.ts
  • gui/src/i18n/zh.ts
  • gui/src/models-groups.ts
  • gui/src/pages/ClaudeCode.tsx
  • gui/src/pages/Models.tsx
  • gui/tests/claude-code-background-helper.test.tsx
  • gui/tests/models-empty-provider.test.tsx
  • src/codex/catalog/provider-fetch.ts
  • src/codex/model-cache.ts
  • src/server/management/provider-routes.ts
  • tests/codex-catalog.test.ts
  • tests/cursor-hardening.test.ts
  • tests/management-provider-validation.test.ts
  • tests/models-page-groups.test.ts

Comment thread gui/src/i18n/ko.ts
Comment on lines +748 to +750
"claude.smallFastModelAccurateHint": "Claude Code가 대화 요약, 주제 감지 같은 백그라운드 작업에 쓰는 모델입니다. 서브에이전트의 haiku 별칭도 이 모델을 사용합니다.",
"claude.smallFastModelUnsetOption": "Claude Code가 선택(네이티브 모델)",
"claude.smallFastModelNativeWarning": "비워 두면 OpenCodex가 보조 모델 환경 변수를 설정하지 않습니다. Claude Code가 네이티브 Sonnet 모델을 사용할 수 있으며, 네이티브 프로바이더 요금이 발생할 수 있습니다.",

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 | 🟡 Minor | ⚡ Quick win

Mistranslation: "환경 변수" (environment variable) isn't in the English source and misrepresents the mechanism.

Line 750: "claude.smallFastModelNativeWarning": "비워 두면 OpenCodex가 보조 모델 환경 변수를 설정하지 않습니다. ..." — this says OpenCodex "does not set the helper-model environment variable". The English source (en.ts line 1035) says only "When unset, OpenCodex leaves the helper-model overrides unset." — no mention of an environment variable. de.ts ("setzt OpenCodex keine Hilfsmodell-Overrides") and ja.ts ("ヘルパーモデルの上書きを設定しません") both correctly translate "overrides", not "environment variable". A Korean-reading user could conclude they need to set some env var manually to fix this, which isn't how the GUI setting works.

💬 Suggested fix
-  "claude.smallFastModelNativeWarning": "비워 두면 OpenCodex가 보조 모델 환경 변수를 설정하지 않습니다. Claude Code가 네이티브 Sonnet 모델을 사용할 수 있으며, 네이티브 프로바이더 요금이 발생할 수 있습니다.",
+  "claude.smallFastModelNativeWarning": "비워 두면 OpenCodex가 보조 모델 오버라이드를 설정하지 않습니다. Claude Code가 네이티브 Sonnet 모델을 사용할 수 있으며, 네이티브 프로바이더 요금이 발생할 수 있습니다.",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"claude.smallFastModelAccurateHint": "Claude Code가 대화 요약, 주제 감지 같은 백그라운드 작업에 쓰는 모델입니다. 서브에이전트의 haiku 별칭도 이 모델을 사용합니다.",
"claude.smallFastModelUnsetOption": "Claude Code가 선택(네이티브 모델)",
"claude.smallFastModelNativeWarning": "비워 두면 OpenCodex가 보조 모델 환경 변수를 설정하지 않습니다. Claude Code가 네이티브 Sonnet 모델을 사용할 수 있으며, 네이티브 프로바이더 요금이 발생할 수 있습니다.",
"claude.smallFastModelAccurateHint": "Claude Code가 대화 요약, 주제 감지 같은 백그라운드 작업에 쓰는 모델입니다. 서브에이전트의 haiku 별칭도 이 모델을 사용합니다.",
"claude.smallFastModelUnsetOption": "Claude Code가 선택(네이티브 모델)",
"claude.smallFastModelNativeWarning": "비워 두면 OpenCodex가 보조 모델 오버라이드를 설정하지 않습니다. Claude Code가 네이티브 Sonnet 모델을 사용할 수 있으며, 네이티브 프로바이더 요금이 발생할 수 있습니다.",
🤖 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 `@gui/src/i18n/ko.ts` around lines 748 - 750, Update the Korean translation
value for claude.smallFastModelNativeWarning to translate “helper-model
overrides” directly, removing the reference to an environment variable while
preserving the warning about native Sonnet usage and provider charges.

clearProviderDiscoveryStatus(name);
return configured;
}
if (!apiKey) return configured;

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

Stale discovery status leaks through the !apiKey skip paths.

Both the Cursor !apiKey early-return (line 254) and the OAuth !apiKey early-return (lines 281-286) skip the live discovery attempt entirely but never touch discoveryStatus. Contrast this with the liveModels === false branches at lines 250-253 and 288, which correctly call clearProviderDiscoveryStatus(name) before returning configured.

Failure mode: if a provider previously discovered successfully ({status:"ok"}) or failed with some HTTP/network reason, and the credential later goes missing (OAuth logout, Cursor token revoked), GET /api/providers keeps reporting that stale status indefinitely — even though no live attempt is being made this round. The Models page would then show either a misleadingly "healthy" badge for a provider that isn't actually discovering, or a stale/irrelevant failure reason (e.g. an old "HTTP 500" from months ago) instead of reflecting the current "not logged in" state. This directly conflicts with the PR's goal of surfacing discovery status accurately.

🩹 Proposed fix — clear stale status on the credential-skip paths too
-    if (!apiKey) return configured;
+    if (!apiKey) {
+      clearProviderDiscoveryStatus(name);
+      return configured;
+    }
   if (prov.authMode === "oauth" && !apiKey) {
     // No usable token (logged out, or account marked needsReauth). Still surface the
     // configured static catalog so the GUI Models tab / rail counts are not empty —
     // matching Cursor's !apiKey → configured degradation and fetch-failure fallback.
+    clearProviderDiscoveryStatus(name);
     return configured;
   }

Also applies to: 281-286

🧰 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 { execFileSync } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)

🤖 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 `@src/codex/catalog/provider-fetch.ts` at line 254, Clear the provider
discovery status before returning configured from both credential-skip paths:
the Cursor and OAuth branches that return when !apiKey. Reuse
clearProviderDiscoveryStatus(name), matching the existing liveModels === false
handling, so stale discovery results are removed whenever live discovery is
skipped due to missing credentials.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants