Commit Graph
13273 Commits
Author SHA1 Message Date
Kyle Carberry d9fc5a5be1 feat: persist chat instruction files as context-file message parts (#23592)
## Summary

Introduces a new `context-file` ChatMessagePart type for persisting
workspace instruction files (AGENTS.md) as durable, frontend-visible
message parts. This is the foundation for showing loaded context files
in the chat input's context indicator tooltip.

### Problem

Previously, instruction files were resolved transiently on every turn
via `resolveInstructions()` → `InsertSystem()` and injected into the
in-memory prompt without persistence. The frontend had no knowledge that
instruction files were loaded into context, and there was no way to
surface this information to users.

### Solution

Instruction files are now read **once** when a workspace is first
attached to a chat (matching how [openai/codex handles
it](https://developers.openai.com/codex/guides/agents-md)) and persisted
as `user`-role, `both`-visibility message parts with a new
`context-file` type. This ensures:

- **Durability**: survives page refresh (data is in the DB, returned by
`getChatMessages`)
- **Cache-friendly**: `user`-role avoids the system-message hoisting
that providers do, keeping the instruction content in a stable position
for prompt caching
- **Frontend-visible**: the frontend receives paths and truncation
status for future context indicator rendering
- **Extensible**: the same pattern works for Skills (future)

### Key changes

| Layer | Change |
|---|---|
| **SDK** (`codersdk/chats.go`) | Add `ChatMessagePartTypeContextFile`
with `context_file_path`, `context_file_content` (internal, stripped
from API), `context_file_truncated` fields |
| **Prompt expansion** (`chatprompt`) | Expand `context-file` parts to
`<workspace-context>` text blocks in `partsToMessageParts()` |
| **Chat engine** (`chatd.go`) | Add `persistInstructionFiles()`, called
on first turn with a workspace. Remove per-turn `resolveInstructions()`
+ `InsertSystem()` from `processChat()` and `ReloadMessages` |
| **Frontend** | Ignore `context-file` parts in `messageParsing.ts` and
`streamState.ts` (no rendering yet — follow-up will add tooltip display)
|

### How it works

1. On each turn, `processChat` checks if any loaded message contains
`context-file` parts
2. If not (first turn with a workspace), reads AGENTS.md files via the
workspace agent connection and persists them
3. For this first turn, also injects the instruction text into the
prompt (since messages were loaded before persistence)
4. On all subsequent turns, `ConvertMessagesWithFiles()` encounters the
persisted `context-file` parts and expands them into text automatically
— no extra resolution needed
2026-03-25 17:08:27 +00:00
Atif Ali 6ce35b4af2 fix(site): show accurate health messages in workspace hover menu and status tooltip (#23591) 2026-03-25 21:54:15 +05:00
Danielle Maywood 110af9e834 fix(site): fix agents sidebar not loading all pages when sentinel stays visible (#23613) 2026-03-25 16:40:26 +00:00
david-fraley 9d0945fda7 fix(site): use consistent contact sales URL (#23607) 2026-03-25 16:09:48 +00:00
Cian Johnston fb5c3b5800 ci: restore depot runners (#23611)
This commit reverts the previous changes to CI jobs affected by disk
space issues on depot runners.
2026-03-25 16:08:11 +00:00
david-fraley 677ca9c01e fix(site): correct observability paywall documentation link (#23597) 2026-03-25 11:06:43 -05:00
david-fraleyandJake Howell 62ec49be98 fix(site): fix redundant phrasing in template permissions paywall (#23604)
The description read "Control access of templates for users and groups
to templates" with "templates" appearing twice and garbled grammar.
Simplified to "Control user and group access to templates."

---------

Co-authored-by: Jake Howell <jacob@coder.com>
2026-03-25 16:05:27 +00:00
david-fraley 80eef32f29 fix(site): point provisioner paywall docs links to provisioner docs (#23598) 2026-03-25 11:00:15 -05:00
Jeremy Ruppel 8f181c18cc fix(site): add coder agents logo to aibridge clients (#23608)
Add the Coder icon to Coder Agents AI Bridge client icon
2026-03-25 11:48:35 -04:00
Mathias Fredriksson 239520f912 fix(site): disable refetchInterval in storybook QueryClient (#23585)
HealthLayout sets refetchInterval: 30_000 on its health query.
In storybook tests, the seeded cache data prevents the initial
fetch, but interval polling still fires after 30s, hitting the
Vite proxy with no backend. This caused test-storybook to hang
indefinitely in environments without a running coderd.

Set refetchInterval: false in the storybook QueryClient defaults
alongside the existing staleTime: Infinity and retry: false.
2026-03-25 17:37:53 +02:00
Hugo Dutka 398e2d3d8a chore: upgrade kylecarbs/fantasy to 112927d9b6d8 (#23596)
The `ComputerUseProviderTool` function needed a little bit of an
adjustment because I changed `NewComputerUseTool`'s signature in
upstream fantasy a little bit.
2026-03-25 15:30:46 +00:00
Cian Johnston 796872f4de feat: add deployment-wide template allowlist for chats (#23262)
- Stores a deployment-wide agents template allowlist in `site_configs`
(`agents_template_allowlist`)
- Adds `GET/PUT /api/experimental/chats/config/template-allowlist`
endpoints
- Filters `list_templates`, `read_template`, and `create_workspace` chat
tools by allowlist, if defined (empty=all allowed)
- Add "Templates" admin settings tab in Agents UI ([what it looks
like](https://624de63c6aacee003aa84340-sitjilsyrr.chromatic.com/?path=/story/pages-agentspage-agentsettingspageview--template-allowlist))

> 🤖 This PR was created with the help of Coder Agents, and has been
reviewed by my human. 🧑‍💻
2026-03-25 15:19:17 +00:00
david-fraley c0ab22dc88 fix(site): update registry link to point to templates page (#23589) 2026-03-25 09:57:14 -05:00
Ethan 196c61051f feat(site): structured error/retry UX for agent chat (#23282)
> **PR Stack**
>
> 1. #23351 ← `#23282`
> 2. **#23282** ← `#23275` *(you are here)*
> 3. #23275 ← `#23349`
> 4. #23349 ← `main`

---

## Summary

Replaces raw error strings and infinite "Thinking..." spinners in the
agents chat UI with a structured live-status model that drives startup,
retry, and failure UI from one source of truth.

This branch also folds in the frontend follow-up fixes that fell out of
that refactor: malformed `retrying_at` timestamps no longer render
`Retrying in NaNs`, stale persisted generic errors no longer outlive a
recovered chat status, and partial streamed output stays visible when a
response fails after blocks have already rendered.

Consumes the structured error metadata added in #23275.
Retry-After header handling remains in #23351.

<img width="853" height="493" alt="image"
src="https://github.com/user-attachments/assets/5a4a1690-5e22-4ece-965c-a000fd669244"
/>

<img width="812" height="517" alt="image"
src="https://github.com/user-attachments/assets/e78d28ce-1566-48ca-a991-62c6e1838079"
/>

<img width="847" height="523" alt="image"
src="https://github.com/user-attachments/assets/e5fd7b60-4a3c-4573-ba4c-4e5f6dbfbdc3"
/>

## Problem

The previous AgentDetail chat UI derived startup, retry, and failure
behavior from several loosely connected bits of state spread across
`ChatContext`, `AgentDetailContent`, `ConversationTimeline`, and ad hoc
props. That made the UI inconsistent: some failures were just raw
strings, retry state could only partially describe what was happening,
startup could sit on an infinite spinner, and rendering decisions
depended on local booleans instead of one authoritative model.

Those splits also made edge cases brittle. Invalid retry timestamps
could produce broken countdown text, persisted generic errors could
linger after recovery, and streamed partial output could disappear if
the turn later failed.

## Fix

Introduce a structured live-status pipeline for AgentDetail.
`ChatContext` now normalizes stream errors and retry metadata into
richer state, `liveStatusModel` centralizes precedence and phase
derivation, and `ChatStatusCallout` renders startup, retry, and terminal
failure states with shared copy, provider attribution, status links,
attempt metadata, and guarded countdown handling.

`AgentDetailContent` and `ConversationTimeline` now consume that single
model instead of juggling separate error and stream booleans, while
usage-limit messaging stays on its explicit path. The result is a
timeline that shows consistent state transitions, preserves accumulated
assistant output across failures, suppresses stale generic errors once
live state recovers, and has focused model, store, and story coverage
around those behaviors.
2026-03-26 01:45:39 +11:00
david-fraley 649e727f3d docs: add Release Candidates section to releases page (#23584) 2026-03-25 09:40:33 -05:00
Kyle Carberry fdc9b3a7e4 fix: match text and image attachment heights in conversation timeline (#23593)
## Problem

Text attachments (`InlineTextAttachmentButton`) and image thumbnails
(`ImageThumbnail`) rendered at different heights when displayed side by
side in user messages. Text cards had no explicit height
(content-driven), while images used `h-16` (64px).

## Changes

**`ConversationTimeline.tsx`**
- Added `h-16` to `InlineTextAttachmentButton` to match `ImageThumbnail`
- Added `isPlaceholder` prop: when the content hasn't been fetched yet
(file_id path), renders "Pasted text" in sans-serif `text-sm` with
`items-center` alignment instead of monospace `text-xs`
- Once real content loads, it still renders in `font-mono text-xs` with
`formatTextAttachmentPreview()`

**`ConversationTimeline.stories.tsx`**
- Added `UserMessageWithMixedAttachments` story showing a text
attachment and image side by side as a visual regression guard
2026-03-25 14:37:55 +00:00
Mathias Fredriksson 7eca33c69b fix(site): cancel stale refetches before WebSocket cache writes (#23582)
When a chat is created, createChat.onSuccess invalidates the sidebar
list query, triggering a background refetch. The refetch can hit the
server before async title generation finishes, returning the fallback
(truncated) title. If the title_change WebSocket event arrives and
writes the generated title into the cache, the in-flight refetch
response then overwrites it with the stale fallback title.

Cancel any in-flight sidebar-list and per-chat refetches before every
WebSocket-driven cache write. This mirrors the existing pattern in
archiveChat/unarchiveChat, which cancel queries before optimistic
updates for the same reason.
2026-03-25 16:18:32 +02:00
Kyle Carberry 40395c6e32 fix(coderd): fast-retry PR discovery after git push (#23579)
## Problem

When chatd pushes a branch and then creates a PR (e.g. `git push`
followed by `gh pr create`), the gitsync background worker often picks
up the stale `chat_diff_statuses` row between the two operations. At
that point no PR exists yet, so the worker skips the row. However, the
acquisition SQL locks the row for **5 minutes** (crash-recovery
interval), creating a dead zone where the PR diff is invisible in the UI
until the user manually navigates to the chat.

### Root cause

1. `git push` triggers `GIT_ASKPASS` → coderd external-auth handler →
`MarkStale()` sets `stale_at = now - 1s`
2. Background worker acquires the row within ~10s, atomically bumps
`stale_at = NOW() + 5 min` (crash-recovery lock)
3. Worker calls `ResolveBranchPullRequest` → no PR exists yet → returns
`nil` → worker skips with `continue`
4. `gh pr create` completes moments later, but uses its own auth (not
`GIT_ASKPASS`), so no second `MarkStale` fires
5. Row is locked for 5 minutes before the worker can retry

Loading the chat works immediately because `GET /chats/{chat}` calls
`resolveChatDiffStatus` synchronously, which discovers the PR inline.

## Fix

When `ResolveBranchPullRequest` returns nil (no PR yet) **and** the row
was recently marked stale (within 2 minutes), apply a short 15-second
backoff via `BackoffChatDiffStatus` instead of letting the 5-minute
acquisition lock stand. Outside the retry window, the worker skips the
row as before — no indefinite fast-polling for branches that never
receive a PR.

To make the "recently marked stale" check work, `updated_at` is no
longer overwritten by the acquisition and backoff SQL queries. This
preserves it as a reliable "last externally changed" timestamp (set by
`MarkStale` or a successful refresh).

### Behavior summary

| Scenario | `updated_at` age | Backoff | Effective retry |
|---|---|---|---|
| Fresh push, no PR yet | < 2 min | 15s (`NoPRBackoff`) | ~15s |
| Old row, no PR | ≥ 2 min | None (skip) | ~5 min (acquisition lock) |
| Error (any age) | Any | 120s (`DiffStatusTTL`) | ~120s |
| Success (any age) | Any | 120s (`DiffStatusTTL`) | ~120s |

## Changes

- **`coderd/database/queries/chats.sql`** — Remove `updated_at = NOW()`
from `AcquireStaleChatDiffStatuses` and `BackoffChatDiffStatus`
- **`coderd/database/queries.sql.go`** — Regenerated
- **`coderd/x/gitsync/worker.go`** — Add `NoPRBackoff` (15s) and
`NoPRRetryWindow` (2 min) constants; apply short backoff only within the
retry window
- **`coderd/x/gitsync/worker_test.go`** — Add
`TestWorker_NoPR_RecentMarkStale_BacksOffShort` and
`TestWorker_NoPR_OldRow_Skips`
2026-03-25 10:09:44 -04:00
Cian Johnston ef2eb9f8d2 fix: strip invisible Unicode from prompt content (#23525)
- Add `SanitizePromptText` stripping ~24 invisible Unicode codepoints
and collapsing excessive newlines
- Apply at write and read paths for defense-in-depth
- Frontend: warn in both prompt textareas when invisible characters
detected
- Explicit codepoint list (not blanket `unicode.Cf`) to avoid breaking
flag emoji
- 34 Go tests + idempotency meta-test, 11 TS unit tests, 4 Storybook
stories

> This PR was created with the help of Coder Agents, and was reviewed by my human.
2026-03-25 14:09:24 +00:00
Danielle Maywood 8791328d6e fix(site): fix right panel layout issues at responsive breakpoints (#23573) 2026-03-25 13:57:43 +00:00
Rowan Smith c33812a430 chore: switch agent gone response from 502 to 404 (#23090)
When a user creates a workspace, opens the web terminal, then the
workspace stops but the web terminal remains open the web terminal will
retry the connection. Coder will issue a HTTP 502 Bad Gateway response
when this occurs because coderd cannot connect to the workspace agent,
however this is problematic as any load balancer sitting in front of
Coder sees a 502 and thinks Coder is unhealthy.

The main change is in
https://github.com/coder/coder/pull/23090/changes#diff-bbe3b56ed3532289481a0e977867cd15048b7ca718ce676aae3f3332378eebc2R97,
however the main test and downstream tests are also updated.

This PR changes the response to a [HTTP
404](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/404)
after internal discussion.

<img width="1832" height="1511" alt="image"
src="https://github.com/user-attachments/assets/0baff80d-bb98-4644-89cd-e80c87947098"
/>

Created with the help of Mux, reviewed and tested by a human
2026-03-25 09:57:28 -04:00
Kyle Carberry 44baac018a fix(site): replace model catalog loading text with skeleton (#23583)
## Changes

Replaces the "Loading model catalog..." / "Loading models..." text flash
on `/agents` with a clean skeleton loading state, and removes the
admin-nag status messages entirely.

### Removed
- `getModelCatalogStatusMessage()` function and
`modelCatalogStatusMessage` prop chain — "Loading model catalog..." /
"No chat models are configured. Ask an admin to configure one." text
below the input
- `inputStatusText` prop chain — "No models configured. Ask an admin." /
"Models are configured but unavailable. Ask an admin." inline text
- `modelCatalogError` prop from `AgentCreateForm`

### Changed
- `AgentChatInput`: when `isModelCatalogLoading` is true, renders a
`Skeleton` in place of the `ModelSelector`
- `getModelSelectorPlaceholder()`: "No Models Configured" / "No Models
Available" (title case)

### Added
- `LoadingModelCatalog` story — skeleton where model selector sits
- `NoModelsConfigured` story — selector shows "No Models Configured"

Net -69 lines.
2026-03-25 13:54:24 +00:00
Cian Johnston f14f58a58e feat(coderd/x/chatd): send Coder identity headers to upstream LLM providers (#23578)
- Add `X-Coder-Owner-Id`, `X-Coder-Chat-Id`, `X-Coder-Subchat-Id`,
`X-Coder-Workspace-Id` headers to all outgoing LLM API requests from
chatd
- Extend `ModelFromConfig` with `extraHeaders` param, forwarded via
Fantasy `WithHeaders` on all 8 providers
- Add `CoderHeaders(database.Chat)` helper to build the header map from
chat state
- Update all 4 `ModelFromConfig` call sites (resolveChatModel,
computer-use override, title gen, push summary)
- Thread `database.Chat` into `generatePushSummary` (was `chatTitle
string`)
- Tests: `TestCoderHeaders` (4 subtests),
`TestModelFromConfig_ExtraHeaders` (OpenAI + Anthropic),
`TestModelFromConfig_NilExtraHeaders`
- Refactor existing `TestModelFromConfig_UserAgent` to use channel-based
signaling

> 🤖 This PR was generated by Coder Agents and self-reviewed by a human.
2026-03-25 13:34:29 +00:00
Danielle Maywood 8bfc5e0868 fix(site): use focus-visible instead of focus for keyboard-only outlines (#23581) 2026-03-25 13:31:50 +00:00
Danielle Maywood a8757d603a fix(site): use lightbox for computer tool screenshots in PWA mode (#23529) 2026-03-25 13:18:09 +00:00
Ethan c0a323a751 fix(coderd): use DB liveness for chat workspace reuse (#23551)
create_workspace could create a replacement workspace after a single 5s
agent dial failed, even when the existing workspace agent had recently
checked in. That made temporary reachability blips look like dead
workspaces and let chatd replace a running workspace too aggressively.

Use the workspace agent's DB-backed status with the deployment's
AgentInactiveDisconnectTimeout before allowing replacement. Recently
connected and still-connecting agents now reuse the existing workspace,
while disconnected or timed-out agents still allow a new workspace. This
also threads the inactivity timeout through chatd and adds focused
coverage for the reuse and replacement branches.
2026-03-26 00:12:05 +11:00
Kyle Carberry 4ba9986301 fix(site): update sticky messages during streaming (#23577)
## Problem

The sticky user message visual state (`--clip-h`, fade gradient, push-up
positioning) is driven by an `update()` function that only ran on
`scroll` events. The chat scroll container uses `flex-col-reverse`,
where `scrollTop = 0` means "at bottom." When streaming content grows
the transcript while the user is auto-scrolled to the bottom,
`scrollTop` stays at `0` — no `scroll` event fires — so `update()` never
runs and the sticky messages become visually stale until the user
manually scrolls.

## Fix

Add a `ResizeObserver` on the scroller's content wrapper inside the
existing `useLayoutEffect` that sets up the scroll/resize listeners.
When the content wrapper resizes (streaming growth), it fires the
observer which calls `update()` through the same RAF-throttle pattern
used by the scroll handler.

Single observer per sticky message instance. Zero cost when nothing is
resizing. Cleanup handled in the same effect teardown.
2026-03-25 09:07:20 -04:00
Danielle Maywood 82f9a4c691 fix: center X icon in agent chat chip close buttons (#23580) 2026-03-25 13:07:04 +00:00
Danielle Maywood 12872be870 fix(site): auto-reload on stale chunk after redeploy (#23575) 2026-03-25 12:49:51 +00:00
Kyle Carberry 07dbee69df feat: collapse MCP tool results by default (#23568)
Wraps the `GenericToolRenderer` (used for MCP and unrecognized tools) in
`ToolCollapsible` so the result content is hidden behind a
click-to-expand chevron, matching the pattern used by `read_file`,
`write_file`, and other built-in tool renderers.

### Changes

- Move `ToolIcon` + `ToolLabel` into the `ToolCollapsible` `header` prop
- Compute `hasContent` from `writeFileDiff` / `fileContent` /
`resultOutput` — when there's no content, the header renders as a plain
div with no chevron
- Remove `ml-6` from `ScrollArea` classNames (the `ToolCollapsible`
button handles its own layout)
- `defaultExpanded` is `false` by default in `ToolCollapsible`, so
results start collapsed

### Before

MCP tool results were always fully visible inline.

### After

MCP tool results are collapsed by default with a chevron toggle,
consistent with `read_file`, `edit_files`, `list_templates`, etc.
2026-03-25 12:47:57 +00:00
Danielle Maywood ae9174daff fix(site): remove rounded-full override from agent sidebar avatar (#23570) 2026-03-25 12:36:30 +00:00
Kyle Carberry f784b230ba fix(coderd/x/chatd/mcpclient): handle EmbeddedResource and ResourceLink in MCP tool results (#23569)
## Problem

When an MCP tool returns an `EmbeddedResource` content item (e.g. GitHub
MCP server returning file contents via `get_file_contents`), the
`convertCallResult` function falls through to the `default` case,
producing:

```
[unsupported content type: mcp.EmbeddedResource]
```

This loses the actual resource content and shows an unhelpful message in
the chat UI.

## Root Cause

The type switch in `convertCallResult` handles `TextContent`,
`ImageContent`, and `AudioContent`, but not the other two `mcp.Content`
implementations from `mcp-go`:
- `mcp.EmbeddedResource` — wraps a `ResourceContents` (either
`TextResourceContents` or `BlobResourceContents`)
- `mcp.ResourceLink` — contains a URI, name, and description

## Fix

Add two new cases to the type switch:

1. **`mcp.EmbeddedResource`**: nested type switch on `.Resource`:
   - `TextResourceContents` → append `.Text` to `textParts`
- `BlobResourceContents` → base64-decode `.Blob` as binary (type
`"image"` or `"media"` based on MIME)
   - Unknown → fallback `[unsupported embedded resource type: ...]`

2. **`mcp.ResourceLink`**: render as `[resource: Name (URI)]` text

## Testing

Added 3 new test cases (all passing, full suite 23/23 PASS):
- `TestConnectAll_EmbeddedResourceText` — text resource extraction
- `TestConnectAll_EmbeddedResourceBlob` — binary blob decoding
- `TestConnectAll_ResourceLink` — resource link rendering
2026-03-25 12:31:17 +00:00
Danielle Maywood a25f9293a1 fix(site): add plus menu to chat input toolbar (#23489) 2026-03-25 12:13:27 +00:00
Kyle Carberry 6b105994c8 feat(site): persist MCP server selection in localStorage (#23572)
## Summary

Previously the user's MCP server toggles were ephemeral — every page
reload or navigation to a new chat reset them to the admin-configured
defaults (`force_on` + `default_on`). This was frustrating for users who
routinely disabled a default-on server or enabled a default-off one.

This PR persists the MCP server picker selection to `localStorage` under
the key `agents.selected-mcp-server-ids`.

## Changes

### `MCPServerPicker.tsx`
- **`mcpSelectionStorageKey`** — exported constant for the localStorage
key.
- **`getSavedMCPSelection(servers)`** — reads from localStorage, filters
out stale/disabled IDs, always includes `force_on` servers.
- **`saveMCPSelection(ids)`** — writes the current selection to
localStorage.

### `AgentCreateForm.tsx`
- Initialises `userMCPServerIds` from `getSavedMCPSelection` instead of
`null`.
- Calls `saveMCPSelection` on every toggle.

### `AgentDetail.tsx`
- Adds localStorage as a fallback tier in `effectiveMCPServerIds`: user
override → chat record → **saved selection** → defaults.
- Calls `saveMCPSelection` on every toggle.

### `MCPServerPicker.test.ts` (new)
- 13 unit tests covering save, restore, stale-ID filtering, force_on
merging, invalid JSON handling, and disabled server filtering.

## Fallback priority

| Priority | Source | When |
|----------|--------|------|
| 1 | In-memory state | User toggled during this session |
| 2 | Chat record | Existing conversation with `mcp_server_ids` |
| 3 | localStorage | User has a saved selection from a prior session |
| 4 | Server defaults | `force_on` + `default_on` servers |
2026-03-25 07:51:34 -04:00
Kyle Carberry 894fcecfdc fix: inherit MCP server IDs from parent chat when spawning subagents (#23571)
Child chats created via `spawn_agent` and `spawn_computer_use_agent`
were not inheriting the parent's `MCPServerIDs`, meaning subagents lost
access to the parent's MCP server tools.

## Changes

- Pass `parent.MCPServerIDs` in the `CreateOptions` for both
`createChildSubagentChat()` and the `spawn_computer_use_agent` tool
handler in `coderd/x/chatd/subagent.go`.

## Tests

Added 3 tests in `subagent_internal_test.go`:
- `TestCreateChildSubagentChat_InheritsMCPServerIDs` — verifies child
chat gets parent's MCP server IDs (multiple servers)
- `TestSpawnComputerUseAgent_InheritsMCPServerIDs` — verifies computer
use subagent gets parent's MCP server IDs via the tool
- `TestCreateChildSubagentChat_NoMCPServersStaysEmpty` — verifies no
regression when parent has no MCP servers
2026-03-25 11:22:18 +00:00
Danny Kopping 3220d1d528 fix(coderd/x/chatd): use *_TEST_API_KEY env vars in integration tests instead of *_API_KEY (#23567)
*Disclaimer: implemented by a Coder Agent and reviewed by me.*

Renames the env vars used by chatd integration tests from the canonical
`SOMEPROVIDER_API_KEY` (e.g. `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`) to
`SOMEPROVIDER_TEST_API_KEY` (e.g. `ANTHROPIC_TEST_API_KEY`,
`OPENAI_TEST_API_KEY`) so that test-specific keys don't collide with
production/canonical provider credentials.

Relates to https://github.com/coder/internal/issues/1425

See also:
https://codercom.slack.com/archives/C0AGTPWLA3U/p1774433646799499
2026-03-25 11:04:53 +00:00
Danielle Maywood c408210661 refactor(site/src/pages/AgentsPage): remove dead typeof window checks (#23559) 2026-03-25 10:48:07 +00:00
Michael Suchacz 5f57465518 fix: support xhigh reasoning effort for OpenAI models (#23545)
## Summary

Adds `xhigh` to the OpenAI reasoning effort normalizer so GPT-5.4 class
models can use `reasoning_effort: xhigh` without it being silently
dropped.

## Problem

The SDK schema (`codersdk/chats.go`) already advertises `xhigh` as a
valid `reasoning_effort` value, but the runtime normalizer in
`chatprovider.go` only accepts `minimal|low|medium|high` for the OpenAI
provider. When a user sets `xhigh`, `ReasoningEffortFromChat()` returns
`nil` and the value never reaches the OpenAI API.

## Changes

- **Fantasy dependency**: Updated `kylecarbs/fantasy` (cj/go1.25) which
now includes the `ReasoningEffortXHigh` constant
([kylecarbs/fantasy#9](https://github.com/kylecarbs/fantasy/pull/9)).
- **`chatprovider.go`**: Adds `fantasyopenai.ReasoningEffortXHigh` to
the OpenAI case in `ReasoningEffortFromChat()`.
- **`chatprovider_test.go`**: Adds `OpenAIXHighEffort` test case.

## Upstream

-
[charmbracelet/fantasy#186](https://github.com/charmbracelet/fantasy/pull/186)
2026-03-25 11:44:05 +01:00
Cian Johnston 46edaf2112 test: reduce number of coderdtest instances (#23463)
Consolidates coderdtest invocations in 7 tests to reduce 23 instances to 7 across:
- `TestGetUser` (3 → 1) — read-only user lookups
- `TestUserTerminalFont` (3 → 1) — each creates own user via
CreateAnotherUser
- `TestUserTaskNotificationAlertDismissed` (3 → 1) — each creates own
user
- `TestUserLogin` (3 → 1) — each creates/deletes own user
- `TestExpMcpConfigureClaudeCode` (5 → 1) — writes to isolated temp dirs
- `TestOAuth2RegistrationTokenSecurity` (3 → 1) — independent
registrations
- `TestOAuth2SpecificErrorScenarios` (3 → 1) — independent error
scenarios

> 🤖 This PR was created with the help of Coder Agents, and has been
reviewed by my human. 🧑‍💻
2026-03-25 09:53:06 +00:00
Kacper Sawicki 72976b4749 feat(site): warn about active prebuilds when duplicating template (#22945)
## Description

When duplicating a template that has prebuilds configured, a warning
alert is now shown above the create template form. The warning displays
the total number of prebuilds that will be automatically created.

![Warning
example](https://img.shields.io/badge/⚠️-This_template_has_prebuilds_configured-orange)

### Changes

**Single file modified:**
`site/src/pages/CreateTemplatePage/DuplicateTemplateView.tsx`

- Fetches presets for the template's active version using the existing
`templateVersionPresets` React Query helper
- Computes total prebuild count by summing `DesiredPrebuildInstances`
across all presets
- Renders a warning `<Alert>` above the form when prebuilds are
configured

### Design decisions

| Decision | Rationale |
|---|---|
| Warning in `DuplicateTemplateView`, not `CreateTemplateForm` | Only
the duplicate flow needs this. Keeps data fetching local. No new props.
|
| Feature-flag gated (`workspace_prebuilds`) | Matches existing pattern
in `TemplateLayout.tsx`. |
| Non-blocking query | Presets fetch failure shouldn't prevent
duplication. Warning is informational. |
| Count with pluralization | Users know exactly how many prebuilds will
spin up. |

<img width="1136" height="375" alt="image"
src="https://github.com/user-attachments/assets/1ca42608-a204-48f5-b27d-6d476ab32fa7"
/>


Closes #18987
2026-03-25 10:36:17 +01:00
Jake Howell 4bfa0b197b chore: update offlinedocs/ logo to new coder logo (#23550)
This is a super boring change, put simply we're using the old logo still
in our `offlinedocs/` subfolder. I noticed this when working through
#23549.

| Old | New |
| --- | --- |
| <img width="1624" height="1061" alt="image"
src="https://github.com/user-attachments/assets/fb555630-2f69-45e8-a320-d57bfebc32ec"
/> | <img width="1624" height="1061" alt="image"
src="https://github.com/user-attachments/assets/7787e3fa-87f7-491d-b8f4-7ccb17ccb091"
/>
2026-03-25 20:35:38 +11:00
Jakub Domeracki 6bc6e2baa6 fix: explicitly trust our own GPG key (#23556)
GPG emits an "untrusted key" warning when signing with a key that hasn't
been assigned a trust level, which can cause verification steps to fail
or produce noisy output.

Example:
```sh
gpg: Signature made Tue Mar 24 20:56:59 2026 UTC
gpg:                using RSA key 21C96B1CB950718874F64DBD6A5A671B5E40A3B9
gpg: Good signature from "Coder Release Signing Key <security@coder.com>" [unknown]
gpg: WARNING: This key is not certified with a trusted signature!
gpg:          There is no indication that the signature belongs to the owner.
Primary key fingerprint: 21C9 6B1C B950 7188 74F6  4DBD 6A5A 671B 5E40 A3B9
```

After importing the release key, derive its fingerprint from the keyring
and mark it as ultimately trusted via `--import-ownertrust`.
The fingerprint is extracted dynamically rather than hard-coded, so this
works for any key supplied via `CODER_GPG_RELEASE_KEY_BASE64`.
2026-03-25 10:24:31 +01:00
Jake Howell 0cea4de69e fix: AI governance into AI Governance (#23553) 2026-03-25 20:06:48 +11:00
Sas Swart 98143e1b70 fix(coderd): allow template deletion when only prebuild workspaces remain (#23417)
## Problem

Template administrators cannot delete templates that have running
prebuilds.
The `deleteTemplate` handler fetches all non-deleted workspaces and
blocks
deletion if any exist, making no distinction between human-owned
workspaces
and prebuild workspaces (owned by the system `PrebuildsSystemUserID`).

This forces admins into a manual multi-step workflow: set
`desired_instances`
to 0 on every preset, wait for the reconciler to drain prebuilds, then
retry
deletion. Prebuilds are an internal system concern that admins should
not need
to manage manually.

## Fix

Replace the blanket `len(workspaces) > 0` guard in `deleteTemplate` with
a
loop that only blocks deletion when a non-prebuild (human-owned)
workspace
exists. Prebuild workspaces — owned by `database.PrebuildsSystemUserID`
— are
now ignored during the check.

Once the template is soft-deleted (`deleted=true`), the existing
prebuilds
reconciler detects `isActive()=false` and cleans up remaining prebuilds
asynchronously. No changes to the reconciler are needed.

The error message and HTTP status for human workspaces remain unchanged.

## Testing

Added two new subtests to `TestDeleteTemplate`:
- **`OnlyPrebuilds`**: deletion succeeds when only prebuild workspaces
exist.
- **`PrebuildsAndHumanWorkspaces`**: deletion is blocked when both
prebuild
  and human workspaces exist.

Existing reconciler test ("soft-deleted templates MAY have prebuilds")
already
covers post-deletion prebuild cleanup.
2026-03-25 09:43:06 +02:00
Ethan 70f031d793 feat(coderd/chatd): structured chat error classification and retry hardening (#23275)
> **PR Stack**
> 1. #23351 ← `#23282`
> 2. #23282 ← `#23275`
> 3. **#23275** ← `#23349` *(you are here)*
> 4. #23349 ← `main`

---

## Summary

Extracts a structured error classification subsystem for agent chat
(`chatd`) so that retry and error payloads carry machine-readable
metadata — error kind, provider name, HTTP status code, and retryability
— instead of raw error strings.

This is the **backend half** of the error-handling work. The frontend
counterpart is in #23282.

## Changes

### New package: `coderd/chatd/chaterror/`

Canonical error classification — extracts error kind, provider, status
code, and user-facing message from raw provider errors. One source of
truth that drives both retry policy and stream payloads.

- **`kind.go`**: Error kind enum (`rate_limit`, `timeout`, `auth`,
`config`, `overloaded`, `unknown`).
- **`signals.go`**: Signal extraction — parses provider name, HTTP
status code, and retryability from error strings and wrapped types.
- **`classify.go`**: Classification logic — maps extracted signals to an
error kind.
- **`message.go`**: User-facing message templates keyed by kind +
signals.
- **`payload.go`**: Projectors that build `ChatStreamError` and
`ChatStreamRetry` payloads from a classified error.

### Modified

- **`codersdk/chats.go`**: Added `Kind`, `Provider`, `Retryable`,
`StatusCode` fields to `ChatStreamError` and `ChatStreamRetry`.
- **`coderd/chatd/chatretry/`**: Thinned to retry-policy only;
classification logic moved to `chaterror`.
- **`coderd/chatd/chatloop/`**: Added per-attempt first-chunk timeout
(60 s) via `guardedStream` wrapper — produces retryable
`startup_timeout` errors instead of hanging forever.
- **`coderd/chatd/chatd.go`**: Publishes normalized retry/error payloads
via `chaterror` projectors.
2026-03-25 13:47:54 +11:00
Mathias Fredriksson 38f723288f fix: correct malformed struct tags in organizationroles and scim_test (#23497)
Fix leading space in table tag and escaped-quote tag syntax.

Extracted from #23201.
2026-03-25 13:11:08 +11:00
Jeremy Ruppel 8bd87f8588 feat(site): add AI sessions list page (#23388)
<!--

If you have used AI to produce some or all of this PR, please ensure you
have read our [AI Contribution
guidelines](https://coder.com/docs/about/contributing/AI_CONTRIBUTING)
before submitting.

-->

Adds the AI Bridge sessions list page.
2026-03-24 22:01:10 -04:00
Jeremy Ruppel 210dbb6d98 feat(site): add AI Bridge sessions queries (#23385)
Introduces the query for paginated sessions
2026-03-24 20:56:29 -04:00
Danielle Maywood 4a0d707bca fix(site): compact context compaction rows in behavior settings (#23543) 2026-03-25 00:39:17 +00:00
Danielle Maywood 6a04e76b48 fix(site): fix AgentDetailView storybook tests hanging indefinitely (#23544) 2026-03-25 00:19:07 +00:00