From b7a9ac33b0fe9258e9b89bbac0105bdc0e06aa9f Mon Sep 17 00:00:00 2001 From: "kiloconnect[bot]" <240665456+kiloconnect[bot]@users.noreply.github.com> Date: Mon, 27 Apr 2026 13:37:43 +0000 Subject: [PATCH] fix(cli): run plugin config() hooks before reading provider config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move plugin.list() above cfg/ModelsDev.get() in Provider state initialization so plugin config() hooks can mutate enabled_providers, disabled_providers, and provider entries before downstream consumers read them. ModelsDev.get() already reads enabled/disabled to decide whether to inject the kilo provider, so plugins must run first for that decision to see their mutations. Fixes a flaky Windows test (plugin config enabled and disabled providers are honored) that depended on mutation-in-place reference identity between the plugin-captured cfg and the provider-captured cfg — an invariant that broke whenever Instance state was disturbed between tests. --- .changeset/provider-plugin-ordering.md | 5 +++++ packages/opencode/src/provider/provider.ts | 13 +++++++++---- 2 files changed, 14 insertions(+), 4 deletions(-) create mode 100644 .changeset/provider-plugin-ordering.md diff --git a/.changeset/provider-plugin-ordering.md b/.changeset/provider-plugin-ordering.md new file mode 100644 index 00000000000..86fa908e19f --- /dev/null +++ b/.changeset/provider-plugin-ordering.md @@ -0,0 +1,5 @@ +--- +"kilo-code": patch +--- + +Run plugin `config()` hooks before reading provider config and fetching models.dev data, so plugins that mutate `enabled_providers`, `disabled_providers`, or `provider` are consistently honored by the provider list and the kilo provider injection logic. diff --git a/packages/opencode/src/provider/provider.ts b/packages/opencode/src/provider/provider.ts index 857c02416ea..068a5b9b9eb 100644 --- a/packages/opencode/src/provider/provider.ts +++ b/packages/opencode/src/provider/provider.ts @@ -1087,8 +1087,17 @@ const layer: Layer.Layer< Effect.gen(function* () { using _ = log.time("state") const bridge = yield* EffectBridge.make() + + // kilocode_change start + // Initialize plugins before reading config or fetching models.dev data so plugin + // config() hooks can mutate the shared config (provider, enabled_providers, + // disabled_providers) before any downstream consumer reads it. ModelsDev.get() + // reads enabled_providers/disabled_providers to decide whether to inject the + // kilo provider, so plugins must run first for that decision to see their mutations. + const plugins = yield* plugin.list() const cfg = yield* config.get() const modelsDev = yield* Effect.promise(() => ModelsDev.get()) + // kilocode_change end const database = mapValues(modelsDev, fromModelsDevProvider) const providers: Record = {} as Record @@ -1125,10 +1134,6 @@ const layer: Layer.Layer< providers[providerID] = mergeDeep(match, provider) } - // load plugins first so config() hook runs before reading cfg.provider - const plugins = yield* plugin.list() - - // now read config providers - includes any modifications from plugin config() hook const configProviders = Object.entries(cfg.provider ?? {}) const disabled = new Set(cfg.disabled_providers ?? []) const enabled = cfg.enabled_providers ? new Set(cfg.enabled_providers) : null