Commit Graph
13183 Commits
Author SHA1 Message Date
Matt Vollmer 08577006c6 fix(site): improve Workspace Autostop Fallback UX on agents settings page (#23465)
https://github.com/user-attachments/assets/a482ef45-402a-4d86-af59-b1526b2ce3e2

## Summary

Redesigns the **Default Autostop** section on the `/agents` settings
page to clarify that it is a fallback for chat-linked workspaces whose
templates do not define their own autostop policy. Template-level
settings always take priority — this is a backstop, not an override.

## Changes

### UX
- Renamed to **Workspace Autostop Fallback** with clearer description
- Replaced always-visible duration field (confusing `0` in an hours box)
with a **toggle-to-enable** pattern matching the Virtual Desktop section
- Toggle ON auto-saves with a 1-hour default; toggle OFF auto-saves with
0
- Save button is always visible when the toggle is on but disabled until
the user changes the duration value
- Per-section disabled flags — toggling autostop no longer freezes the
Virtual Desktop switch or prompt textareas during the save round-trip

### Reliability
- `onError` rollback on toggle auto-saves so the UI snaps back to server
truth on failure
- Stateful mocks in Storybook stories to prevent race conditions from
instant mock resolution

### Accessibility
- Added `aria-label="Autostop duration"` to the DurationField input
- Updated `DurationField` component to merge external `inputProps` with
internal ones (preserves `step: 1`)

### Stories
- Updated all existing autostop stories for the new toggle-based flow
- Added `DefaultAutostopToggleOff` — tests disabling from an enabled
state
- Added `DefaultAutostopSaveDisabled` — verifies Save button is visible
but disabled when no duration change

---

PR generated with Coder Agents
2026-03-24 09:28:10 -04:00
Kyle Carberry 13241a58ba fix(coderd/x/chatd/mcpclient): use dedicated HTTP transport per MCP connection (#23494)
## Problem

`TestConnectAll_MultipleServers` flakes with:

```
net/http: HTTP/1.x transport connection broken: http: CloseIdleConnections called
```

Each MCP client connection implicitly uses `http.DefaultTransport`. When
`httptest.Server.Close()` runs during parallel test cleanup, it calls
`CloseIdleConnections` on `http.DefaultTransport`, breaking in-flight
connections from other goroutines or parallel tests sharing that
transport.

## Fix

Clone the default transport for each MCP connection via
`http.DefaultTransport.(*http.Transport).Clone()`, passed through
`WithHTTPBasicClient` (StreamableHTTP) and `WithHTTPClient` (SSE). This
scopes idle connection cleanup to a single MCP server so it cannot
disrupt unrelated connections.

Fixes coder/internal#1420
2026-03-24 09:22:45 -04:00
Kyle Carberry 631e4449bb fix: use actual config ID in MCP OAuth2 redirect URI during auto-discovery (#23491)
## Problem

During OAuth2 auto-discovery for MCP servers, the callback URL
registered with the remote authorization server via Dynamic Client
Registration (RFC 7591) contained the literal string `{id}` instead of
the actual config UUID:

```
https://coder.example.com/api/experimental/mcp/servers/{id}/oauth2/callback
```

This happened because the discovery and registration occurred **before**
the database insert that generates the ID. When the user later initiated
the OAuth2 connect flow, the redirect URL used the real UUID, causing
the authorization server to reject it with:

> The provided redirect URIs are not approved for use by this
authorization server

## Fix

Restructure the auto-discovery flow in `createMCPServerConfig` to:

1. **Insert** the MCP server config first (with empty OAuth2 fields) to
get the database-generated UUID
2. **Build** the callback URL with the actual UUID
3. **Perform** OAuth2 discovery and dynamic client registration with the
correct URL
4. **Update** the record with the discovered OAuth2 credentials
5. **Clean up** the record if discovery fails

## Testing

Added regression test
`TestMCPServerConfigsOAuth2AutoDiscovery/RedirectURIContainsRealConfigID`
that:
- Stands up mock auth + MCP servers
- Captures the `redirect_uris` sent during dynamic client registration
- Asserts the URI contains the real config UUID, not `{id}`
- Verifies the full callback path structure

All existing MCP server config tests continue to pass.
2026-03-24 13:04:55 +00:00
Matt Vollmer 76eac82e5b docs: soften security implications intro wording (#23492) 2026-03-24 08:59:33 -04:00
Michael Suchacz 405d81be09 fix(coderd/database): fall back to model names in PR insights (#23490)
Fallback to the configured model name in PR Insights when a model config
has a blank display name.

This updates both the by-model breakdown and recent PR rows, and adds a
regression test for blank display names.
2026-03-24 13:58:29 +01:00
Mathias Fredriksson 1c0442c247 fix(agent/agentfiles): fix replace_all in fuzzy matching mode (#23480)
replace_all in fuzzy mode (passes 2 and 3 of fuzzyReplace) only
replaced the first match. seekLines returned the first match,
spliceLines replaced one range, and there was no loop.

Extract fuzzy pass logic into fuzzyReplaceLines which:
- Returns a 3-tuple (result, matched, error) for clean caller flow
- When replaceAll is true, collects all non-overlapping matches
  then applies replacements from last to first to preserve indices
- When replaceAll is false with multiple matches, returns an error

Add test cases for replace_all with fuzzy trailing whitespace and
fuzzy indent matching.
2026-03-24 14:41:45 +02:00
Mathias Fredriksson 16edcbdd5b fix(agent/agentfiles): follow symlinks in write_file and edit_files (#23478)
Both write_file and edit_files use atomic writes (write to temp
file, then rename). Since rename operates on directory entries, it
replaces symlinks with regular files instead of writing through
the link to the target.

Add resolveSymlink() that uses afero.Lstater/LinkReader to resolve
symlink chains (up to 10 levels) before the atomic write. Both
writeFile and editFile resolve the path before any filesystem
operations, matching the behavior of 'echo content > symlink'.

Gracefully no-ops on filesystems that don't support symlinks (e.g.
MemMapFs used in existing tests).
2026-03-24 12:39:55 +00:00
Kyle Carberry f62f2ffe6a feat(site): add MCP server picker to agent chat UI (#23470)
## Summary

Adds a user-facing MCP server configuration panel to the chat input
toolbar. Users can toggle which MCP servers provide tools for their chat
sessions, and authenticate with OAuth2 servers via popup windows.

## Changes

### New Components
- **`MCPServerPicker`** (`MCPServerPicker.tsx`): Popover-based picker
that appears in the chat input toolbar next to the model selector. Shows
all enabled MCP servers with toggles.
- **`MCPServerPicker.stories.tsx`**: 13 Storybook stories covering all
states.

### Availability Policies
Respects the admin-configured availability for each server:
- **`force_on`**: Always active, toggle disabled, lock icon shown. User
cannot disable.
- **`default_on`**: Pre-selected by default, user can opt out via
toggle.
- **`default_off`**: Not selected by default, user must opt in via
toggle.

### OAuth2 Authentication
For servers with `auth_type: "oauth2"`:
- Shows auth status (connected/not connected)
- "Connect to authenticate" link opens a popup window to
`/api/experimental/mcp/servers/{id}/oauth2/connect`
- Listens for `postMessage` with `{type: "mcp-oauth2-complete"}` from
the callback page
- Same UX pattern as external auth on the Create Workspace screen

### Integration Points
- `AgentChatInput`: MCP picker appears in the toolbar after the model
selector
- `AgentDetail`: Manages MCP selection state, initializes from
`chat.mcp_server_ids` or defaults
- `AgentDetailView` / `AgentDetailContent`: Props plumbed through to
input
- `AgentCreatePage` / `AgentCreateForm`: MCP selection for new chats
- `mcp_server_ids` now sent with `CreateChatMessageRequest` and
`CreateChatRequest`

### Helper
- `getDefaultMCPSelection()`: Computes default selection from
availability policies (`force_on` + `default_on`)

## Storybook Stories
| Story | Description |
|-------|-------------|
| NoServers | No servers - picker hidden |
| AllDisabled | All disabled servers - picker hidden |
| SingleForceOn | Force-on server with locked toggle |
| SingleDefaultOnNoAuth | Default-on with no auth required |
| SingleDefaultOff | Optional server not selected |
| OAuthNeedsAuth | OAuth2 server needing authentication |
| OAuthConnected | OAuth2 server already connected |
| MixedServers | Multiple servers with mixed availability/auth |
| AllConnected | All OAuth2 servers authenticated |
| Disabled | Picker in disabled state |
| WithDisabledServer | Disabled servers filtered out |
| AllOptedOut | All toggled off except force_on |
| OptionalOAuthNeedsAuth | Optional OAuth2 needing auth |
2026-03-24 08:13:18 -04:00
Vlad 2dc3466f07 docs: update JetBrains client downloader link (#23287) 2026-03-24 11:36:20 +00:00
Cian Johnston cbd56d33d4 ci: disable go cache for build jobs to prevent disk space exhaustion (#23484)
Disables Go cache for the setup-go step to workaround depot runner disk space issues.
2026-03-24 11:17:39 +00:00
Mathias Fredriksson b23aed034f fix: make terraform ConvertState fully deterministic (#23459)
All map iterations in ConvertState now use sorted helpers instead of
ranging over Go maps directly. Previously only coder_env and
coder_script were sorted (via sortedResourcesByType). This extends
the pattern to coder_agent, coder_devcontainer, coder_agent_instance,
coder_app, coder_metadata, coder_external_auth, and the main
resource output list.

Also fixes generate.sh writing version.txt to the wrong directory
(resources/ instead of testdata/), which caused the Makefile version
check to silently desync and trigger unnecessary regeneration.

Adds TestConvertStateDeterministic that calls ConvertState 10 times
per fixture and asserts byte-identical JSON output without any
post-hoc sorting.
2026-03-24 11:02:45 +00:00
Ethan 56e80b0a27 fix(site): use HttpResponse constructor for binary mock response (#23474)
## Context

`./scripts/develop.sh` was failing to build in my dogfood workspace
with:

```
src/testHelpers/handlers.ts(346,35): error TS2345: Argument of type 'NonSharedBuffer'
is not assignable to parameter of type 'ArrayBuffer'.
  Type 'Buffer<ArrayBuffer>' is missing the following properties from type 'ArrayBuffer':
  maxByteLength, resizable, resize, detached, and 2 more.
```

## Alternatives considered

**`fileBuffer.buffer`** — `.buffer` gives you the underlying
`ArrayBuffer`, but Node pools small buffers into a shared 8 KB slab. A
`Buffer.from("hello")` has `byteOffset: 1472` and `.buffer.byteLength:
8192` — passing `.buffer` to a `Response` sends all 8,192 bytes instead
of 5. It happens to work for `readFileSync` (dedicated allocation,
offset 0), but breaks silently if someone refactors how the buffer is
constructed.

**`fileBuffer.buffer.slice(byteOffset, byteOffset + byteLength)`** — the
safe version of the above. Always correct, but unnecessarily complex.

**`new HttpResponse(fileBuffer)`** (chosen) — `HttpResponse` extends
`Response`, whose constructor accepts `BodyInit` which includes
`Uint8Array`. When you pass a typed array view, `Response` reads only
the bytes within that view (respecting `byteOffset`/`byteLength`), so
it's safe regardless of pooling. `Buffer` is a `Uint8Array` subclass, so
this just works:

```
pooled = Buffer.from("hello")   → byteOffset: 1472, .buffer: 8192 bytes
new Response(pooled.buffer)     → body: 8192 bytes ✗
new Response(pooled)            → body: 5 bytes    ✓
```
2026-03-24 21:53:56 +11:00
Danny Kopping dba9f68b11 chore!: remove members' ability to read their own interceptions; rationalize RBAC requirements (#23320)
_Disclaimer:_ _produced_ _by_ _Claude_ _Opus_ _4\.6,_ _reviewed_ _by_ _me._

**This is a breaking change.** Users who are not have `owner` or sitewide `auditor` roles will no longer be able to view interceptions.  
Regular users should not need to view this information; in fact, it could be used by a malicious insider to see what information we track and don't track to exfiltrate data or perform actions unobserved.

---

Changed authorization for AI Bridge interception-related operations from system-level permissions to resource-specific permissions. The following functions now authorize against `rbac.ResourceAibridgeInterception` instead of `rbac.ResourceSystem`:

- `ListAIBridgeTokenUsagesByInterceptionIDs`
- `ListAIBridgeToolUsagesByInterceptionIDs`
- `ListAIBridgeUserPromptsByInterceptionIDs`

Updated RBAC roles to grant AI Bridge interception permissions:

- **User/Member roles**: Can create and update AI Bridge interceptions but cannot read them back
- **Service accounts**: Same create/update permissions without read access
- **Owners/Auditors**: Retain full read access to all interceptions

Removed system-level authorization bypass in `populatedAndConvertAIBridgeInterceptions` function, allowing proper resource-level authorization checks.

Updated tests to reflect the new permission model where members cannot view AI Bridge interceptions, even their own, while owners and auditors maintain full visibility.
2026-03-24 12:03:20 +02:00
Jaayden Halko 245ce91199 feat: add bar charts for premium and AI governance add-on license usage (#23442)
Implemented with the help of Cursor agents using Figma MCP

Figma design:
https://www.figma.com/design/klGTlHSPQwI4KBvAMdebrx/Customer-Usage-Controls-for-AI-Governance-Add-On?node-id=448-7658&m=dev

<img width="1143" height="639" alt="Screenshot 2026-03-23 at 20 10 05"
src="https://github.com/user-attachments/assets/300d4d5d-aad2-49a9-bfdd-a329312e5fa8"
/>
2026-03-24 09:07:06 +00:00
Danielle Maywood 5d0734e005 fix(site): diff viewer virtualizer buffer fix and styling polish (#23462) 2026-03-24 09:04:14 +00:00
Danny Kopping 43a1af3cd6 feat: session list API (#23202)
<!--

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.

-->

_Disclaimer:_ _initially_ _produced_ _by_ _Claude_ _Opus_ _4\.6,_ _heavily_ _modified_ _and_ _reviewed_ _by_ _me._

Closes https://github.com/coder/internal/issues/1360

Adds a new `/api/v2/aibridge/sessions` API which returns "sessions".

Sessions, as defined in the [RFC](https://www.notion.so/coderhq/AI-Bridge-Sessions-Threads-2ccd579be59280f28021d3baf7472fbe?source=copy_link), are a set of interceptions logically grouped by a session key issued by the client.  
The API design for this endpoint was done in [this doc](https://github.com/coder/internal/issues/1360).

If the client has not provided a session ID, we will revert to the thread root ID, and if that's not present we use the interception's own ID (i.e. a session of a single interception - which is effectively what we show currently in our `/api/v2/aibridge/interceptions` API).

The SQL query looks gnarly but it's relatively simple, and seems to perform well (~200ms) even when I import dogfood's `aibridge_*` tables into my workspace. If we need to improve performance on this later we can investigate materialized views, perhaps, but for now I don't think it's warranted.

---

_The PR looks large but it's got a lot of generated code; the actual changes aren't huge._
2026-03-24 08:58:47 +02:00
Jaayden Halko 3d5d58ec2b fix: make LicenseCard stories use deterministic dates (#23437)
## Summary
- replace dynamic dayjs() date generation in LicenseCard stories with
fixed deterministic timestamps
- preserve story behavior while preventing day-over-day visual drift in
Chromatic
- use shared constants for expired and future date scenarios
2026-03-24 04:38:23 +00:00
dependabot[bot] 37d937554e ci: bump dorny/paths-filter from 3.0.2 to 4.0.1 in the github-actions group (#23435)
Bumps the github-actions group with 1 update:
[dorny/paths-filter](https://github.com/dorny/paths-filter).

Updates `dorny/paths-filter` from 3.0.2 to 4.0.1
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/dorny/paths-filter/releases">dorny/paths-filter's
releases</a>.</em></p>
<blockquote>
<h2>v4.0.1</h2>
<h2>What's Changed</h2>
<ul>
<li>Support merge queue by <a
href="https://github.com/masaru-iritani"><code>@​masaru-iritani</code></a>
in <a
href="https://redirect.github.com/dorny/paths-filter/pull/255">dorny/paths-filter#255</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a
href="https://github.com/masaru-iritani"><code>@​masaru-iritani</code></a>
made their first contribution in <a
href="https://redirect.github.com/dorny/paths-filter/pull/255">dorny/paths-filter#255</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/dorny/paths-filter/compare/v4.0.0...v4.0.1">https://github.com/dorny/paths-filter/compare/v4.0.0...v4.0.1</a></p>
<h2>v4.0.0</h2>
<h2>What's Changed</h2>
<ul>
<li>feat: update action runtime to node24 by <a
href="https://github.com/saschabratton"><code>@​saschabratton</code></a>
in <a
href="https://redirect.github.com/dorny/paths-filter/pull/294">dorny/paths-filter#294</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a
href="https://github.com/saschabratton"><code>@​saschabratton</code></a>
made their first contribution in <a
href="https://redirect.github.com/dorny/paths-filter/pull/294">dorny/paths-filter#294</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/dorny/paths-filter/compare/v3.0.3...v4.0.0">https://github.com/dorny/paths-filter/compare/v3.0.3...v4.0.0</a></p>
<h2>v3.0.3</h2>
<h2>What's Changed</h2>
<ul>
<li>Add missing predicate-quantifier by <a
href="https://github.com/wardpeet"><code>@​wardpeet</code></a> in <a
href="https://redirect.github.com/dorny/paths-filter/pull/279">dorny/paths-filter#279</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/wardpeet"><code>@​wardpeet</code></a>
made their first contribution in <a
href="https://redirect.github.com/dorny/paths-filter/pull/279">dorny/paths-filter#279</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/dorny/paths-filter/compare/v3...v3.0.3">https://github.com/dorny/paths-filter/compare/v3...v3.0.3</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/dorny/paths-filter/blob/master/CHANGELOG.md">dorny/paths-filter's
changelog</a>.</em></p>
<blockquote>
<h1>Changelog</h1>
<h2>v4.0.0</h2>
<ul>
<li><a
href="https://redirect.github.com/dorny/paths-filter/pull/294">Update
action runtime to node24</a></li>
</ul>
<h2>v3.0.3</h2>
<ul>
<li><a
href="https://redirect.github.com/dorny/paths-filter/pull/279">Add
missing predicate-quantifier</a></li>
</ul>
<h2>v3.0.2</h2>
<ul>
<li><a
href="https://redirect.github.com/dorny/paths-filter/pull/224">Add
config parameter for predicate quantifier</a></li>
</ul>
<h2>v3.0.1</h2>
<ul>
<li><a
href="https://redirect.github.com/dorny/paths-filter/pull/133">Compare
base and ref when token is empty</a></li>
</ul>
<h2>v3.0.0</h2>
<ul>
<li><a
href="https://redirect.github.com/dorny/paths-filter/pull/210">Update to
Node.js 20</a></li>
<li><a
href="https://redirect.github.com/dorny/paths-filter/pull/215">Update
all dependencies</a></li>
</ul>
<h2>v2.11.1</h2>
<ul>
<li><a
href="https://redirect.github.com/dorny/paths-filter/pull/167">Update
<code>@​actions/core</code> to v1.10.0 - Fixes warning about deprecated
set-output</a></li>
<li><a
href="https://redirect.github.com/dorny/paths-filter/pull/168">Document
need for pull-requests: read permission</a></li>
<li><a
href="https://redirect.github.com/dorny/paths-filter/pull/164">Updating
to actions/checkout@v3</a></li>
</ul>
<h2>v2.11.0</h2>
<ul>
<li><a
href="https://redirect.github.com/dorny/paths-filter/pull/157">Set
list-files input parameter as not required</a></li>
<li><a
href="https://redirect.github.com/dorny/paths-filter/pull/161">Update
Node.js</a></li>
<li><a
href="https://redirect.github.com/dorny/paths-filter/pull/162">Fix
incorrect handling of Unicode characters in exec()</a></li>
<li><a
href="https://redirect.github.com/dorny/paths-filter/pull/163">Use
Octokit pagination</a></li>
<li><a
href="https://redirect.github.com/dorny/paths-filter/pull/160">Updates
real world links</a></li>
</ul>
<h2>v2.10.2</h2>
<ul>
<li><a href="https://redirect.github.com/dorny/paths-filter/pull/91">Fix
getLocalRef() returns wrong ref</a></li>
</ul>
<h2>v2.10.1</h2>
<ul>
<li><a
href="https://redirect.github.com/dorny/paths-filter/pull/85">Improve
robustness of change detection</a></li>
</ul>
<h2>v2.10.0</h2>
<ul>
<li><a href="https://redirect.github.com/dorny/paths-filter/pull/82">Add
ref input parameter</a></li>
<li><a href="https://redirect.github.com/dorny/paths-filter/pull/83">Fix
change detection in PR when pullRequest.changed_files is
incorrect</a></li>
</ul>
<h2>v2.9.3</h2>
<ul>
<li><a href="https://redirect.github.com/dorny/paths-filter/pull/78">Fix
change detection when base is a tag</a></li>
</ul>
<h2>v2.9.2</h2>
<ul>
<li><a href="https://redirect.github.com/dorny/paths-filter/pull/75">Fix
fetching git history</a></li>
</ul>
<h2>v2.9.1</h2>
<ul>
<li><a href="https://redirect.github.com/dorny/paths-filter/pull/74">Fix
fetching git history + fallback to unshallow repo</a></li>
</ul>
<h2>v2.9.0</h2>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/dorny/paths-filter/commit/fbd0ab8f3e69293af611ebaee6363fc25e6d187d"><code>fbd0ab8</code></a>
feat: add merge_group event support</li>
<li><a
href="https://github.com/dorny/paths-filter/commit/efb1da7ce8d89bbc261191e5a2dc1453c3837339"><code>efb1da7</code></a>
feat: add dist/ freshness check to PR workflow</li>
<li><a
href="https://github.com/dorny/paths-filter/commit/d8f7b061b24c30a325ff314b76c37adb05b041ce"><code>d8f7b06</code></a>
Merge pull request <a
href="https://redirect.github.com/dorny/paths-filter/issues/302">#302</a>
from dorny/issue-299</li>
<li><a
href="https://github.com/dorny/paths-filter/commit/addbc147a95845176e1bc013a012fbf1d366389a"><code>addbc14</code></a>
Update README for v4</li>
<li><a
href="https://github.com/dorny/paths-filter/commit/9d7afb8d214ad99e78fbd4247752c4caed2b6e4c"><code>9d7afb8</code></a>
Update CHANGELOG for v4.0.0</li>
<li><a
href="https://github.com/dorny/paths-filter/commit/782470c5d953cae2693d643172b14e01bacb71f3"><code>782470c</code></a>
Merge branch 'releases/v3'</li>
<li><a
href="https://github.com/dorny/paths-filter/commit/d1c1ffe0248fe513906c8e24db8ea791d46f8590"><code>d1c1ffe</code></a>
Update CHANGELOG for v3.0.3</li>
<li><a
href="https://github.com/dorny/paths-filter/commit/ce10459c8b92cd8901166c0a222fbb033ef39365"><code>ce10459</code></a>
Merge pull request <a
href="https://redirect.github.com/dorny/paths-filter/issues/294">#294</a>
from saschabratton/master</li>
<li><a
href="https://github.com/dorny/paths-filter/commit/5f40380c5482e806c81cec080f5192e7234d8fe9"><code>5f40380</code></a>
feat: update action runtime to node24</li>
<li><a
href="https://github.com/dorny/paths-filter/commit/668c092af3649c4b664c54e4b704aa46782f6f7c"><code>668c092</code></a>
Merge pull request <a
href="https://redirect.github.com/dorny/paths-filter/issues/279">#279</a>
from wardpeet/patch-1</li>
<li>Additional commits viewable in <a
href="https://github.com/dorny/paths-filter/compare/de90cc6fb38fc0963ad72b210f1f284cd68cea36...fbd0ab8f3e69293af611ebaee6363fc25e6d187d">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=dorny/paths-filter&package-manager=github_actions&previous-version=3.0.2&new-version=4.0.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-24 15:06:13 +11:00
dependabot[bot] 796190d435 chore: bump github.com/gohugoio/hugo from 0.157.0 to 0.158.0 (#23432)
Bumps [github.com/gohugoio/hugo](https://github.com/gohugoio/hugo) from
0.157.0 to 0.158.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/gohugoio/hugo/releases">github.com/gohugoio/hugo's
releases</a>.</em></p>
<blockquote>
<h2>v0.158.0</h2>
<p>This release adds <a
href="https://gohugo.io/functions/css/build/">css.Build</a>, native and
very fast bundling/transformation/minifying of CSS resources. Also see
the new <a
href="https://gohugo.io/functions/strings/replacepairs/">strings.ReplacePairs</a>,
a very fast option if you need to do many string replacements.</p>
<h2>Notes</h2>
<ul>
<li>Upgrade to to Go 1.26.1 (<a
href="https://redirect.github.com/gohugoio/hugo/issues/14597">#14597</a>)
(note) 1f578f16 <a href="https://github.com/bep"><code>@​bep</code></a>
<a
href="https://redirect.github.com/gohugoio/hugo/issues/14595">#14595</a>.
This fixes a security issue in Go's template package used by Hugo: <a
href="https://www.cve.org/CVERecord?id=CVE-2026-27142">https://www.cve.org/CVERecord?id=CVE-2026-27142</a></li>
</ul>
<h2>Deprecations</h2>
<p>The methods and config options are deprecated and will be removed in
a future Hugo release.</p>
<p>Also see <a
href="https://discourse.gohugo.io/t/deprecations-in-v0-158-0/56869">this
article</a></p>
<h3>Language configuration</h3>
<ul>
<li><code>languageCode</code> → Use <code>locale</code> instead.</li>
<li><code>languages.&lt;lang&gt;.languageCode</code> → Use
<code>languages.&lt;lang&gt;.locale</code> instead.</li>
<li><code>languages.&lt;lang&gt;.languageName</code> → Use
<code>languages.&lt;lang&gt;.label</code> instead.</li>
<li><code>languages.&lt;lang&gt;.languageDirection</code> → Use
<code>languages.&lt;lang&gt;.direction</code> instead.</li>
</ul>
<h3>Language methods</h3>
<ul>
<li><code>.Site.LanguageCode</code> → Use
<code>.Site.Language.Locale</code> instead.</li>
<li><code>.Language.LanguageCode</code> → Use
<code>.Language.Locale</code> instead.</li>
<li><code>.Language.LanguageName</code> → Use
<code>.Language.Label</code> instead.</li>
<li><code>.Language.LanguageDirection</code> → Use
<code>.Language.Direction</code> instead.</li>
</ul>
<h2>Bug fixes</h2>
<ul>
<li>tpl/css: Fix external source maps e431f90b <a
href="https://github.com/bep"><code>@​bep</code></a> <a
href="https://redirect.github.com/gohugoio/hugo/issues/14620">#14620</a></li>
<li>hugolib: Fix server no watch 59e0446f <a
href="https://github.com/jmooring"><code>@​jmooring</code></a> <a
href="https://redirect.github.com/gohugoio/hugo/issues/14615">#14615</a></li>
<li>resources: Fix context canceled on GetRemote with per-request
timeout 842d8f10 <a href="https://github.com/bep"><code>@​bep</code></a>
<a
href="https://redirect.github.com/gohugoio/hugo/issues/14611">#14611</a></li>
<li>tpl/tplimpl: Prefer early suffixes when media type matches 4eafd9eb
<a href="https://github.com/bep"><code>@​bep</code></a> <a
href="https://redirect.github.com/gohugoio/hugo/issues/13877">#13877</a>
<a
href="https://redirect.github.com/gohugoio/hugo/issues/14601">#14601</a></li>
<li>all: Run go fix ./... e3108225 <a
href="https://github.com/bep"><code>@​bep</code></a></li>
<li>internal/warpc: Fix SIGSEGV in Close() when dispatcher fails to
start c9b88e4d <a href="https://github.com/bep"><code>@​bep</code></a>
<a
href="https://redirect.github.com/gohugoio/hugo/issues/14536">#14536</a></li>
<li>Fix index out of range panic in fileEventsContentPaths f797f849 <a
href="https://github.com/bep"><code>@​bep</code></a> <a
href="https://redirect.github.com/gohugoio/hugo/issues/14573">#14573</a></li>
</ul>
<h2>Improvements</h2>
<ul>
<li>resources: Re-publish on transformation cache hit 3c980c07 <a
href="https://github.com/bep"><code>@​bep</code></a> <a
href="https://redirect.github.com/gohugoio/hugo/issues/14629">#14629</a></li>
<li>create/skeletons: Use css.Build in theme skeleton 404ac000 <a
href="https://github.com/jmooring"><code>@​jmooring</code></a> <a
href="https://redirect.github.com/gohugoio/hugo/issues/14626">#14626</a></li>
<li>tpl/css: Add a test case for rebuilds on CSS options changes
06fcb724 <a href="https://github.com/bep"><code>@​bep</code></a></li>
<li>hugolib: Allow regular pages to cascade to self 9b5f1d49 <a
href="https://github.com/jmooring"><code>@​jmooring</code></a> <a
href="https://redirect.github.com/gohugoio/hugo/issues/14627">#14627</a></li>
<li>tpl/css: Allow the user to override single loader entries 623722bb
<a href="https://github.com/bep"><code>@​bep</code></a> <a
href="https://redirect.github.com/gohugoio/hugo/issues/14623">#14623</a></li>
<li>tpl/css: Make default loader resolution for CSS <a
href="https://github.com/import"><code>@​import</code></a> and url()
always behave the same a7cbcf15 <a
href="https://github.com/bep"><code>@​bep</code></a> <a
href="https://redirect.github.com/gohugoio/hugo/issues/14619">#14619</a></li>
<li>internal/js: Add default mainFields for CSS builds 36cdb2c7 <a
href="https://github.com/jmooring"><code>@​jmooring</code></a> <a
href="https://redirect.github.com/gohugoio/hugo/issues/14614">#14614</a></li>
<li>Add css.Build 3e3b849c <a
href="https://github.com/bep"><code>@​bep</code></a> <a
href="https://redirect.github.com/gohugoio/hugo/issues/14609">#14609</a>
<a
href="https://redirect.github.com/gohugoio/hugo/issues/14613">#14613</a></li>
<li>resources: Use full path for Exif etc. decoding error/warning
messages c47ec233 <a
href="https://github.com/bep"><code>@​bep</code></a> <a
href="https://redirect.github.com/gohugoio/hugo/issues/12693">#12693</a></li>
<li>Move to new locales library and upgrade CLDR from v36.1 to v48.1
4652ae4a <a href="https://github.com/bep"><code>@​bep</code></a></li>
<li>tpl/strings: Add strings.ReplacePairs function 13a95b9c <a
href="https://github.com/jmooring"><code>@​jmooring</code></a> <a
href="https://redirect.github.com/gohugoio/hugo/issues/14594">#14594</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/gohugoio/hugo/commit/f41be7959a44108641f1e081adf5c4be7fc1bb63"><code>f41be79</code></a>
releaser: Bump versions for release of 0.158.0</li>
<li><a
href="https://github.com/gohugoio/hugo/commit/0e46a97e8a0d5b7ad1dbea1a39dace7a3ee29fcf"><code>0e46a97</code></a>
deps: Upgrade github.com/evanw/esbuild v0.27.3 =&gt; v0.27.4</li>
<li><a
href="https://github.com/gohugoio/hugo/commit/c27d9e8fcfa5aad6cfedd0552add2a6c8ec74525"><code>c27d9e8</code></a>
build(deps): bump github.com/getkin/kin-openapi from 0.133.0 to
0.134.0</li>
<li><a
href="https://github.com/gohugoio/hugo/commit/098eac59a9d4f4567acb16018453c0d389677690"><code>098eac5</code></a>
build(deps): bump golang.org/x/tools from 0.42.0 to 0.43.0</li>
<li><a
href="https://github.com/gohugoio/hugo/commit/3c980c072ee6a9c37a1c6028a7d328696f745836"><code>3c980c0</code></a>
resources: Re-publish on transformation cache hit</li>
<li><a
href="https://github.com/gohugoio/hugo/commit/404ac00001de49c0ccbff4131be40fa2651e4a06"><code>404ac00</code></a>
create/skeletons: Use css.Build in theme skeleton</li>
<li><a
href="https://github.com/gohugoio/hugo/commit/06fcb724219eecdc20367e86e1a8134d3d7e0e5b"><code>06fcb72</code></a>
tpl/css: Add a test case for rebuilds on CSS options changes</li>
<li><a
href="https://github.com/gohugoio/hugo/commit/9b5f1d491d2b7cde198dd2fd858de92e9e97700f"><code>9b5f1d4</code></a>
hugolib: Allow regular pages to cascade to self</li>
<li><a
href="https://github.com/gohugoio/hugo/commit/87f8de8c7ab10516614180080f97490645bbfdec"><code>87f8de8</code></a>
build(deps): bump gocloud.dev from 0.44.0 to 0.45.0</li>
<li><a
href="https://github.com/gohugoio/hugo/commit/67ef6c68deb031f2dcff926b0cc236a07dcca334"><code>67ef6c6</code></a>
build(deps): bump golang.org/x/sync from 0.19.0 to 0.20.0</li>
<li>Additional commits viewable in <a
href="https://github.com/gohugoio/hugo/compare/v0.157.0...v0.158.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/gohugoio/hugo&package-manager=go_modules&previous-version=0.157.0&new-version=0.158.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-24 03:59:55 +00:00
Ethan c1474c7ee2 fix(coderd/httpmw): return 500 for internal auth errors (#23352)
## Issue context
On `dev.coder.com`, users could successfully log in, briefly see the web
UI, and then get redirected back to `/login`.

We traced the most reliable repro to viewing Tracy's workspaces on the
`/workspaces` page. That page eagerly issues authenticated per-row
requests such as:
- `POST /api/v2/authcheck`
- `GET /api/v2/workspacebuilds/:workspacebuild/parameters`

One confirmed failing request was for Tracy's workspace
`nav-scroll-fix-1f6b`:
- route: `GET
/api/v2/workspacebuilds/f2104ae6-7d53-457c-a8df-de831bee76db/parameters`
- build owner/workspace: `tracy/nav-scroll-fix-1f6b`

The failing response body was:
- message: `An internal error occurred. Please try again or contact the
system administrator.`
- detail: `Internal error fetching API key by id. fetch object: pq:
password authentication failed for user "coder"`

That showed the request was not actually unauthorized. The server hit an
internal database/authentication problem while resolving the session API
key. The underlying issue was that DB password rotation had been
enabled, it has since been disabled.

However, the logout cascade happened because:
1. `APIKeyFromRequest()` returned `ok=false` for both genuine auth
failures and internal backend failures.
2. `ValidateAPIKey()` wrapped every `!ok` result as `401 Unauthorized`.
3. `RequireAuth.tsx` signs the user out on any `401` response.

So a transient backend/database failure was being misreported as an auth
failure, which made the client forcibly log the user out.

A useful extra clue was that the installed PWA did not repro. The PWA
starts on `/agents`, which avoids the `/workspaces` request fan-out.
That helped narrow the problem to the eager authenticated requests on
the workspace list rather than to cookies or the login flow itself.

## What changed
This PR now fixes the bug without changing the exported
`APIKeyFromRequest()` surface:
- `ValidateAPIKey()` now uses a new internal helper that returns a typed
`ValidateAPIKeyError`
- the exported `APIKeyFromRequest()` helper remains compatible for
existing callers like `userauth.go`
- internal API-key lookup failures are classified as `500 Internal
Server Error` plus `Hard: true`
- internal `UserRBACSubject()` failures now return `500 Internal Server
Error` instead of `401 Unauthorized`
- a focused regression test verifies that an internal `GetAPIKeyByID`
failure surfaces as `500`

This removes the brittle message-based classification and makes the
internal-auth-failure path robust for all API-key lookup failures
handled by auth middleware.
2026-03-24 12:37:17 +11:00
Danielle Maywood a8e7cc10b6 fix(site): isolate draft prompts per conversation (#23469) 2026-03-24 01:05:19 +00:00
Michael Suchacz 82f965a0ae feat: per-user per-model chat compaction threshold overrides (#23412)
## What

Adds per-user per-model auto-compaction threshold overrides. Users can
now customize the percentage of context window usage that triggers chat
compaction, independently for each enabled model.

## Why

The compaction threshold was previously only configurable at the
deployment level (`chat_model_configs.compression_threshold`). Different
users have different preferences — some want aggressive compaction to
keep costs low, others prefer higher thresholds to retain more context.
This gives users control without requiring admin intervention.

## Architecture

**Storage:** Reuses the existing `user_configs` table (no migration
needed). Overrides are stored as key/value pairs with keys shaped
`chat_compaction_threshold:<modelConfigID>` and integer percent values.

**API:** Three new experimental endpoints under
`/api/experimental/chats/config/`:
- `GET /user-compaction-thresholds` — list all overrides for the current
user
- `PUT /user-compaction-thresholds/{modelConfig}` — upsert an override
(validates model exists and is enabled, validates 0–100 range)
- `DELETE /user-compaction-thresholds/{modelConfig}` — clear an override
(idempotent)

**Runtime resolution:** In `coderd/chatd/chatd.go`, a new
`resolveUserCompactionThreshold()` helper runs at the start of each chat
turn (inside `runChat()`), after the model config is resolved but before
`CompactionOptions` is built. If a valid override exists, it replaces
`modelConfig.CompressionThreshold`. The threshold source
(`user_override` vs `model_default`) is logged with each compaction
event.

**Precedence:** `effectiveThreshold = userOverride ??
modelConfig.CompressionThreshold`

**UI:** New "Context Compaction" subsection in the Agents → Settings →
Behavior tab, placed after Personal Instructions. Shows one row per
enabled model with the system default, a number input for the override,
and Save/Reset controls.

## Testing

- 9 API subtests covering CRUD, validation (boundary values 0/100,
out-of-range rejection), upsert behavior, idempotent delete, user
isolation, and non-existent model config
- 4 dbauthz tests (16 scenarios) verifying `ActionReadPersonal` /
`ActionUpdatePersonal` on all query methods
- 4 Storybook stories with play functions (Default, WithOverrides,
Loading, Error)

<details>
<summary>Implementation plan</summary>

### Phase 1 — Tests
- Backend API tests in `coderd/chats_test.go` (9 subtests)
- Database auth wrapper tests in
`coderd/database/dbauthz/dbauthz_test.go` (4 methods)
- Frontend stories in `UserCompactionThresholdSettings.stories.tsx` (4
stories)

### Phase 2 — Backend preference surface
- 4 SQL queries in `coderd/database/queries/users.sql` (list, get,
upsert, delete)
- `make gen` to propagate into generated artifacts
- Auth/metrics wrappers in dbauthz and dbmetrics
- SDK types and client methods in `codersdk/chats.go`
- HTTP handlers and routes in `coderd/chats.go` and `coderd/coderd.go`
- Key prefix constant shared between handlers and runtime

### Phase 3 — Runtime override
- `resolveUserCompactionThreshold()` helper in `coderd/chatd/chatd.go`
- Override injection in `runChat()` before building `CompactionOptions`
- `threshold_source` field added to compaction log

### Phase 4 — Settings UI
- API client methods and React Query hooks in `site/src/api/`
- `UserCompactionThresholdSettings` component extracted from
`SettingsPageContent`
- Per-model mutation tracking (only the active row disables during save)
- 100% warning, "System default" label, helpful empty state copy

### Phase 5 — Refactor and review fixes
- Consolidated key prefix constant in `codersdk`
- Explicit PUT range validation (not just struct tags)
- GET handler gracefully skips malformed rows instead of 500
- Boundary value, upsert, and non-existent model config tests
- UX improvements: per-model mutation state, aria-live on errors

</details>
2026-03-24 00:48:18 +01:00
Kyle Carberry acbfb90c30 feat: auto-discover OAuth2 config for MCP servers via RFC 7591 DCR (#23406)
## Problem

When adding an external MCP server with `auth_type=oauth2`, admins
currently must manually provide:
- `oauth2_client_id`
- `oauth2_client_secret`
- `oauth2_auth_url`
- `oauth2_token_url`

This requires the admin to manually register an OAuth2 client with the
external MCP server's authorization server first — a friction-heavy
process that contradicts the MCP spec's vision of plug-and-play
discovery.

## Solution

When an admin creates an MCP server config with `auth_type=oauth2` and
omits the OAuth2 fields, Coder now automatically discovers and registers
credentials following the MCP authorization spec:

1. **Protected Resource Metadata (RFC 9728)** — Fetches
`/.well-known/oauth-protected-resource` from the MCP server to discover
its authorization server. Falls back to probing the server URL for a
`WWW-Authenticate` header with a `resource_metadata` parameter.

2. **Authorization Server Metadata (RFC 8414)** — Fetches
`/.well-known/oauth-authorization-server` from the discovered auth
server to find all endpoints.

3. **Dynamic Client Registration (RFC 7591)** — Registers Coder as an
OAuth2 client at the auth server's registration endpoint, obtaining a
`client_id` and `client_secret` automatically.

The discovered/generated credentials are stored in the MCP server
config, and the existing per-user OAuth2 connect flow works unchanged.

### Backward compatibility

- **Manual config still works**: If all three fields
(`oauth2_client_id`, `oauth2_auth_url`, `oauth2_token_url`) are
provided, the existing behavior is unchanged.
- **Partial config is rejected**: Providing some but not all fields
returns a clear error explaining the two options.
- **Discovery failure is clear**: If auto-discovery fails, the error
message explains what went wrong and suggests manual configuration.

## Changes

- **New package `coderd/mcpauth`** — Self-contained discovery and DCR
logic with no `codersdk` dependency
- **Modified `coderd/mcp.go`** — `createMCPServerConfig` handler now
attempts auto-discovery when OAuth2 fields are omitted
- **Tests** — Unit tests for discovery (happy path, WWW-Authenticate
fallback, no registration endpoint, registration failure) and
`parseResourceMetadataParam` helper
2026-03-23 19:26:47 -04:00
Danielle Maywood c344d7c00e fix(site): improve mobile layout for settings and analytics (#23460) 2026-03-23 22:00:23 +00:00
david-fraley 53350377b3 docs: add Agents Getting Started enablement page (#23244) 2026-03-23 16:56:46 -05:00
Mathias Fredriksson 147df5c971 refactor: replace sort.Strings with slices.Sort (#23457)
The slices package provides type-safe generic replacements for the
old typed sort convenience functions. The codebase already uses
slices.Sort in 43 call sites; this finishes the migration for the
remaining 29.

- sort.Strings(x)          -> slices.Sort(x)
- sort.Float64s(x)         -> slices.Sort(x)
- sort.StringsAreSorted(x) -> slices.IsSorted(x)
2026-03-23 23:19:23 +02:00
Cian Johnston 9e4c283370 test: share coderdtest instances in OAuth2 validation tests (#23455)
Consolidates invocations of `coderdtest.New` to a single shared instance per
parent for the following tests:

- `TestOAuth2ClientMetadataValidation`
- `TestOAuth2ClientNameValidation`
- `TestOAuth2ClientScopeValidation`
- `TestOAuth2ClientMetadataEdgeCases`

> 🤖 This PR was created with the help of Coder Agents, and was
reviewed by my human. 🧑‍💻
2026-03-23 21:03:34 +00:00
Mathias Fredriksson 145817e8d3 fix(Makefile): install playwright browsers before storybook tests (#23456)
The test-storybook target uses @vitest/browser-playwright with
Chromium but never installs the browser binaries. pnpm install
only fetches the npm package; the actual browser must be
downloaded separately via playwright install. This mirrors what
test-e2e already does.
2026-03-23 20:57:03 +00:00
Cian Johnston 956f6b2473 test: share coderdtest instances to stop paying the startup tax 22 times (#23454)
Consolidates 6 tests that spun up separate coderdtest instances per sub-test into a single shared instance per parent. 

> 🤖 This PR was created with the help of Coder Agents, and has been
reviewed by my human. 🧑‍💻
2026-03-23 19:54:43 +00:00
Kayla はな d2afda8191 feat: allow restricting sharing to service accounts (#23327) 2026-03-23 13:18:49 -06:00
Michael Suchacz c389c2bc5c fix(coderd/x/chatd): stabilize auto-promotion flake (#23448)
TestInterruptAutoPromotionIgnoresLaterUsageLimitIncrease still relied on
wall-clock polling after the acquire loop moved to a mock clock, so it
could assert before chatd finished its asynchronous cleanup and
auto-promotion work.

Wait on explicit request-start signals and on the server's in-flight
chat work before asserting the intermediate and final database state.
This keeps the test synchronized with the actual processor lifecycle
instead of scheduler timing.

Closes https://github.com/coder/internal/issues/1406
2026-03-23 19:17:58 +00:00
Kayla はな 4c9e37b659 feat: add page for editing users (#23328) 2026-03-23 12:42:50 -06:00
Cian Johnston 3b268c95d3 chore(dogfood): evict 22 freeloading tools from the Dockerfile (#23378)
Removes unused tools from dogfood Dockerfile:
- Go tools `moq`, `go-swagger`, `goreleaser`, `goveralls`, `kind`,
`helm-docs`, `gcr-cleaner-cli`
- curl-installed `cloud_sql_proxy`, `dive`, `docker-credential-gcr`, `grype`,
`kube-linter`, `stripe` CLI, `terragrunt`, `yq` v3, GoLand 2021.2 , ANTLR v4 jar
- apt packages `cmake`, `google-cloud-sdk-datastore-emulator`, `graphviz`, `packer`

> 🤖 This PR was created with the help of Coder Agents, and was reviewed by my human. 🧑‍💻
2026-03-23 18:25:58 +00:00
Mathias Fredriksson 138bc41563 fix: improve process tool descriptions to prefer foreground execution (#23395)
The tool descriptions pushed agents toward backgrounding anything over
5 seconds, including builds, tests, and installs where you actually
want to wait for the result. This led to unnecessary process_output
round-trips and missed the foreground timeout-to-reattach workflow
entirely.

Reframe background mode as the exception (persistent processes with
no natural exit) and foreground with an appropriate timeout as the
default. Replace "background process" with "tracked process" in
process_output, process_list, and process_signal since they work on
all tracked processes regardless of how they were started.
2026-03-23 17:54:30 +00:00
Cian Johnston 80a172f932 chore: move chatd and related packages to /x/ subpackage (#23445)
- Moves `coderd/chatd/`, `coderd/gitsync/`, `enterprise/coderd/chatd/`
under `x/` parent directories to signal instability
- Adds `Experimental:` glue code comments in `coderd/coderd.go`

> 🤖 This PR was created with the help of Coder Agents, and was
reviewed by my human. 🧑‍💻
2026-03-23 17:34:43 +00:00
Danielle Maywood 86d8b6daee fix(site/src/pages/AgentsPage): add collapse button to settings sidebar panel (#23438) 2026-03-23 17:22:08 +00:00
Danielle Maywood 470e6c7217 feat(site): enable intra-file virtualization in DiffViewer (#23363) 2026-03-23 16:37:55 +00:00
Danielle Maywood ed19a3a08e refactor(site): move experimental endpoints to ExperimentalApiMethods (#23449) 2026-03-23 16:29:07 +00:00
Danielle Maywood 975373704f fix(site): unify diff header styling between conversation and panel viewers (#23422) 2026-03-23 16:21:53 +00:00
Danielle Maywood 522288c9d5 fix(site): add chat input skeleton to prevent layout shift on agent detail (#23439) 2026-03-23 14:41:09 +00:00
Danielle Maywood edd13482a0 fix(site): focus chat input after submitting diff comment (#23440) 2026-03-23 14:40:10 +00:00
Cian Johnston ef14654078 chore: move chat methods to ExperimentalClient (#23441)
- Changes all 41 chat method receivers in `codersdk/chats.go` from
`*Client` to `*ExperimentalClient` to ensure that callers are aware that
these reference potentially unstable `/api/experimental` endpoints.


> 🤖 This PR was created with the help of Coder Agents, and has been
reviewed by my human. 🧑‍💻
2026-03-23 14:32:11 +00:00
Thomas Kosiewski ea37f1ff86 feat: pass session token as query param on agent chat WebSockets (#23405)
## Problem

When the Coder chat UI is embedded in a VS Code webview, the session
token is set via the Coder-Session-Token header for HTTP requests.
However, browsers cannot attach custom headers to WebSocket connections,
and VS Code Electron webview environment does not support cookies set
via Set-Cookie from iframe origins. This causes all chat WebSocket
connections to fail with authorization errors.

## Solution

Pass the session token as a coder_session_token query parameter on all
chat-related WebSocket connections. The backend already accepts this
parameter (see APITokenFromRequest in coderd/httpmw/apikey.go).

The token is only included when API.getSessionToken() returns a value,
which only happens in the embed bootstrap flow. Normal browser sessions
use cookies and are unaffected.

> Built with [Coder Agents](https://coder.com/agents)
2026-03-23 15:27:55 +01:00
Mathias Fredriksson c49170b6b3 fix(scaletest): handle ignored io.ReadAll error in bridge runner (#22850)
Surface the io.ReadAll error in the error message when an HTTP
request fails with a non-200 status, instead of silently
discarding it.
2026-03-23 15:58:14 +02:00
Danielle Maywood ee9b46fe08 fix(site/src/pages/AgentsPage): replace navigating buttons with anchor tags (#23426) 2026-03-23 12:20:56 +00:00
Mathias Fredriksson 1ad3c898a0 fix(coderd/chatd): preserve identifiers in chat title generation (#23436)
The prompt told the model to "describe the primary intent" and gave
only generic examples, so it stripped PR numbers, repo names, and
other distinguishing details. Added explicit GOOD/BAD examples to
steer away from generic titles like "Review pull request changes".
Also removed "no special characters" which prevented # and / in
identifiers.
2026-03-23 12:02:05 +00:00
Jakub Domeracki b8e09d09b0 chore: remove trivy GHA job (#23415)
Action taken In response to an ongoing incident:

https://www.aquasec.com/blog/trivy-supply-chain-attack-what-you-need-to-know/

> We've not been compromised due to a combination of pinning [GitHub
Actions by commit
SHA](https://github.com/coder/coder/blob/c8e58575e0ee44fad37b5f2ffe1ef0f220c3cf23/.github/workflows/security.yaml#L149)
coupled with a [dependabot cooldown
period](https://github.com/coder/coder/pull/21079)
2026-03-23 12:52:28 +01:00
dependabot[bot] 0900a44ff3 chore: bump github.com/fatih/color from 1.18.0 to 1.19.0 (#23431)
Bumps [github.com/fatih/color](https://github.com/fatih/color) from
1.18.0 to 1.19.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/fatih/color/releases">github.com/fatih/color's
releases</a>.</em></p>
<blockquote>
<h2>v1.19.0</h2>
<h2>What's Changed</h2>
<ul>
<li>Bump golang.org/x/sys from 0.25.0 to 0.28.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/fatih/color/pull/246">fatih/color#246</a></li>
<li>Fix for issue <a
href="https://redirect.github.com/fatih/color/issues/230">#230</a>
set/unsetwriter symmetric wrt color support detection by <a
href="https://github.com/ataypamart"><code>@​ataypamart</code></a> in <a
href="https://redirect.github.com/fatih/color/pull/243">fatih/color#243</a></li>
<li>chore: go mod cleanup by <a
href="https://github.com/sashamelentyev"><code>@​sashamelentyev</code></a>
in <a
href="https://redirect.github.com/fatih/color/pull/244">fatih/color#244</a></li>
<li>Bump golang.org/x/sys from 0.28.0 to 0.30.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/fatih/color/pull/249">fatih/color#249</a></li>
<li>Bump github.com/mattn/go-colorable from 0.1.13 to 0.1.14 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/fatih/color/pull/248">fatih/color#248</a></li>
<li>Update CI and go deps by <a
href="https://github.com/fatih"><code>@​fatih</code></a> in <a
href="https://redirect.github.com/fatih/color/pull/254">fatih/color#254</a></li>
<li>Bump golang.org/x/sys from 0.31.0 to 0.37.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/fatih/color/pull/268">fatih/color#268</a></li>
<li>fix: include escape codes in byte counts from <code>Fprint</code>,
<code>Fprintf</code> by <a
href="https://github.com/qualidafial"><code>@​qualidafial</code></a> in
<a
href="https://redirect.github.com/fatih/color/pull/282">fatih/color#282</a></li>
<li>Bump golang.org/x/sys from 0.37.0 to 0.40.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/fatih/color/pull/277">fatih/color#277</a></li>
<li>fix: add nil check for os.Stdout to prevent panic on Windows
services by <a
href="https://github.com/majiayu000"><code>@​majiayu000</code></a> in <a
href="https://redirect.github.com/fatih/color/pull/275">fatih/color#275</a></li>
<li>Bump dominikh/staticcheck-action from 1.3.1 to 1.4.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/fatih/color/pull/259">fatih/color#259</a></li>
<li>Bump actions/checkout from 4 to 6 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/fatih/color/pull/273">fatih/color#273</a></li>
<li>Optimize Color.Equals performance (O(n²) → O(n)) by <a
href="https://github.com/UnSubble"><code>@​UnSubble</code></a> in <a
href="https://redirect.github.com/fatih/color/pull/269">fatih/color#269</a></li>
<li>Bump actions/setup-go from 5 to 6 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/fatih/color/pull/266">fatih/color#266</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a
href="https://github.com/ataypamart"><code>@​ataypamart</code></a> made
their first contribution in <a
href="https://redirect.github.com/fatih/color/pull/243">fatih/color#243</a></li>
<li><a
href="https://github.com/sashamelentyev"><code>@​sashamelentyev</code></a>
made their first contribution in <a
href="https://redirect.github.com/fatih/color/pull/244">fatih/color#244</a></li>
<li><a
href="https://github.com/qualidafial"><code>@​qualidafial</code></a>
made their first contribution in <a
href="https://redirect.github.com/fatih/color/pull/282">fatih/color#282</a></li>
<li><a
href="https://github.com/majiayu000"><code>@​majiayu000</code></a> made
their first contribution in <a
href="https://redirect.github.com/fatih/color/pull/275">fatih/color#275</a></li>
<li><a href="https://github.com/UnSubble"><code>@​UnSubble</code></a>
made their first contribution in <a
href="https://redirect.github.com/fatih/color/pull/269">fatih/color#269</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/fatih/color/compare/v1.18.0...v1.19.0">https://github.com/fatih/color/compare/v1.18.0...v1.19.0</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/fatih/color/commit/ca25f6e17f118a5a259f3c2c0d395949d1103a5a"><code>ca25f6e</code></a>
Merge pull request <a
href="https://redirect.github.com/fatih/color/issues/266">#266</a> from
fatih/dependabot/github_actions/actions/setup-go-6</li>
<li><a
href="https://github.com/fatih/color/commit/120598440a16510564204450092d1e7925fad9ae"><code>1205984</code></a>
Bump actions/setup-go from 5 to 6</li>
<li><a
href="https://github.com/fatih/color/commit/5715c20323d8c79f60d4944831fcfa3b76cd5734"><code>5715c20</code></a>
Merge pull request <a
href="https://redirect.github.com/fatih/color/issues/269">#269</a> from
UnSubble/main</li>
<li><a
href="https://github.com/fatih/color/commit/2f6e2003760028129f34c4ad5c3728b904811d3c"><code>2f6e200</code></a>
Merge branch 'main' into main</li>
<li><a
href="https://github.com/fatih/color/commit/f72ec947d0c34504dfd08b0db68d89f37503fc90"><code>f72ec94</code></a>
Merge pull request <a
href="https://redirect.github.com/fatih/color/issues/273">#273</a> from
fatih/dependabot/github_actions/actions/checkout-6</li>
<li><a
href="https://github.com/fatih/color/commit/848e6330af5690fa24bb038d5330839a33f1f0e5"><code>848e633</code></a>
Merge branch 'main' into main</li>
<li><a
href="https://github.com/fatih/color/commit/4c2cd3443934693bd8892fc0f7bb5bbec8e3788a"><code>4c2cd34</code></a>
Add tests</li>
<li><a
href="https://github.com/fatih/color/commit/7f812f029c41eddd3ac7fbbdf6cc78e4b175944b"><code>7f812f0</code></a>
Bump actions/checkout from 4 to 6</li>
<li><a
href="https://github.com/fatih/color/commit/b7fc9f9557629556aff702751b5268cefcbafa15"><code>b7fc9f9</code></a>
Merge pull request <a
href="https://redirect.github.com/fatih/color/issues/259">#259</a> from
fatih/dependabot/github_actions/dominikh/staticc...</li>
<li><a
href="https://github.com/fatih/color/commit/239a88f715e8e35f40492da7a1e08f7173e78e05"><code>239a88f</code></a>
Bump dominikh/staticcheck-action from 1.3.1 to 1.4.0</li>
<li>Additional commits viewable in <a
href="https://github.com/fatih/color/compare/v1.18.0...v1.19.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/fatih/color&package-manager=go_modules&previous-version=1.18.0&new-version=1.19.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-23 11:41:47 +00:00
dependabot[bot] 4537413315 chore: bump google.golang.org/api from 0.271.0 to 0.272.0 (#23430)
Bumps
[google.golang.org/api](https://github.com/googleapis/google-api-go-client)
from 0.271.0 to 0.272.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/googleapis/google-api-go-client/releases">google.golang.org/api's
releases</a>.</em></p>
<blockquote>
<h2>v0.272.0</h2>
<h2><a
href="https://github.com/googleapis/google-api-go-client/compare/v0.271.0...v0.272.0">0.272.0</a>
(2026-03-16)</h2>
<h3>Features</h3>
<ul>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3534">#3534</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/b4d37a1279665d52b8b4672a6a91732ae8eb3cf6">b4d37a1</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3536">#3536</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/549ef3e69575edbe4fee27bc485a093dc88b90b3">549ef3e</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3537">#3537</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/6def284013185ab4ac2fa389594ee6013086d5d0">6def284</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3538">#3538</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/319b5abcbc42b77f6acc861e45365b65695e8096">319b5ab</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3539">#3539</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/73bcfcf9b2fd8def3aec1cdff10e6d4ee646af41">73bcfcf</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3541">#3541</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/6374c496fde577aa9f5b32470e45676ff4f69dde">6374c49</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/googleapis/google-api-go-client/blob/main/CHANGES.md">google.golang.org/api's
changelog</a>.</em></p>
<blockquote>
<h2><a
href="https://github.com/googleapis/google-api-go-client/compare/v0.271.0...v0.272.0">0.272.0</a>
(2026-03-16)</h2>
<h3>Features</h3>
<ul>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3534">#3534</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/b4d37a1279665d52b8b4672a6a91732ae8eb3cf6">b4d37a1</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3536">#3536</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/549ef3e69575edbe4fee27bc485a093dc88b90b3">549ef3e</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3537">#3537</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/6def284013185ab4ac2fa389594ee6013086d5d0">6def284</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3538">#3538</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/319b5abcbc42b77f6acc861e45365b65695e8096">319b5ab</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3539">#3539</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/73bcfcf9b2fd8def3aec1cdff10e6d4ee646af41">73bcfcf</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3541">#3541</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/6374c496fde577aa9f5b32470e45676ff4f69dde">6374c49</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/googleapis/google-api-go-client/commit/e7df9fe0b92461f87b6d267a600e6825d1221e75"><code>e7df9fe</code></a>
chore(main): release 0.272.0 (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3535">#3535</a>)</li>
<li><a
href="https://github.com/googleapis/google-api-go-client/commit/5d8b2662ac4cd19ac978d9f08bedb59dc41c8247"><code>5d8b266</code></a>
chore(all): update all (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3540">#3540</a>)</li>
<li><a
href="https://github.com/googleapis/google-api-go-client/commit/6374c496fde577aa9f5b32470e45676ff4f69dde"><code>6374c49</code></a>
feat(all): auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3541">#3541</a>)</li>
<li><a
href="https://github.com/googleapis/google-api-go-client/commit/73bcfcf9b2fd8def3aec1cdff10e6d4ee646af41"><code>73bcfcf</code></a>
feat(all): auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3539">#3539</a>)</li>
<li><a
href="https://github.com/googleapis/google-api-go-client/commit/319b5abcbc42b77f6acc861e45365b65695e8096"><code>319b5ab</code></a>
feat(all): auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3538">#3538</a>)</li>
<li><a
href="https://github.com/googleapis/google-api-go-client/commit/6def284013185ab4ac2fa389594ee6013086d5d0"><code>6def284</code></a>
feat(all): auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3537">#3537</a>)</li>
<li><a
href="https://github.com/googleapis/google-api-go-client/commit/549ef3e69575edbe4fee27bc485a093dc88b90b3"><code>549ef3e</code></a>
feat(all): auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3536">#3536</a>)</li>
<li><a
href="https://github.com/googleapis/google-api-go-client/commit/b4d37a1279665d52b8b4672a6a91732ae8eb3cf6"><code>b4d37a1</code></a>
feat(all): auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3534">#3534</a>)</li>
<li>See full diff in <a
href="https://github.com/googleapis/google-api-go-client/compare/v0.271.0...v0.272.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=google.golang.org/api&package-manager=go_modules&previous-version=0.271.0&new-version=0.272.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-23 11:33:26 +00:00
Cian Johnston ab86ed0df8 fix(site): stop hijacking navigation after archive-and-delete settles (#23372)
- Guard both `onSettled` callbacks in
`archiveAndDeleteMutation.mutate()` with `shouldNavigateAfterArchive()`,
which checks whether the user is still viewing the archived chat (or a
sub-agent of it) before calling `navigate("/agents")`
- Extract `shouldNavigateAfterArchive` into `agentWorkspaceUtils.ts`
with 6 unit test cases covering: direct match, different chat, no active
chat, sub-agent of archived parent, sub-agent of different parent, and
cache-cleared fallback
- Look up the active chat's `root_chat_id` from the per-chat query cache
(stable across WebSocket eviction of sub-agents) to handle the sub-agent
case

> 🤖 This PR was created with the help of Coder Agents, and has been
reviewed by my human. 🧑‍💻
2026-03-23 11:28:06 +00:00