Skip to content

feat(extension): model picker parity with the app — search, favorites, sections - #4810

Merged
iscekic merged 10 commits into
mainfrom
feat/ext-model-picker-parity
Jul 28, 2026
Merged

feat(extension): model picker parity with the app — search, favorites, sections#4810
iscekic merged 10 commits into
mainfrom
feat/ext-model-picker-parity

Conversation

@iscekic

@iscekic iscekic commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

What changed

The extension's footer model control is upgraded from a native <select> to a full picker overlay with parity to the mobile app picker:

  • Search filters by model name and model id, case-insensitively, on every keystroke (A2).
  • Sections match the app's gateway grouping exactly: FAVORITES (when any favorite matches the filter), RECOMMENDED (gateway isPreferred, preferred order), ALL MODELS (remainder, gateway order). Each model appears in exactly one section (A3). Plain headers, no provider grouping — same as the app's groupForModel behaviour for gateway catalogs.
  • Server-synced favorites. Each row has an icon-only star toggle (aria-pressed + explicit action label) that adds/removes the model from the user's server-side favorites via modelPreferences.get / addFavorite / removeFavorite. These are the same per-user favorites the web and mobile apps read and write — same tRPC router, same user_model_preferences rows, keyed by gateway model id — so a star in the extension shows up in the app and vice versa (A5/A6). Favorites are per user, not per conversation and not per organization; switching organization refetches (the router filters by the org's allowed models).
  • Row chrome mirrors the app: model name, mono model id, Free chip (isFree && !byok), BYOK chip, and the Data collected icon (BookOpenCheck, accessible name) when mayTrainOnYourPrompts (A4).
  • Current model is marked with a check icon and aria-current="true", and the overlay opens with the selected row scrolled into view (A8, verified by E2E).
  • Failure states per the feature matrix: retryable favorites load ("Couldn't load favorites." + Retry, models stay selectable), optimistic toggle rollback ("Couldn't update favorites."), and a terminal per-token+organization state for 401/403 / tRPC UNAUTHORIZED/FORBIDDEN ("Favorites aren't available here.", no CTA, stars hidden) that clears on any successful get and never latches session-wide.
  • The trigger keeps the old control's exact contract: accessible name Model, visible text Loading models... while empty/loading, same disabled flags, and the existing Retry models footer error row is unchanged (A1b/A9). Existing selectors keep resolving; Chrome and Firefox behave identically with no new manifest permission (A10).

Simpler-shape decisions (S1–S4)

  • S1: no tRPC client dependency. The picker talks raw HTTP to /api/trpc/modelPreferences.* — the endpoint has no transformer, so a query is GET …/modelPreferences.get?input=<url-encoded JSON> and a mutation is POST with the raw input JSON body. Two small functions + zod parsing instead of pulling @trpc/client and the server router's types into the MV3 bundle. Org scoping goes in the procedure input (input.organizationId), never in the x-kilocode-organizationid header (which getUserFromAuth validates as a UUID and would 400 into the terminal state for non-UUID E2E orgs).
  • S2: full-panel overlay, reusing the existing overlays' shape (fixed inset-0, role="dialog", labelled close button) — no popover layer, no new dependency, no new dialog semantics (close button only; focus moves to search on open).
  • S3: no list virtualization — the gateway catalog is a few hundred models (noted in code).
  • S4: no variant chips in rows — the thinking-effort select and its reset behaviour are untouched.

Test infrastructure

  • tests/e2e/kilo-api-fixture.ts now mocks /api/trpc/modelPreferences.* with path-prefix matching, real tRPC success envelopes, stateful favorites, and opt-in 401/403/500; shared model-picker-e2e-helpers.ts (selectModelById waits for the row inside the dialog; expectSelectedModelId asserts the trigger's data-model-id) replaces every selectOption/toHaveValue on the old control, mirrored in the Selenium harness.
  • New Chrome spec tests/e2e/model-picker.test.ts covers happy path, row chrome, search, no-match + Clear search, favorites toggle + section placement, retryable 500, terminal 403 (no CTA, no stars), toggle rollback, empty catalog, the org-scoped wire assertion (org selected → input.organizationId; personal → no input), and scroll-into-view on a long catalog. New Firefox scenario covers open/search/select.
  • tests/e2e/local-backend-live.test.ts: live device-auth sign-in now resolves the stack's canonical origin by probing /users/after-sign-in (no-redirect) before navigating — the shared stack may run with APP_URL_OVERRIDE at a LAN IP, and cookies set off-origin made the post-login redirect silently drop authentication. The live favorites round-trip is personal-only, ensure-favorite (reads prior state, never blind-toggles), and restores prior state in finally because fl@fl.fl is a shared account.
  • tests/e2e/design-tokens.test.ts: the seeded-transcript wait gets an explicit 30s timeout; under full-suite load on a busy machine the first reload can exceed the 5s Playwright default (observed flaking locally in-suite while passing in isolation; style assertions unchanged).

Verification

  • pnpm format; pnpm --filter kilo-extension verify (448 vitest) ✓
  • pnpm --filter kilo-extension build, build:firefox
  • pnpm --filter kilo-extension e2e:chrome: 91 passed, 11 skipped, 0 failed ✓
  • pnpm --filter kilo-extension e2e:firefox: 34/34 workflows passed ✓
  • pnpm --filter kilo-extension e2e:local against the shared local stack: 11/11 passed, including the personal favorites round-trip (star → persisted across reload → FAVORITES section) against the real modelPreferences router ✓

Notes

  • Assumption verified live: the extension's stored bearer token is accepted by /api/trpc (getUserFromAuth honors Authorization: Bearer), so no backend change was needed.
  • The parallel side-panel polish run owns adjacent files; the only shared surface is the two-prop wiring in agent-chat-panel.tsx. Whichever PR merges second integrates main and re-runs the extension gate.

@iscekic iscekic self-assigned this Jul 27, 2026
Comment thread apps/extension/src/shared/model-preferences-client.ts
Comment thread apps/extension/entrypoints/sidepanel/model-preferences-state.ts Outdated
Comment thread apps/extension/tests/e2e/kilo-api-fixture.ts Outdated
Comment thread apps/extension/tests/e2e/local-backend-live.test.ts Outdated
@kilo-code-bot

kilo-code-bot Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Executive Summary

Incremental diff adds a single test-only wait (boot hydration gate) in design-tokens.test.ts; no functional or test-correctness issues found.

Files Reviewed (1 file)
  • apps/extension/tests/e2e/design-tokens.test.ts
Previous Review Summaries (2 snapshots, latest commit 9e63002)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit 9e63002)

Status: No Issues Found | Recommendation: Merge

Executive Summary

All four previously flagged findings are resolved: the organizationId favorites concern was a false positive (verified against apps/web/src/routers/model-preferences-router.tsaddFavorite/removeFavorite are user-scoped by design, with no org-scoped write path to drop), and the three WARNING items (blocking invalidateQueries await, origin-less route matcher, unguarded test cleanup) are fixed as diffed.

Files Reviewed (3 files)
  • apps/extension/entrypoints/sidepanel/model-preferences-state.ts
  • apps/extension/tests/e2e/kilo-api-fixture.ts
  • apps/extension/tests/e2e/local-backend-live.test.ts

Previous review (commit 36f3895)

Status: 4 Issues Found | Recommendation: Address before merge

Executive Summary

Favorite add/remove mutations silently drop organizationId, so starring a model while an organization is selected can write to the wrong scope (apps/extension/src/shared/model-preferences-client.ts).

Overview

Severity Count
CRITICAL 1
WARNING 3
SUGGESTION 0
Issue Details (click to expand)

CRITICAL

File Line Issue
apps/extension/src/shared/model-preferences-client.ts 171 organizationId never sent for addFavorite/removeFavorite mutations (URL and body both omit it)

WARNING

File Line Issue
apps/extension/entrypoints/sidepanel/model-preferences-state.ts 180 Awaited invalidateQueries in finally can block the next queued favorite toggle for seconds
apps/extension/tests/e2e/kilo-api-fixture.ts 128 Route matcher ignores host/origin, unlike every neighboring route in the file
apps/extension/tests/e2e/local-backend-live.test.ts 990 Unguarded cleanup calls can mask the original failure and skip context.close()/rm(userDataDir)
Files Reviewed (20 files)
  • apps/extension/entrypoints/sidepanel/agent-chat-panel.tsx
  • apps/extension/entrypoints/sidepanel/agent-footer-controls.tsx
  • apps/extension/entrypoints/sidepanel/model-picker-row.tsx
  • apps/extension/entrypoints/sidepanel/model-picker.tsx
  • apps/extension/entrypoints/sidepanel/model-preferences-state.ts - 1 issue
  • apps/extension/entrypoints/sidepanel/use-model-preferences.test.ts
  • apps/extension/entrypoints/sidepanel/use-model-preferences.ts
  • apps/extension/src/shared/model-picker-rows.test.ts
  • apps/extension/src/shared/model-picker-rows.ts
  • apps/extension/src/shared/model-preferences-client.test.ts
  • apps/extension/src/shared/model-preferences-client.ts - 1 issue
  • apps/extension/src/shared/side-panel-query-options.test.ts
  • apps/extension/src/shared/side-panel-query-options.ts
  • apps/extension/tests/e2e/conversation-tabs.test.ts
  • apps/extension/tests/e2e/design-tokens.test.ts
  • apps/extension/tests/e2e/firefox-selenium-e2e.ts
  • apps/extension/tests/e2e/kilo-api-fixture.ts - 1 issue
  • apps/extension/tests/e2e/local-backend-live.test.ts - 1 issue
  • apps/extension/tests/e2e/model-picker-e2e-helpers.ts
  • apps/extension/tests/e2e/model-picker.test.ts

Fix these issues in Kilo Cloud


Reviewed by claude-sonnet-5 · Input: 22 · Output: 2.4K · Cached: 415.3K

Review guidance: REVIEW.md from base branch main

@iscekic
iscekic merged commit ed14a62 into main Jul 28, 2026
18 checks passed
@iscekic
iscekic deleted the feat/ext-model-picker-parity branch July 28, 2026 08:44
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.

2 participants