fix: stop Agents dead-ending on unsupported providers (#26841)

Configuring only a GitHub Copilot provider left the Agents page stuck on
"set up a provider then add a model", even with a provider and models
configured. The catalog dropped any provider type that NormalizeProvider
did not recognize, so a Copilot-only deployment looked identical to an
empty one and never unlocked the page.

The Agents harness cannot use Copilot: it needs a per-request token only
an official Copilot client can mint, and the harness is not one. Instead
of dropping such providers, the catalog now reports them as unsupported
so the UI can explain the dead end and point elsewhere, rather than ask
for setup that already happened. The providers stay usable through the
AI Gateway proxy.

Support is derived from the provider type, not stored, so there is no
migration. codersdk.IsAgentsUnsupportedProviderType is the single source
of truth, consulted by the chatd catalog and, through the generated
AgentsUnsupportedProviderTypes list, the frontend.

The diff also carries unrelated modernization of nearby db2sdk and
chatprovider helpers (slices.SortFunc, strings.Cut, range-over-int).

Closes CODAGT-627
Refs CODAGT-256
Refs CODAGT-682
This commit is contained in:
Mathias Fredriksson
2026-06-30 18:49:50 +03:00
committed by GitHub
parent a79fcd34af
commit 2fd5ae4323
30 changed files with 589 additions and 37 deletions
+26
View File
@@ -48,6 +48,32 @@ const (
AIProviderTypeCopilot AIProviderType = "copilot"
)
// AgentsUnsupportedProviderType is an AIProviderType the Coder Agents harness
// cannot use. Declaring these as an enum exposes the generated
// AgentsUnsupportedProviderTypes list to the frontend, which labels these
// providers without a per-provider field on the AIProvider response.
type AgentsUnsupportedProviderType string
const (
// AgentsUnsupportedProviderTypeCopilot is GitHub Copilot: it authenticates
// with a per-request token only an official Copilot client can mint, which
// the server-side harness is not.
AgentsUnsupportedProviderTypeCopilot AgentsUnsupportedProviderType = AgentsUnsupportedProviderType(AIProviderTypeCopilot)
)
// IsAgentsUnsupportedProviderType reports whether the Coder Agents harness
// cannot use the provider type. It is the single source of truth, shared by
// the chatd catalog predicate and, via the generated
// AgentsUnsupportedProviderTypes list, the frontend.
func IsAgentsUnsupportedProviderType(t AIProviderType) bool {
switch AgentsUnsupportedProviderType(t) {
case AgentsUnsupportedProviderTypeCopilot:
return true
default:
return false
}
}
// AIProviderSettings is the discriminated container for type-specific
// provider settings stored in ai_providers.settings. Providers that
// need no type-specific configuration (current OpenAI and standard
+11
View File
@@ -701,6 +701,17 @@ type ChatModelProvider struct {
// ChatModelsResponse is the catalog returned from chat model discovery.
type ChatModelsResponse struct {
Providers []ChatModelProvider `json:"providers"`
// UnsupportedProviders lists configured providers the Agents harness
// cannot use, so the UI can explain the empty state.
UnsupportedProviders []ChatUnsupportedProvider `json:"unsupported_providers"`
}
// ChatUnsupportedProvider is a configured provider the Agents harness cannot
// use.
type ChatUnsupportedProvider struct {
// Provider is the provider type, e.g. "copilot".
Provider string `json:"provider"`
DisplayName string `json:"display_name"`
}
// ChatSystemPromptResponse is the response body for the chat system prompt