## Summary
Routes chatd model calls backed by concrete AI Provider rows through the
in-process aibridge transport by default, with deployment options to use
direct provider routing when AI Gateway is disabled or chat AI Gateway
routing is disabled.
- Splits model routing into common, direct provider, and AI Gateway
paths behind a single deployment-mode entry point.
- Builds chatd models through explicit request, route, and options data.
Active API key attribution is passed explicitly instead of being hidden
inside generic model construction.
- For AI Gateway BYOK routes, resolves the user's provider key in chatd,
forwards it through provider-specific auth headers, and sets
`X-Coder-AI-Governance-Token` to the `delegated` marker so aibridge
preserves those headers while still stripping Coder-specific metadata.
- Keeps central provider credentials and deployment fallback credentials
out of forwarded provider auth headers, so AI Gateway central policy
remains authoritative.
- Redacts delegated provider auth from default string formatting to
avoid accidental plaintext logging of user BYOK credentials.
- Covers selected chat models, advisor overrides, title and quickgen
paths, subagent overrides, computer use model selection, and an
integration-style chat turn through the aibridge transport path.
- Persists initiating API key IDs on chat and queued user messages,
including subagent child messages, and fails closed for AI
Gateway-routed model builds without an active key.
- Removes unused `api_key_id` indexes while keeping the persistence
columns and foreign keys.
- Keeps the deployment option available through config and env parsing,
but hides it from CLI help and generated docs.
- Stabilizes the subagent poll fallback test so background CreateChat
processing cannot win the state transition under slower CI environments.
## Tests
- `go test ./coderd/x/chatd -run
'TestAIGatewayProviderAuthForUser|TestAIGatewayProviderAuthRedactsFormatting|TestResolveModelRouteForConfigAIGatewayProviderAuth|TestAIGatewayModelForwardsProviderAuth|TestProcessChat_AIGatewayRoutingUsesDelegatedAPIKey|TestAwaitSubagentCompletion'
-count=1`
- `go test ./coderd/aibridged -run
'TestServeHTTP_DelegatedAPIKey|TestServeHTTP_StripCoderToken' -count=1`
- `git diff --check HEAD~1..HEAD`
- `make lint`
> Mux working on behalf of Mike.
## Problem
Two related symptoms of the same architectural issue: the `dbcrypt`
wrapper is installed inside `enterprise/coderd.New`, so any access to
`options.Database` that happens before `newAPI` runs bypasses
encryption.
**Symptom 1 (reads):** Provider keys added via the admin UI are
encrypted at rest. `BuildProviders` was running *before* `newAPI`,
against the unwrapped store, so the ciphertext was read as-is and shoved
into the keypool as the upstream credential. Anthropic/OpenAI reject it,
and the interception log shows:
```
coderd.aibridged.pool: interception failed ... error="all configured keys failed authentication"
credential_kind=centralized credential_hint=PaPb...4A== credential_length=184
```
**Symptom 2 (writes):** `SeedAIProvidersFromEnv` was also running before
`newAPI`, against the unwrapped store, so env-derived keys
(`CODER_AIBRIDGE_OPENAI_KEY`, indexed `CODER_AIBRIDGE_PROVIDER_<N>_KEY`,
etc.) landed in `ai_provider_keys` as plaintext with `ApiKeyKeyID =
null` even when `CODER_EXTERNAL_TOKEN_ENCRYPTION_KEYS` was set.
## Fix
Move both `SeedAIProvidersFromEnv` and `BuildProviders` to after
`newAPI`, where `options.Database` is the dbcrypt-wrapped store. Writes
encrypt correctly; reads decrypt correctly.
The enterprise closure (`enterprise/cli/server.go`) runs *inside*
`newAPI` and calls `BuildProviders` for the aibridgeproxyd at that
point. Once the agpl seed moves to after `newAPI`, the proxy on first
boot would see no env-seeded providers. Add a matching seed call inside
the enterprise closure before its `BuildProviders` to cover that case.
Seeding is idempotent, so the agpl-side seed running again post-`newAPI`
is a no-op when the rows already exist.
## Known shortcomings
The clean version of this fix would just inherit `ctx` like every other
startup step and place these calls naturally. It can't, for two reasons
that are both about the surrounding handler architecture rather than
this change:
1. **`dbcrypt` wrapping is positioned inside `newAPI`, not around
`options.Database` at creation.** That's why both seed and build have to
wait until after `newAPI` in the first place. The principled fix is to
install the wrapper at the point the store is created (behind a hook the
enterprise build supplies), so every consumer sees a single
authoritative view and the ordering stops mattering. This would also
collapse the duplicated seed call back to a single site.
2. **The handler's shutdown sequence is not deferred.**
`coderAPICloser.Close()` and the other teardown steps run only if
control reaches the `select` at the bottom of the handler. An early
`return` from anywhere in Phase 1 (e.g. seed/build returning
`context.Canceled` when the user hits ctrl-c during startup) skips that
block and orphans all the goroutines `newAPI` spawned — tailnet workers,
gitsync, telemetry batcher, etc. `goleak` then catches them at package
teardown and `TestServer_TelemetryDisabled_FinalReport` fails. Moving
the shutdown into deferred closers (with a `sync.Once`-guarded close to
avoid double-close from the explicit Phase 2 call) is the principled
fix.
For this PR I took the smallest change that fixes the reported bugs: a
detached context (`context.WithoutCancel(ctx)` + a 30s timeout) at the
seed and build call sites in both the agpl and enterprise paths. It lets
the calls complete even if the user cancels during startup, after which
the handler reaches its shutdown select naturally and tears down through
Phase 2. Both shortcomings above are worth addressing separately.
## Test plan
- `make test RUN=TestServer_TelemetryDisabled_FinalReport` with `-race`;
passes locally with `-count=3`.
- Manually verified on a deployment with
`CODER_EXTERNAL_TOKEN_ENCRYPTION_KEYS` set and env-configured providers:
`ai_provider_keys.api_key_key_id` is populated, `api_key` is base64
ciphertext, and upstream auth succeeds.
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
## Problem
When visiting `/ai/settings/governance`, both **AI Governance** and
**Providers** items in the AI settings subnav appear highlighted as
active.
## Cause
`SettingsSidebarNavItem` is built on react-router's `<NavLink>`, which
by default treats a link as active when the current URL **starts with**
the link's `to` path. Since `/ai/settings/governance` starts with
`/ai/settings`, the Providers item is also marked active.
## Fix
Pass `end` on the Providers nav item so it only matches when the path is
exactly `/ai/settings` (the index route). The `SettingsSidebarNavItem`
component already supports this prop for exactly this case.
Co-authored-by: blink-so[bot] <211532188+blink-so[bot]@users.noreply.github.com>
> 🤖 Generated with [Coder Agents](https://coder.com/agents) on behalf of
@tracyjohnsonux
Updates the providers page description to explain that providers power
Coder Agents, AI Gateway, and other LLM features. Adds a "Manage
deployment-wide BYOK" link to the docs.
Uses `<Link>` component and `docs()` helper per project conventions.
Adds the account settings UI for managing user secrets, including the
table, add/edit/delete dialog, Storybook coverage, and route/sidebar
entry.
Also updates the shared `FeatureStageBadge` beta variant with
dedicated beta styling, sizing, and label casing for the Secrets
page.
Stacked on #25370.
_This PR was generated by Coder Agents._
The `multi-select` form type description in the dynamic parameters docs
incorrectly stated it renders checkboxes. The actual UI is a searchable
dropdown combobox (`MultiSelectCombobox`) with selected items shown as
removable chips.
> This PR was authored by Coder Agents on behalf of @uzair-coder07.
Previously we were only extracting the API when _not_ delegating auth;
this is incorrect.
We need to extract the key _always_ when BYOK is intended.
---------
Signed-off-by: Danny Kopping <danny@coder.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
> 🤖 This PR was written by Coder Agents on behalf of Jake Howell.
Linear: [DEVEX-355](https://linear.app/coder/issue/DEVEX-355)
Fifth and final PR in a 5-PR stack splitting #25328. Surfaces the AI
settings section in the dashboard chrome and moves the existing AI
Governance page out of `/deployment`.
- `Navbar` / `NavbarView` / `DeploymentDropdown` gain a
`canViewAISettings` prop sourced from the `viewAnyAIProvider` permission
added in PR 2. The deployment dropdown gets a new AI entry that links to
`/ai/settings`.
- `DeploymentSidebarView` drops the AI-related entries that now live
under `/ai/settings`.
- `AISettingsSidebarView` expands to include AI Governance and a
cross-section link to Manage Coder Agents.
- `router.tsx` removes the `/deployment/ai-governance` route and mounts
the matching `/ai/settings/governance` child route under the new AI
settings layout.
- `ChatsSidebar` settings panel repoints the Providers link from
`/deployment/ai-providers` to `/ai/settings`.
<details>
<summary>Stack</summary>
1. #25579 jakehwll/DEVEX-355/01-primitives, primitives
2. #25580 jakehwll/DEVEX-355/02-api, API client and query layer
3. #25581 jakehwll/DEVEX-355/03-components, provider form components
4. #25583 jakehwll/DEVEX-355/04-pages, pages and routes
5. **jakehwll/DEVEX-355/05-section, section reshuffle (this PR)**
Replaces #25328 once the stack lands.
</details>
> 🤖 This PR was written by Coder Agents on behalf of Jake Howell.
Linear: [DEVEX-355](https://linear.app/coder/issue/DEVEX-355)
Fourth PR in a 5-PR stack splitting #25328. Wires the new `/ai/settings`
provider management UI.
- `AISettingsLayout` hosts the section under `/ai/settings` with a
sidebar outlet.
- `AISettingsSidebar(View)` shows a single "Providers" nav entry. The
remaining sidebar entries arrive with the broader AI settings section
reshuffle in the next PR.
- `ProvidersPage` lists configured AI providers via the queries added in
PR 2.
- `AddProviderPage` walks through provider-type selection and form
submission, with type-specific credential fields.
- `UpdateProviderPage` edits an existing provider with the same form
components.
- Storybook stories cover each view's loading, empty, populated, error,
and form states using the mock providers from `testHelpers/entities.ts`.
- `router.tsx` mounts the new `/ai/settings` layout with index, `add`,
and `:providerId` child routes. The `governance` child route lands
together with the dashboard navigation changes in the next PR.
Removes the now-unused knip ignore entries for
`src/api/queries/aiProviders.ts` and
`src/pages/AISettingsPage/ProvidersPage/components/addableProviderTypes.ts`,
and drops the matching `@lintignore` tags on `getProviderIcon` and
`MockAIProviders` since the pages and page stories now consume them.
<details>
<summary>Stack</summary>
1. #25579 jakehwll/DEVEX-355/01-primitives, primitives
2. #25580 jakehwll/DEVEX-355/02-api, API client and query layer
3. #25581 jakehwll/DEVEX-355/03-components, provider form components
4. **jakehwll/DEVEX-355/04-pages, pages and routes (this PR)**
5. jakehwll/DEVEX-355/05-section, section reshuffle
Replaces #25328 once the stack lands.
</details>
> 🤖 This PR was written by Coder Agents on behalf of Jake Howell.
Linear: [DEVEX-355](https://linear.app/coder/issue/DEVEX-355)
Third PR in a 5-PR stack splitting #25328. Adds the component-level
pieces used by the provider management pages landing in the next PR of
the stack.
- `ProviderForm` + `CredentialField` + a provider type-to-form mapping
for reading and editing the per-type credential and config fields, with
the form API map covered by unit tests.
- `ProviderIcon` resolves the bundled per-provider SVG icons and falls
back to a building glyph for unknown types.
- `ProviderRow` renders a single provider entry for the list view.
- `useUnsavedChangesPrompt` hook intercepts unsaved-form navigation.
- Storybook stories for `ProviderForm`, `ProviderIcon`, and
`ProviderRow` exercise each provider type and form state and consume the
mock providers from PR 2.
Stories now consume `MockAIProviderOpenAI` / `Anthropic` / `Bedrock` so
their per-mock `@lintignore` tags are removed; the `MockAIProviders`
aggregate and the `addableProviderTypes` / `aiProviders` query modules
keep their exclusions for the page stories in the next PR.
<details>
<summary>Stack</summary>
1. #25579 jakehwll/DEVEX-355/01-primitives, primitives
2. #25580 jakehwll/DEVEX-355/02-api, API client and query layer
3. **jakehwll/DEVEX-355/03-components, provider form components (this
PR)**
4. jakehwll/DEVEX-355/04-pages, pages and routes
5. jakehwll/DEVEX-355/05-section, section reshuffle
Replaces #25328 once the stack lands.
</details>
> 🤖 This PR was written by Coder Agents on behalf of Jake Howell.
Linear: [DEVEX-355](https://linear.app/coder/issue/DEVEX-355)
Second PR in a 5-PR stack splitting #25328. Adds the frontend layer that
talks to the existing `/api/v2/ai/providers` endpoints already shipped
on `main`:
- API client: `getAIProviders`, `getAIProvider`, `createAIProvider`,
`updateAIProvider`, `deleteAIProvider`.
- React Query wrappers in `queries/aiProviders.ts` with a shared key
helper and matching cache invalidations.
- Mock fixtures for OpenAI, Anthropic, and Bedrock providers in
`testHelpers/entities.ts` for stories and unit tests.
- `viewAnyAIProvider` registered in `permissions.json` so the existing
permissions hook can read it.
- `viewAnyAIProvider` added to `canViewDeploymentSettings` so admins who
can only manage providers still see the deployment dropdown.
The `aiProviders` query module and the per-provider mocks are
temporarily added to the `knip` ignore list / annotated with
`@lintignore`; the next PRs in the stack consume them and remove the
exclusions.
<details>
<summary>Stack</summary>
1. #25579 jakehwll/DEVEX-355/01-primitives, primitives
2. **jakehwll/DEVEX-355/02-api, API client and query layer (this PR)**
3. jakehwll/DEVEX-355/03-components, provider form components
4. jakehwll/DEVEX-355/04-pages, pages and routes
5. jakehwll/DEVEX-355/05-section, section reshuffle
Replaces #25328 once the stack lands.
</details>
> 🤖 This PR was modified by Coder Agents on behalf of Jake Howell.
Linear: [DEVEX-355](https://linear.app/coder/issue/DEVEX-355)
First PR in a 5-PR stack splitting #25328. Adds the small UI primitives
the AI settings stack depends on.
- `FormField` accepts a `description` prop and renders a required
marker. `aria-describedby` is composed from the description, helper, and
error IDs.
- `PageHeader` title, subtitle, and caption forward `className` and
other intrinsic `h1`/`h2`/`span` props to their root elements.
- `AvatarData` gains an opt-in `truncate` prop that clips overflowing
title and subtitle with an ellipsis. Off by default so existing
consumers passing non-text nodes (icons, badges) do not clip silently.
- Bundles the Vercel provider icon (`vercel.svg`) and registers it in
`icons.json` and `externalImages.ts`.
No new pages or routes here; later PRs in the stack consume these
primitives.
<details>
<summary>Stack</summary>
1. **jakehwll/DEVEX-355/01-primitives, primitives (this PR)**
2. jakehwll/DEVEX-355/02-api, API client and query layer
3. jakehwll/DEVEX-355/03-components, provider form components
4. jakehwll/DEVEX-355/04-pages, pages and routes
5. jakehwll/DEVEX-355/05-section, section reshuffle
Replaces #25328 once the stack lands.
</details>
Restyles the Agents page usage trigger to match the new quota meter
presentation by Tracy.
The trigger now shows one compact row per section with the existing
severity colors, the same spend icon used in settings, a server icon for
workspace quota, and right-aligned counters. The related stories were
updated to reflect the new trigger layout.
**Before:**
<img width="318" height="332" alt="image"
src="https://github.com/user-attachments/assets/4c6087bd-7c14-4cb6-b2e7-26bb7a1d3e70"
/>
**After:**
<img width="323" height="353" alt="image"
src="https://github.com/user-attachments/assets/95bfb992-cab8-473e-838d-1dcbe246fe3d"
/>
Relates to CODAGT-197
*Disclaimer: implemented by a Coder Agent using Claude Opus 4.6*
## Summary
Renames "AI Bridge" to "AI Gateway" in the paywall card shown on the AI
Governance settings page when the feature is not entitled.
## Changes
In `PaywallAIGovernance.tsx`:
- Title: `AI Bridge` -> `AI Gateway`
- Description text: `AI Bridge provides...` / `AI Bridge requires...` ->
`AI Gateway provides...` / `AI Gateway requires...`
- Docs link label: `AI Bridge Docs` -> `AI Gateway Docs`
Closes#24333
This was a common setup in `updateWorkspace()` but was not appropriately
ported to `changeWorkspaceVersion()`. Some tests have been added also to
ensure this works 🙂 Simple smooth and easy.
Replace the env-based `BuildProviders` with a DB-backed loader. The database is now the single source of truth for runtime provider configuration; env config arrives via `SeedAIProvidersFromEnv` (run at boot) and `BuildProviders` reads it back as `aibridge.Provider` instances. `cli/server.go` and `enterprise/cli/server.go` both call the same path, so aibridged and aibridgeproxyd see the same provider set.
Per-provider `DumpDir` is replaced by a top-level `CODER_AI_GATEWAY_DUMP_DIR` base; each provider's effective dump path is `<base>/<provider name>`.
Splits the dogfood image into two artifacts:
- `ghcr.io/coder/oss-dogfood-base:<distro>-<base-sha>`: Ubuntu base with
apt packages, chrome, rustup, brew, gh, and the mise binary. The
base-sha is a cache key over `Dockerfile.base` and `files/`, so commits
that don't touch those inputs reuse the previous build.
- `codercom/oss-dogfood:<final-sha>-<distro>` and rolling tags
(`:22.04`, `:26.04`, `:latest`, `:<branch>`): produced by `mise oci
build` on top of the base, with one content-addressed OCI layer per mise
tool. The rolling tag scheme is unchanged, so the workspace template
doesn't need updating.
Single-tool version bumps now invalidate only that tool's OCI layer, so
workspaces re-pull just what changed instead of the entire 5-6 GB image
on every recreate.
Also:
- Drops the build-time `pnpm dlx playwright@1.47.0 install --with-deps
chromium` step (~400 MB) and the equivalent `playwright-driver.browsers`
install from `flake.nix`. `@playwright/mcp` (used by the claude-code and
codex MCP servers in `dogfood/coder/main.tf`) does NOT auto-install
browsers, so the existing `install-deps` `coder_script` now runs two
installs on workspace start: `pnpm exec playwright install chromium` for
the site's pinned `@playwright/test`, and `npx
--package=@playwright/mcp@latest playwright-core install --no-shell
chromium` so the MCP servers find their matching browser revision.
Browser revisions coexist under
`~/.cache/ms-playwright/chromium-<rev>/`, which lives on the home volume
so both downloads happen once per workspace recreate and persist across
restarts. Net effect: same MCP behavior as before, +~1-2 min on first
workspace start. Nix devshell users running site e2e tests locally now
need `pnpm exec playwright install` once (instead of getting browsers
via nixpkgs).
- Bumps the pinned mise binary to v2026.5.12 (matching main after
#25521) and adds top-level `min_version = "2026.5.12"` to `mise.toml` so
every consumer (devs, CI, the embedded mise inside the dogfood image,
mise oci builds) fails fast on an older mise.
- Adds bison, flex, libicu-dev, libreadline-dev, uuid-dev, and
zlib1g-dev to both Ubuntu base images for source-build use cases (e.g.,
building Postgres from source).
- Replaces skopeo with crane as the registry client `mise oci push`
shells out to: crane is added to `mise.toml`, the workflow drops its
`apt-get install skopeo` and forces `--tool crane`, and the local
wrapper image stops bundling skopeo. One source of truth for tool
versions, no apt drift, smaller wrapper image, and workspace users get a
registry client on PATH for free via mise oci's tool layers.
- Removes `nix.hash`/`mise.hash` and their Makefile rules. The registry
digest already captures every effective change since CI rebuilds when
any baked-in input moves; the per-file `filesha1()` entries in
`pull_triggers` are redundant.
Supersedes #25400 (the `mise.hash` pull trigger landed there in
`2b612abe7b`; this PR removes it as part of the broader simplification).
> [!NOTE]
> `mise oci build` is experimental and requires `MISE_EXPERIMENTAL=1`
(set at job level in the workflow). The local-only
`scripts/dogfood/mise-oci-wrapper.sh` builds a tiny
`coderdev/mise-oci-wrapper:<version>` Debian image with curl-installed
mise on first invocation (cached by version tag thereafter); we don't
reuse `jdxcode/mise:latest` because that tag lags upstream GitHub
releases by days and would defeat the `min_version` enforcement above.
> [!NOTE]
> `compute-base-sha.sh` and `compute-final-sha.sh` are cache keys, not
strict content addresses: the base Dockerfile still pulls dynamic
resources at build time (gh/buildx `releases/latest`, chrome
`stable_current_amd64.deb`, apt mirror state). Two runs with identical
checked-in files can produce slightly different bytes, which is
acceptable here because the cache-hit savings on irrelevant commits
outweigh that drift.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Signed-off-by: Thomas Kosiewski <tk@coder.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
*Disclaimer: implemented by a Coder Agent using Claude Opus 4.6*
Adds a clause under **Writing Comments and Avoiding Unnecessary
Changes** in `AGENTS.md` specifying that comments must be:
- **Substantive** and **concise**
- Describe the **behaviour** of the code
- NOT describe the reasoning the agent used to produce the change
Includes concrete anti-pattern examples (`// Added per PR feedback`, `//
Refactored for clarity`) to make the expectation clear.
Models frequently confuse the search and replace fields in the
edit_files tool (CODAGT-312). Rename the model-facing JSON fields
to old_text/new_text so the intent is unambiguous.
Backend: custom UnmarshalJSON on editFileEdit falls back to
deprecated search/replace when old_text/new_text are empty. The
workspace agent API is unchanged; toSDKFiles maps old_text/new_text
back to search/replace for agent/agentfiles.
Frontend: normalizeEdit in parseEditFilesArgs accepts both
old_text/new_text and search/replace, normalizing to the internal
{ search, replace } representation so streaming diff rendering
works with either field naming convention.
We weren't stripping the API base (`/api/v2/aibridge`), leading to 404s
when using the in-memory transport.
Signed-off-by: Danny Kopping <danny@coder.com>
Adds `coder exp scaletest chat`, a harness for creating Coder Agents
chat load.
Start the mock LLM separately, prepare the scaletest workspaces you want
to target, then run the chat scaletest against the existing
`scaletest-*` fleet selected by the shared workspace targeting flags:
```sh
coder exp scaletest llm-mock --address 127.0.0.1:18080
coder exp scaletest chat --llm-mock-url http://127.0.0.1:18080/v1 --chats-per-workspace 10 --turns 1
coder exp scaletest chat --llm-mock-url http://127.0.0.1:18080/v1 --template docker --target-workspaces 0:10 --chats-per-workspace 1 --turns 10 --turn-start-delay 30s
```
This is the same pattern used by the `workspace-traffic` load generator.
Keeping the fake LLM as a separate process is intentional so it can be
scaled independently from the Coder deployment, which will likely be
necessary as we scale up and up.
This PR is the starting point: it provides the command, mock
provider/model bootstrap, existing workspace selection, chat streaming,
follow-up turns, metrics, and cleanup. Follow-up PRs will add multi-step
turns via tool calls. I'm still a bit iffy on the mechanism I have for
that. It'll likely involve having the runner send some magic strings
that the mock will recognise.
Relates to CODAGT-307
Relates to GRU-48
Relates to https://github.com/coder/scaletest/issues/124
Generated by Mux, but reviewed by a human
This fixes the flaky `TestSubscribeAfterMessageID` by seeding its chat
and messages directly, so the test no longer creates pending work that a
chat worker can pick up. The assertion now covers only the
`afterMessageID` subscription behavior, independent of chat processing
lifecycle timing.
Closes DEVEX-326
Closes https://github.com/coder/internal/issues/1489
When removing the `/` personal skill trigger, the popover content stayed
mounted during its close transition and briefly rendered the empty
skills state at the viewport origin.
This keeps the menu content mounted for stable Radix positioning,
preserves the last open menu state during the close transition, and adds
a Storybook regression for the backspace path.
> Mux is creating this PR on behalf of Mike.
## Description
`Provider.InjectAuthHeader` is no longer needed. With the addition of `KeyFailoverConfig` in #24920, authentication is now applied per-attempt by `KeyFailoverTransport` on passthrough routes. This PR removes the dead method from the `Provider` interface, all implementations (`Anthropic`, `OpenAI`, `Copilot`), and the test mock.
The orphaned `InjectAuthHeader` unit tests are replaced with `Test{Anthropic,OpenAI,Copilot}_KeyFailoverConfig`. `TestPassthrough_KeyFailover` is also extended to cover Copilot in the BYOK scenario.
Related to: https://linear.app/codercom/issue/AIGOV-334/aibridge-follow-ups-from-key-failover-prs
> [!NOTE]
> Initially generated by Claude Opus 4.7, modified and reviewed by @ssncferreira
## Description
Cleans up how key pool errors are represented and how they get turned into HTTP responses. Consolidates two error types into a single type with a kind tag, and gives the response helpers in both providers consistent names.
## Changes
- Replaced the keypool sentinel and transient error struct with one error type that carries a kind and a retry-after duration.
- Updated `KeyFailoverConfig.BuildKeyPoolResponse` to take the typed key pool error, so each provider can shape the exhaustion response in its own format.
- Removed the per-provider `MarkKey` callback from `KeyFailoverConfig` since providers can rely on the shared `MarkKeyOnStatus` helper.
- Renamed the response-error helpers so OpenAI and Anthropic use the same naming.
Related to: https://linear.app/codercom/issue/AIGOV-334/aibridge-follow-ups-from-key-failover-prs
> [!NOTE]
> Initially generated by Claude Opus 4.7, modified and reviewed by @ssncferreira
Documents the known race in `EventStream.IsStreaming()` and the
resulting flake in
`TestStreamingInterception_AgenticLoopFailover/agentic_all_keys_fail `,
accepted rather than fixed since the inner agentic loop is on track to
be removed as part of the reverse proxy migration in coder/aibridge#223.
Full reasoning in coder/internal#1524.
waitForTaskIdle used time.NewTicker(5s) which delays the first poll
by 5 seconds. Debugger tracing proved the failure mechanism: on slow
CI (Windows), the first poll at 5s sees "working" (idle patch has not
landed due to goroutine scheduling), needs poll #2 at 10s, but the
25s context expires before it fires.
Two changes:
1. Use r.clock.NewTicker (quartz) with time.Nanosecond initial
interval and Reset(5s) for immediate first poll. Tests inject a
mock clock via clitest.NewWithClock for deterministic control.
2. Rewrite WaitsForWorkingAppState test with quartz traps
(NewTicker + TickerReset) for deterministic synchronization
instead of racing goroutines. Fix PausedDuringWaitForReady
sync point.
Closes DEVEX-381
Adds a `test_image` job that runs `make gen`, `make fmt`, `make lint`, and `make build` inside the
newly built image via `docker run`. This helps detect breaking changes before merge.
> [!NOTE]
> Generated with [Coder Agents](https://coder.com/agents)
ContextPartsFromDir scans ~/.coder/skills via DefaultSkillsDir.
On machines with real skills installed, these leaked into test
results. Set HOME/USERPROFILE to temp dirs on the parent test
so subtests run in a clean environment.
TestPromoteQueuedWhileRunningRespectsMessageOrder was flaky because
it read queue state from the database immediately after PromoteQueued
returned. The active server worker drains queued messages concurrently,
so the DB read races the auto-promote pipeline (TOCTOU).
Instead of asserting intermediate queue state, wait for all three
promoted messages to appear in chat history and verify their relative
order (B before A before C). This asserts the same invariant (promote
reorders B to the front) without reading during the race window.
Closes CODAGT-384
handleProcessOutput read proc.output() then proc.info() using
separate locks. Between the two reads the exit goroutine could
finish I/O and set running=false, pairing stale output with final
status. On Windows CI this caused OutputExceedsBuffer to flake
when the buffer snapshot caught mid-write data (OmittedBytes=0)
but info reported the process as exited.
Swap the read order so info is read first. The exit goroutine
completes cmd.Wait (draining all pipe data) before setting
running=false, so seeing Running=false guarantees the subsequent
output read reflects the final buffer state.
Closes CODAGT-399
The root cause of the TestPromoteQueuedWhileRequiresActionMixedTools
flake (CODAGT-425) was the subscriber out-of-order durable message
delivery bug, fixed by PR #25433 (ec1e861). All five CI failures
predate that fix. Zero failures since.
This change hardens the subscriber event-drain pattern in both
PromoteQueued requires_action tests: wrap the channel select in a
for-loop so interleaved non-target events (status, queue_update,
message_parts) are consumed in the same Eventually tick instead of
each burning a 25ms interval. This is defense-in-depth for slow CI
runners, not a standalone bug fix.
Closescoder/internal#1523
Closes CODAGT-425
Adds `--env-file` to `scripts/develop.sh` to allow reading environment
from a given file. This makes it easier to configure things like external
auth providers, access URLs, and other dev-time settings without
exporting a wall of environment variables in every shell session.
> Generated with [Coder Agents](https://coder.com/agents)
Generic agent chat tool cards now render an `Input` section before the
existing output viewer, so MCP and workspace MCP tools expose the
arguments sent to the tool. Empty inputs stay hidden, model-intent
wrappers are stripped before display, and the formatted input is the
single source of truth for whether an input block renders.
Refs
https://linear.app/codercom/issue/CODAGT-260/show-mcp-tool-inputs-in-agent-chats
> Mux worked on this on Mike's behalf.
RFC: [Bridge ↔ Boundaries Correlation
RFC](https://www.notion.so/coderhq/Gateway-and-Firewall-Correlation-RFC-31ad579be592803aa8b3d48348ccdde9)
Add up/down migrations and matching sqlc queries for persisting Boundary
audit events, as specified in the Bridge/Boundaries Correlation RFC.
**Tables:**
- `boundary_sessions`: session metadata with `workspace_agent_id` FK,
`confined_process_name`, and timestamps (`started_at`, `updated_at`). ID
is externally supplied by the Boundary process (no DB-side default).
Created lazily when the first log for a session arrives.
- `boundary_logs`: individual audit events with `session_id` FK,
`sequence_number` (INT, primary ordering key), protocol/method/detail
fields, and `matched_rule` (nullable; non-NULL implies allowed).
**Indexes (per RFC):**
- `(session_id, sequence_number)` for the ordering query path
- `(captured_at)` for the retention purge path
**Queries:**
- `InsertBoundarySession` / `GetBoundarySessionByID`
- `InsertBoundaryLog` / `GetBoundaryLogByID`
- `ListBoundaryLogsBySessionID` with nullable `seq_after`/`seq_before`
exclusive bounds for fetching events between two known interception
sequence numbers
- `DeleteOldBoundaryLogs` with row limit to avoid long-running
transactions
**Also includes:** dbgen helpers (`BoundarySession`, `BoundaryLog`),
dbauthz implementations (reads gated on `ResourceAuditLog`, deletes on
`ResourceSystem`), and all generated wrappers (dbmock, dbmetrics).
No callers yet. A follow-up PR will add the dedicated `boundary_log`
RBAC resource type.
> Generated by Coder Agents
Allows an `api_key_id` to be passed from a trusted in-memory transport
(currently: `chatd`) to `aibridged` for use in authenticating LLM
requests.
This value can _only_ be passed via context, and all users of the
in-memory transport _must_ provide it.
It can be used in conjunction with BYOK headers.
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Closes#24183
## Changes
Drops `mx-auto` so README content left-aligns with the header. Bumps
padding from 24px to 32px and widens `max-w` from 800px to 860px for
breathing room.
Applied to both:
- `TemplateDocsPage.tsx`
- `StarterTemplatePageView.tsx`
> Generated with [Coder Agents](https://coder.com/agents)
Skips `TestSignalWakeSendMessage`, which flakes because the current
chatd control notification flow can deliver stale status notifications
after a new processing run starts.
This mirrors the existing CODAGT-353 skips for the same
stale-notification class and leaves the deterministic fix to that
notification-flow refactor.
Refs
https://linear.app/codercom/issue/ENG-2727/flake-testsignalwakesendmessage
> Generated by Coder Agents on behalf of @ibetitsmike.