Skip to content

feat(vscode): migrate the providers settings to v2 - #15592

Merged
marius-kilocode merged 14 commits into
settings-foundationfrom
settings-custom-provider
Oct 29, 2026
Merged

marius-kilocode merged 14 commits into
settings-foundationfrom
settings-custom-provider

Conversation

@marius-kilocode

@marius-kilocode marius-kilocode commented Oct 29, 2026 •

Copy link
Copy Markdown
Collaborator

What Problem This Solves

The Providers settings tab on v2 could list and connect integration providers, but there was no way to add a custom provider, edit or remove one, or hide a provider. Users who rely on a custom OpenAI-compatible, OpenAI Responses or Anthropic Messages endpoint had no equivalent on v2.

Why This Change Was Made

The tab now owns the custom provider flow on the v2 config and credential APIs:

  • A custom provider is the v2 config value providers.<id> = { name, package, settings.baseURL, headers, models.<id>.{ name, capabilities } }, written through useSettingsConfig().set so the host writes the config file. The dialog maps the model Reasoning toggle to capabilities.reasoning.supported and the Image toggle to capabilities.input (["text"] or ["text","image"]). The v1 provider package names map to the built-in v2 packages (@opencode/ai/providers/openai-compatible, .../openai, .../anthropic-compatible), so a saved provider loads without an npm install.
  • The API key is a v2 credential, not config. The config provider registers a key method for every configured provider, so the dialog stores the key with settings.connectKey (api.integration.connect.key). Nothing secret is written to the config file; the edit dialog shows the credentials as a mask. The {env:VAR} spelling still writes providers.<id>.env.
  • Disabled providers map to policies: [{ action: "provider.use", resource: "<id>", effect: "deny" }], which removes the provider from provider.list and from the model list. The section preserves the other policies and unsets policies when the last deny is removed. A legacy disabled_providers entry arrives as a normalized deny policy, and a legacy enabled_providers allow-list keeps its resource: "*" catch-all and allow statements through a write (it is not shown as a disabled provider). The write converts the legacy key into policies because config.source and the host alias map know it.
  • Remove unsets providers.<id> in the scope that defines it (the host removes the provider.<id> alias in the same pass) and clears the credential.
  • Editing merges into the current providers.<id> (read at save time), so fields the dialog does not model survive: other provider settings, body, model limit, cost, variants and extra capability keys. Unchecking Image only removes image from capabilities.input, keeping pdf/audio; a reasoning toggle that stays on keeps the effort, budget and toggle overlay; an untouched API key field keeps the stored env and credential; and empty headers or env are removed, because the dialog is the source of truth for those fields.

This PR also reported a write blocker: jsonc-parser throws (Can not delete in empty document) when a delete targets a path the document does not define, which made every write fail unless the legacy alias already existed. The guard landed in the foundation (settings-foundation), so this PR no longer changes config-file-edit.ts; the tests here cover the providers case.

Parity

v1 control v2 key/API status
Add custom provider dialog (provider ID, display name, provider package, base URL, API key, models, headers) providers.<id> through useSettingsConfig().set; key through settings.connectKey done
Validation (ID format/duplicate, name, base URL, model and header rows) CustomProviderValidation.validateCustomProvider done
Invalid {env:NAME} key blocks the save with "Request failed: Invalid environment variable name" same toast and text, no config or credential write done
Model fields: id, name, Reasoning, Image models.<id>.name, capabilities.reasoning.supported, capabilities.input done
Toggle reasoning / image for all, add / remove model, add / remove header same controls done
Custom provider row with Edit and Delete connected row from providers.<id>; Edit opens the dialog, Delete unsets providers.<id> and clears the credential done
Change API key hidden for a custom provider (the dialog owns it) canChangeKey returns false for a custom provider done
Advanced (open the global config file) host openFile on the defining scope's file done
Disabled providers list, add and enable policies with action: "provider.use", effect: "deny" done
Saving re-enables a disabled provider whose id is reused the save clears the matching deny policy done
Fetch models from the provider server needs a new host message and network access; v2 has no model-discovery API out of scope
Anaconda Desktop dialog and its provider row action v2 has no Anaconda Desktop integration out of scope

Out of scope, with reasons:

  • Fetch models from the provider server. v1 fetched the model list through an extension-host message. On v2 there is no model-discovery API and no host message for it, so the button is not shown. Model rows are added by hand, which is fully covered.
  • Anaconda Desktop dialog. It has no v2 integration, so the row action is not carried.

Known divergences (small, deliberate):

  • Removing the last disabled provider unsets policies instead of writing [] as v1 did. The resulting policy set is the same, and the file does not keep an empty key.
  • @ai-sdk/anthropic maps to @opencode/ai/providers/anthropic-compatible, not .../anthropic, because the dialog always supplies a custom base URL (the official anthropic package targets the vendor API).

Proof

Screenshots, v1 on the left and v2 on the right.

Providers tab Custom provider dialog (empty)
v1 providers tab with the custom provider entry v1 custom provider dialog
v2 providers tab with the custom provider entry v2 custom provider dialog

The v1 dialog header also shows a Fetch models button (out of scope above). The v2 dialog is otherwise the same layout, labels and order.

Dialog validation Dialog filled
v2 custom provider dialog with validation errors v2 custom provider dialog filled in
Custom provider row Disabled providers
v1 disabled providers section v1 custom provider row
v2 custom provider row with Change API key, Edit provider and Delete v2 disabled providers section

The model picker shows the custom provider and its model without a server restart:

v2 chat model picker showing the custom provider model

End-to-end check in an isolated dev profile (VS Code 1.106, launch-vscode --mode dev, no model prompts):

  • Added a custom provider (acmeprov, OpenAI-compatible, base URL, one model, API key). The config file received providers.acmeprov with settings.baseURL and models.acme-chat-1.capabilities.input = ["text"]; no key appears in the file.
  • The credential table holds integration_id=acmeprov, {"type":"key","key":...}.
  • api.model.list and the chat model picker show acmeprov/acme-chat-1 at once, with no server restart.
  • Reload Window keeps both the provider and the model.
  • An external edit of the config file (adding a second provider) appears in the tab without a reload.
  • Disable wrote policies: [{ action: "provider.use", resource: "<id>", effect: "deny" }] and hid the provider from the list; Enable removed the policy and unset the key when the last deny was removed.
  • The edit dialog pre-fills the values, shows the key as a mask, and preserves hand-added fields: after editing, providers.acmeprov still carried settings.timeout, body, and the model limit, cost, variants and capabilities.output.
  • An API key of {env:lower} keeps the dialog open with the v1 toast "Request failed: Invalid environment variable name"; no config entry and no credential are written. {env:ACME_KEY} saves env: ["ACME_KEY"] with no credential, and an edit with the key untouched keeps that env.

v2 custom provider dialog rejecting an invalid env variable name

Automated: bun typecheck and bun run lint pass in packages/kilo-vscode; bun test passes 166 tests (0 fail), including test/custom-provider.test.ts. The root bun run check fails in this environment before reaching this package (bun check is not a known subcommand for the installed bun, so @opencode/theme, @opencode/util and @opencode/codemode report Script not found "check"); CI runs the real check.

Progress map

vscode: 1769 to 1766 open cells (3 dispositions). The four ported files close their module cells by name.

Done, and why:

  • by file name (5): webview-ui/src/components/settings/ProvidersTab.tsx, CustomProviderDialog.tsx, CustomProviderModelCard.tsx, CustomProviderValidation.ts, provider-visibility.ts are ported at their v1 paths.
  • ported (3, dispositions): src/shared/custom-provider.ts (key spelling and masked key now live in the dialog and validation, the key is a credential and the deep-merge sentinels are gone), tests/unit/custom-provider-dialog-validate.test.ts and tests/unit/provider-visibility.test.ts (assertions carried by packages/kilo-vscode/test/custom-provider.test.ts and the tab).

Still open:

  • AnacondaDesktopDialog.tsx and the Fetch models behavior are out of scope for this unit, with the reasons above.

Part of #14386

Add the custom provider dialog, model cards, validation and provider
rows (edit and remove) to the v2 Providers tab. Store the API key as a
v2 credential and disabled providers as provider.use deny policies.

Also guard the alias removal in applyConfigEdits: jsonc-parser throws
when a delete targets a path the document does not define.
@github-actions github-actions Bot added the area:vscode VS Code extension label Oct 29, 2026
@github-actions

github-actions Bot commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

📈 v2 migration progress: 6.3% → 6.4% (+8 done, 0 total, 0 dropped; 626/9819 cells)

Placement: 0 new marked lines in shared files, 0 new kilo_port annotations, 0 new core-internal imports.

Ratchets: 27 core-internal imports (base 27); shared patches by rung 35 seam, 25 patch (base 35 seam, 25 patch).

Feature Before After Change
vscode 292/2057 300/2057 +5 modules done; +3 tests done
8 cells closed
  • module:packages/kilo-vscode/src/shared/custom-provider.ts
  • module:packages/kilo-vscode/tests/unit/custom-provider-dialog-validate.test.ts
  • module:packages/kilo-vscode/tests/unit/custom-provider.test.ts
  • module:packages/kilo-vscode/tests/unit/provider-visibility.test.ts
  • module:packages/kilo-vscode/webview-ui/src/components/settings/CustomProviderDialog.tsx
  • module:packages/kilo-vscode/webview-ui/src/components/settings/CustomProviderModelCard.tsx
  • module:packages/kilo-vscode/webview-ui/src/components/settings/CustomProviderValidation.ts
  • module:packages/kilo-vscode/webview-ui/src/components/settings/provider-visibility.ts

Map on v2 · how cells close

@kilo-code-bot

kilo-code-bot Bot commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

The new commit (bdced94) reworks unmaskKey so asterisks left over from the mask around typed text are dropped even when only part of the mask is replaced, and pins the behaviour with ****x*** → x and a 9-asterisk case in test/custom-provider.test.ts. The earlier finding on unmaskKey (mask asterisks persisted into the stored key when typing over a selection) is resolved for every edit shape that changed code and tests cover. No markdown documentation files change, and the changed code adds no listeners, timers or caches, so there is no new memory-leak risk.

Files Reviewed (2 files)
  • packages/kilo-vscode/test/custom-provider.test.ts - no issues
  • packages/kilo-vscode/webview-ui/src/components/settings/CustomProviderValidation.ts - no issues
Previous Review Summaries (7 snapshots, latest commit 2072844)

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

Previous review (commit 2072844)

Status: 1 Issue Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 0
SUGGESTION 1

The new commit (2072844) extracts the first-edit mask handling into unmaskKey and the re-enable-on-save write into enableProvider, with tests for both. enableProvider is behaviourally identical to the inline block it replaces (same disabledProviderIds input, same withDisabledProviders output, undefined when the id is not disabled, so the dialog still has no write when nothing changes, and [] still takes the unset path). unmaskKey fixes the pasted-key case the previous review raised — a pasted key containing * is now kept — but it still lets mask asterisks into the stored key when the user replaces part of the mask by typing over a selection, which the new finding below covers. The previous unmaskKey suggestion for pasted keys and the test gap for the re-enable list are otherwise resolved by this commit. No markdown documentation files are changed, and the changed code adds no listeners, timers or caches, so there is no new memory-leak risk.

Issue Details (click to expand)

SUGGESTION

File Line Issue
packages/kilo-vscode/webview-ui/src/components/settings/CustomProviderValidation.ts 158 unmaskKey returns the mask's asterisks when part of the mask is replaced by typing over a selection, and validateCustomProvider stores that string as the key
Files Reviewed (4 files)
  • packages/kilo-vscode/test/custom-provider.test.ts - no issues (new unmaskKey and enableProvider tests pass the happy paths)
  • packages/kilo-vscode/webview-ui/src/components/settings/CustomProviderDialog.tsx - no issues (helper wiring equivalent to the previous inline block; mask check still gated on !apiTouched)
  • packages/kilo-vscode/webview-ui/src/components/settings/CustomProviderValidation.ts - 1 suggestion (mask replacement)
  • packages/kilo-vscode/webview-ui/src/components/settings/provider-visibility.ts - no issues (catch-all and other policies preserved)

Fix these issues in Kilo Cloud

Previous review (commit 355cb2a)

Status: 2 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 0
SUGGESTION 2

The new commit (355cb2a) adds the {env:NAME} name check that v1 had: validateCustomProvider returns errors.apiKey for an {env:...} spelling whose name fails ENV_NAME, the save is blocked before any config or credential write, and the dialog shows the v1 "Request failed" toast. The check reuses ENV_REF, so it matches exactly what parseEnvKey accepts, and a test pins the rejection. The remainder of the commit is formatting only. The earlier WARNINGs about the Reasoning sub-fields, empty headers, the * allow-list catch-all and the wholesale rewrite are resolved in current code (CustomProviderValidation.ts:189, :200, provider-visibility.ts:44-71, mergeProviderConfig), and the visibility helpers now have tests.

Issue Details (click to expand)

SUGGESTION

File Line Issue
packages/kilo-vscode/webview-ui/src/components/settings/CustomProviderDialog.tsx 396 The first key edit strips every *, so a pasted key containing * is silently truncated
packages/kilo-vscode/webview-ui/src/components/settings/CustomProviderDialog.tsx 224 The re-enable-on-save write still has no test for the written array, scope or unset path
Files Reviewed (7 files)
  • packages/kilo-vscode/test/custom-provider.test.ts - no issues (new invalid-env test; visibility, merge and reasoning tests present)
  • packages/kilo-vscode/webview-ui/src/components/settings/CustomProviderValidation.ts - no issues (env check matches parseEnvKey; reasoning/headers merges verified)
  • packages/kilo-vscode/webview-ui/src/components/settings/CustomProviderDialog.tsx - 2 suggestions (carried, current lines)
  • packages/kilo-vscode/webview-ui/src/components/settings/ProvidersTab.tsx - no issues (re-verified: config-removal failure suppresses the toast, removeCustom has a rejection path, * deny excluded)
  • packages/kilo-vscode/webview-ui/src/components/settings/provider-visibility.ts - no issues (re-verified: catch-all preserved, helpers tested)
  • packages/kilo-vscode/webview-ui/src/components/settings/CustomProviderModelCard.tsx - no issues (re-verified)
  • migration-tracking/progress/dispositions.json - no issues

No memory-leak risk in the changed code: the additions are a validation predicate and a toast, with no listeners, timers or caches added. No markdown documentation files are changed in this PR.

Fix these issues in Kilo Cloud

Previous review (commit 5dbb526)

Status: 2 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 0
SUGGESTION 2

The new commit addresses the counter-check items: the masked key field now strips stray asterisks on a middle edit, the Delete toast waits for the config removal to succeed, canChangeKey hides the key action for custom providers (matching v1's canChangeProviderKey), and a reused disabled provider id is re-enabled on save. The previous wildcard warning is resolved: disabledProviderIds excludes the resource: "*" catch-all and withDisabledProviders re-emits it plus the allow statements, so an enabled_providers allow-list keeps its semantics through a write, checked against the findLast resolution in packages/core/src/config/plugin/policy.ts.

Issue Details (click to expand)

SUGGESTION

File Line Issue
packages/kilo-vscode/webview-ui/src/components/settings/CustomProviderDialog.tsx 388 The first key edit strips every *, so a pasted key containing * is silently truncated
packages/kilo-vscode/webview-ui/src/components/settings/CustomProviderDialog.tsx 222 The new re-enable-on-save write has no test for the written array, scope or unset path
Files Reviewed (7 files)
  • migration-tracking/progress/dispositions.json - no issues
  • packages/kilo-vscode/test/custom-provider.test.ts - no issues (covers validateCustomProvider, mergeProviderConfig and the provider-visibility helpers)
  • packages/kilo-vscode/webview-ui/src/components/settings/CustomProviderDialog.tsx - 2 suggestions
  • packages/kilo-vscode/webview-ui/src/components/settings/CustomProviderModelCard.tsx - no issues
  • packages/kilo-vscode/webview-ui/src/components/settings/CustomProviderValidation.ts - no issues (reasoning and header merges verified in the current code)
  • packages/kilo-vscode/webview-ui/src/components/settings/ProvidersTab.tsx - no issues (config-removal failure now suppresses the success toast; the write layer already surfaces the error)
  • packages/kilo-vscode/webview-ui/src/components/settings/provider-visibility.ts - no issues

No memory-leak risk was found in the new code: the additions are a save-time policy rewrite and a toast guard, and no listeners, timers or caches were added. No markdown documentation files are changed in this PR.

Fix these issues in Kilo Cloud

Previous review (commit c980a88)

Status: No Issues Found | Recommendation: Merge

The incremental change extracts the disabled-provider policy logic into provider-visibility.ts and fixes the previous warning: disabledProviderIds now ignores the resource: "*" catch-all that a legacy enabled_providers allow-list normalizes to, and withDisabledProviders re-emits it (and the allow statements) instead of dropping them. Verified against ConfigNormalize.normalizePolicies and the findLast policy resolution in packages/core/src/config/plugin/policy.ts, the written policies array keeps the allow-list semantics. The new helpers are covered by tests.

Files Reviewed (4 files)
  • packages/kilo-vscode/test/custom-provider.test.ts
  • packages/kilo-vscode/webview-ui/src/components/settings/CustomProviderDialog.tsx
  • packages/kilo-vscode/webview-ui/src/components/settings/ProvidersTab.tsx
  • packages/kilo-vscode/webview-ui/src/components/settings/provider-visibility.ts

Previous review (commit a5419ad)

Status: 1 Issue Found | Recommendation: Address before merge

Overview

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

WARNING

File Line Issue
packages/kilo-vscode/webview-ui/src/components/settings/ProvidersTab.tsx 60 A legacy enabled_providers allow-list now surfaces its generated resource: "*" deny as a disabled provider, and the next write drops that catch-all

The previous review's findings are addressed in this range: the reasoning merge now preserves effort/budget while the toggle stays on, an empty dialog clears stored headers, the tab reads the normalized policies instead of never-matching legacy keys, the dialog's duplicate-ID check only treats deny policies as disabled, and the edit merge base is read from the live config at save time. The remaining warning is in the normalized-policy read: enabled_providers normalizes to a provider.use deny on resource: "*" plus the allow entries, so the tab renders that wildcard as a provider ID and writeDisabled drops it before writing policies (the write also removes enabled_providers), which converts an allow-list into default-allow.

No memory-leak risk was found in the new code: no listeners, timers or caches were added, and the new tests are pure helpers. No documentation files are changed in this range.

Files Reviewed (4 files)
  • packages/kilo-vscode/test/custom-provider.test.ts - no issues (new tests match the merge behaviour)
  • packages/kilo-vscode/webview-ui/src/components/settings/CustomProviderDialog.tsx - no new issues
  • packages/kilo-vscode/webview-ui/src/components/settings/CustomProviderValidation.ts - no new issues
  • packages/kilo-vscode/webview-ui/src/components/settings/ProvidersTab.tsx - 1 issue

Fix these issues in Kilo Cloud

Previous review (commit 373810c)

Status: 5 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 2
SUGGESTION 3

The merge that preserves unmodeled config fields now survives an edit, the apiKeyTouched flag fixes the env/key interaction, and the duplicate model-card error is gone. The merge still loses two values the dialog does model (nested reasoning fields and a fully cleared headers), and the new legacy provider-list reads cannot return anything because the webview reads the normalized config.

Issue Details (click to expand)

WARNING

File Line Issue
packages/kilo-vscode/webview-ui/src/components/settings/CustomProviderValidation.ts 180 capabilities.reasoning is replaced, dropping toggle/effort/budget
packages/kilo-vscode/webview-ui/src/components/settings/CustomProviderValidation.ts 188 Clearing every header row leaves the old headers in the config

SUGGESTION

File Line Issue
packages/kilo-vscode/webview-ui/src/components/settings/ProvidersTab.tsx 68 The legacy provider-list reads never return a value (normalized config)
packages/kilo-vscode/webview-ui/src/components/settings/CustomProviderDialog.tsx 140 Allow policies are treated as disabled IDs, bypassing the duplicate-ID guard
packages/kilo-vscode/webview-ui/src/components/settings/CustomProviderDialog.tsx 215 Merge base is the snapshot captured when the dialog opened
Files Reviewed (7 files)
  • migration-tracking/progress/dispositions.json - no issues
  • packages/kilo-vscode/test/custom-provider.test.ts - no issues (new tests cover the merge and the key-touched cases)
  • packages/kilo-vscode/webview-ui/src/components/settings/CustomProviderDialog.tsx - 2 issues
  • packages/kilo-vscode/webview-ui/src/components/settings/CustomProviderModelCard.tsx - no issues (errors still render through TextField)
  • packages/kilo-vscode/webview-ui/src/components/settings/CustomProviderValidation.ts - 2 issues
  • packages/kilo-vscode/webview-ui/src/components/settings/ProvidersTab.tsx - 1 issue
  • packages/kilo-vscode/webview-ui/src/components/settings/provider-visibility.ts - no issues

Verified while reviewing: mergeProviderConfig keeps provider body/compatibility and model limit/cost/variants (covered by tests); removeCustom now catches a failed credential removal; the model-card error span was a duplicate of TextField's own error. The legacy key reads were checked against ConfigNormalize.normalize and KiloConfigPolicy.parse in core. No memory-leak risk found in the new code: no listeners, timers or caches were added, and the async removeCustom IIFE settles. The earlier settings-paths/settings-config findings are not re-listed because those files are part of the settings-foundation base branch now, not this PR's diff.

Fix these issues in Kilo Cloud

Previous review (commit d94d55a)

Status: 3 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 3
SUGGESTION 3

The two problems from the previous review that mattered most are fixed: an edit now merges into the existing providers.<id> instead of replacing it, and removeCustom has a rejection path. The new merge is incomplete in two places, and the new global-config fallback resolves its paths differently from the spawned server. Previous findings on env handling, the duplicate model error and the missing provider-visibility tests are resolved.

Issue Details (click to expand)

WARNING

File Line Issue
packages/kilo-vscode/webview-ui/src/components/settings/CustomProviderValidation.ts 166 capabilities.input and nested reasoning fields are still replaced on every save
packages/kilo-vscode/webview-ui/src/components/settings/CustomProviderValidation.ts 171 Clearing the last header does not remove headers
packages/kilo-vscode/src/settings-paths.ts 10 KILO_CONFIG_DIR/OPENCODE_CONFIG_DIR precedence differs from the server

SUGGESTION

File Line Issue
packages/kilo-vscode/src/settings-paths.ts 12 The app directory is fixed to kilo2 in the server
packages/kilo-vscode/webview-ui/src/v2/settings-config.tsx 156 pending has no caller
packages/kilo-vscode/webview-ui/src/components/settings/CustomProviderDialog.tsx 214 The merge base is the config snapshot taken when the dialog opened
Files Reviewed (12 files)
  • packages/kilo-vscode/src/config-file-edit.ts - no issues (serialize keeps the chain usable after a rejection; covered by new tests)
  • packages/kilo-vscode/src/protocol.ts - no issues
  • packages/kilo-vscode/src/settings-host.ts - no issues (canonical-path matching and the missing-directory fallback verified against KiloConfigFiles and the server env)
  • packages/kilo-vscode/src/settings-paths.ts - 2 issues
  • packages/kilo-vscode/test/config-file-edit.test.ts - no issues
  • packages/kilo-vscode/test/custom-provider.test.ts - no issues
  • packages/kilo-vscode/test/settings-paths.test.ts - no issues (assertions match the intended dev/XDG layout)
  • packages/kilo-vscode/webview-ui/src/components/settings/CustomProviderDialog.tsx - 1 issue
  • packages/kilo-vscode/webview-ui/src/components/settings/CustomProviderModelCard.tsx - no issues
  • packages/kilo-vscode/webview-ui/src/components/settings/CustomProviderValidation.ts - 2 issues
  • packages/kilo-vscode/webview-ui/src/components/settings/ProvidersTab.tsx - no issues
  • packages/kilo-vscode/webview-ui/src/v2/settings-config.tsx - 1 issue

Verified while reviewing: serialize normalizes tail so one rejected task cannot wedge later writes; canonicalPath/writeCandidates line up with KiloConfigFiles (.kilo2 filter, .jsonc order); resolveGlobalConfigDir matches dev-isolated.ts (XDG_CONFIG_HOME = <profile>/config, app kilo2) and global-roots.ts for the XDG case; kilo-cli env.ts translation and the bundled build define were read to compare the fallback against the actual server. No memory-leak risk was found in the new code: settingsHost still disposes its configuration listener, the write queue is bounded by its callers, and no new listeners, timers or caches were added. Inline comments could not be published (HTTP 502 on the review API), so every finding is listed here only.

Fix these issues in Kilo Cloud


Reviewed by deepseek-flash-latest · Input: 85.4K · Output: 10K · Cached: 340.7K

Review guidance: REVIEW.md from base branch settings-foundation

Preserve unmodeled provider and model config fields when the custom
provider dialog edits an existing provider, drop the stored env var
when a key is typed, handle a rejected credential removal, remove the
duplicate model error text, and add tests for provider-visibility and
the merge. Merge the foundation config write fix and drop the stray
kilo2.json test artifact.
Merge the other input modalities (pdf, audio) instead of replacing the
list, clear the stored env only when the API key field is actually
changed, and cover both in the tests.
The config aliases map disabled_providers, enabled_providers and
experimental.policies to policies. Read them so the disabled list shows
an existing legacy value and a write converts it instead of dropping it.
Keep the reasoning effort, budget and toggle overlay when the toggle
stays on, remove headers when the dialog has none, read the disabled
ids from deny policies only, merge against the current config at save
time, and drop the legacy provider-list reads the server already
normalizes into policies.
An enabled_providers allow-list normalizes to a '*' deny plus allow
statements. Skip the catch-all when listing disabled providers and keep
it when writing policies, so disabling a provider does not turn the
allow-list into default-allow. Covered by tests.
@WebReflection

Copy link
Copy Markdown
Contributor

AI counter-check findings

  • packages/kilo-vscode/webview-ui/src/components/settings/CustomProviderDialog.tsx:369 (API-key onChange) — a mid-mask edit corrupts the stored credential. The first change strips only leading * from the masked value (value.replace(/^\*+/, "")). If the user clicks inside the masked field and types (e.g. ******** → ****x****), the asterisks are kept, the garbage string passes validation (validateCustomProvider has no key format check, CustomProviderValidation.ts:206 returns it as key), and it is persisted verbatim via settings.connectKey. The resulting provider silently fails auth. Stripping mask characters anywhere (or treating any value containing the mask as a replacement) would fix it.

  • packages/kilo-vscode/webview-ui/src/components/settings/ProvidersTab.tsx:~140 (disconnect) — success toast is shown even when the config removal failed. removal = config.unset(...) is awaited in the success branch of props.settings.disconnect(item).then(...), but a false return from config.unset (write failed) still shows the "disconnected" toast while providers.<id> remains. (removeCustom handles this correctly by returning early on !ok.)

@WebReflection WebReflection left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Counter-review findings in here #15592 (comment)

Approving in case you think those concerns should not be addressed (or have been addressed already).

Strip the mask characters anywhere, not only at the start, so an edit
inside the masked key field cannot persist stray asterisks. Show the
disconnect toast only when the config removal succeeded, and re-enable a
disabled provider whose id the dialog reuses (the validation allows the
reuse, so the save must clear the deny).
@marius-kilocode

Copy link
Copy Markdown
Collaborator Author

@WebReflection I am migrating the components as is. I won't fix any bugs that where there previously, could you prompt your review agent to focus on integration issues and migration issues instead?

@marius-kilocode
marius-kilocode merged commit a07c139 into settings-foundation Oct 29, 2026
27 checks passed
@marius-kilocode
marius-kilocode deleted the settings-custom-provider branch October 29, 2026 13:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:vscode VS Code extension

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants