Commit Graph
5539 Commits
Author SHA1 Message Date
McKayla はな e59a67d63f fix(site): use RBAC-filtered organizations for embedded metadata (#27110) 2026-07-09 17:56:09 -06:00
Danielle Maywood d66e4d794f feat: add configurable reasoning effort to Coder agents (#26974) 2026-07-09 23:35:12 +01:00
Thomas Kosiewski 3e608e791d feat(site): add claude-fable-5 and claude-mythos-5 known model defaults (#27131) 2026-07-09 20:02:59 +02:00
Andrew Aquino 8a095d3b38 feat(site/src/pages/TemplateBuilder): deselect modules using button in main content area instead of sidebar (#27113)
closes DEVEX-588

Prototyped in #27077, broken off into a separate PR to make this work
easier to track

## changes

- Reveals the previously hidden trash can icon within
`ModuleConfiguration` (main content area)
- Removes the "x" icons from `ModuleSelection` (sidebar)

## context

@tracyjohnsonux and I decided [in
Slack](https://codercom.slack.com/archives/C0AUKB54P0E/p1783456607073329?thread_ts=1783450189.570979&cid=C0AUKB54P0E)
that it would be a better UX to move the deletion action from the "x"
icons in the sidebar to the trash can icons in the main content area.
This change has the benefits of

1. making it harder to delete modules accidentally
2. removing the responsibility of deletion from the items in
`ModuleSelection`
- interacting with these items will serve only to navigate to
configuring that module (DEVEX-587, to be done in a separate PR)

<img width="1840" height="1191" alt="image"
src="https://github.com/user-attachments/assets/4571ea2f-75cf-4cd7-b626-1826eea83bdf"
/>
2026-07-09 10:48:14 -07:00
Andrew Aquino 3e85cfb2c5 fix: during workspace bulk start/stop, skip workspaces already in target state (#27108)
Previously, bulk start required every selected workspace to be stopped,
and bulk stop required every selected workspace to be running. Mixed
selections disabled both buttons entirely.

- Change the disabled checks on bulk start/stop from `every()` to
`some()` so the buttons are enabled when at least one workspace is
eligible.
- Filter workspaces by status in the mutation functions so only eligible
workspaces are sent to the API, matching the pattern used by other batch
mutations (update, favorite, unfavorite).
- Update docs to reflect the new behavior.

> [!NOTE]
> Generated by Coder Agents. [View session](https://coder.com/).

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

## Problem

When an admin selects multiple workspaces and opens the "Bulk actions"
dropdown, the **Start** menu item is disabled unless *every* selected
workspace has `latest_build.status === "stopped"`. If even one workspace
is already running (or in any other non-stopped state), the Start button
is grayed out and unusable. Same issue applies to **Stop**.

## Changes

### 1. Relax disabled condition (`WorkspacesPageView.tsx`)

Changed `every()` to `some()` for both Start and Stop dropdown items.
The buttons are now enabled when at least one selected workspace is in
the target state.

### 2. Filter in mutations (`batchActions.ts`)

Added `.filter()` before `.map()` in both `startAllMutation` and
`stopAllMutation` so only eligible workspaces hit the API. This matches
the existing pattern in `updateAllMutation`, `favoriteAllMutation`, and
`unfavoriteAllMutation`.

### 3. Update documentation (`docs/user-guides/workspace-management.md`)

Replaced "can only be applied to a set of workspaces which are all in
the same state" with "apply to eligible workspaces in the selection,
skipping workspaces that are already in the target state."

## Testing

Four new Storybook stories:

| Story | What it tests |
|-------|---------------|
| `StartIgnoresAlreadyRunningWorkspaces` | Mixed selection; only stopped
workspaces get `startWorkspace` calls |
| `StopIgnoresAlreadyStoppedWorkspaces` | Mixed selection; only running
workspaces get `stopWorkspace` calls |
| `StartDisabledWhenNoWorkspacesAreStartable` | All running; Start
button is disabled |
| `StopDisabledWhenNoWorkspacesAreStoppable` | All stopped; Stop button
is disabled |

</details>
2026-07-09 08:13:38 -07:00
Jake Howell 1eea4a7e5b fix(site): keep activity bump editable when allow_user_autostop is on (#27083)
> 🤖 This PR was written by Coder Agents on behalf of Jake Howell.

The UI guard added in #22112 disabled the `Activity bump` field and
cleared its saved value whenever the template's `Default autostop` was
0. It did not check the "Allow users to customize autostop duration for
workspaces" (`allow_user_autostop`) setting, so templates that relied on
user-defined autostop timers had their `activity_bump_ms` silently
cleared when saving in the Coder UI.

Enable the field, preserve the value on submit, and update the helper
text when either `default_ttl_ms > 0` or `allow_user_autostop` is true.

Closes
[DEVEX-438](https://linear.app/codercom/issue/DEVEX-438/allow-user-autostop-default-autostop-disabled-causes-activity-bump-to).

> **Note:** This needs to be backported to 2.34 (ESR).

<details>
<summary>Implementation notes</summary>

### Problem

[#22112](https://github.com/coder/coder/pull/22112) introduced a UI
guard that:

1. Disables the `Activity bump (hours)` field when `default_ttl_ms ===
0`.
2. Sends `activity_bump_ms: undefined` on submit under the same
condition, which the backend treats as "do not update", but combined
with the disabled state users cannot re-enter a value once cleared and
the previously stored value effectively becomes orphaned.

The guard ignored `allow_user_autostop`. When that setting is enabled,
workspaces still have a scheduled stop (whatever the user configures on
their workspace), so `activity_bump_ms` is still meaningful.

### Fix

Broaden the guard to consider both signals. The field is only disabled
and the value only discarded when **both** `default_ttl_ms === 0`
**and** `allow_user_autostop === false`.

Changes:

- `TemplateScheduleForm.tsx`
- `disabled` prop now checks `!default_ttl_ms && !allow_user_autostop`.
- Submit path preserves `activity_bump_ms` when either signal is truthy.
  - Passes `allowUserAutostop` through to the helper text.
- `TTLHelperText.tsx`
- `ActivityBumpHelperText` accepts `allowUserAutostop` and only shows
the "no scheduled stop" hint when neither signal is set. Updated copy
mentions both signals.
- Tests and stories
- Existing tests explicitly uncheck `allow_user_autostop` before
asserting the guard fires (since `MockTemplate.allow_user_autostop`
defaults to `true`).
- Added coverage: guard stays off when only `allow_user_autostop` is
enabled; toggling `allow_user_autostop` re-enables the field without
touching `default_ttl_ms`.
- Added a story that verifies `activity_bump_ms` is preserved on submit
when `allow_user_autostop` is enabled and `default_ttl_ms` is 0.

</details>
2026-07-10 00:38:10 +10:00
Danny Kopping ef0b5585d5 feat: record and expose terminal upstream interception errors (#26961)
Categorises the terminal error of a failed interception and persists it
on the interception record, then surfaces it on the AI Gateway API.

- Categorise into an enum (`bad_request`, `unauthorized`,
  `rate_limited`, `overloaded`, `server_error`, `unknown`), unwrapping
  the ResponseError envelope, the upstream Anthropic/OpenAI SDK errors,
  and key-pool exhaustion so blocking and streaming paths agree.
- Thread the type and raw message through the recorder dRPC into the
  `aibridge_interceptions` row (optional proto fields; NULL on success).
- Expose the error on the AI Gateway thread API from the root
  interception.

*This PR was produced by opencode (agent) using the `anthropic/claude-opus-4-8` model, under human direction and review.*
2026-07-09 15:36:56 +02:00
McKayla はな b5a445d3c2 fix(site): stop popover heading from overlapping provisioner tag fields (#27101) 2026-07-08 13:59:54 -06:00
Michael Suchacz d16f254714 fix(site/src/pages/AgentsPage): remove archive actions for child chats (#27063)
Child chats (sub-agent chats) no longer offer archive-state actions in
their menus. Archive state is root-only on the backend and cascades to
children (`coderd/exp_chats.go` rejects `archived` changes when
`parent_chat_id` is set), so a child's "Archive agent", "Archive &
delete workspace", and "Unarchive agent" items always failed with a 400.
All chat action menus (chat header kebab, sidebar row dropdown, sidebar
right-click context menu) render the shared `ChatActionsMenuItems`,
which already hides Pin/Unpin for child chats; this extends the same
gating to the archive and unarchive items.

Since an archived child chat then has no menu actions at all, the menu
triggers are hidden for archived child chats (`chatHasMenuActions`): the
header kebab and the sidebar row's dropdown trigger are not rendered,
and the row's right-click context menu is disabled. Archived root chats
keep their "Unarchive agent" action.

Stories: renamed the ChatTopBar child-chat story to
`ChildChatHidesPinAndArchiveActions` and extended it to assert both
archive items are hidden, plus new stories for the archived-child cases
(`ArchivedChildChatHasNoActionsMenu`,
`ArchivedChildChatRowHasNoActionsMenu`) and a sidebar child-menu story
(`ChildChatMenuHidesArchiveActions`).

Closes CODAGT-631.

> This PR was created by Mux, an AI agent working on behalf of Mike.
2026-07-08 21:31:34 +02:00
Susana Ferreira 48f07e6e13 feat: add user AI spend endpoint (#26978)
## Description

Adds the `GET /api/v2/users/{user}/ai/spend` endpoint returning the
user's current AI spend, effective budget, and period bounds.

## Changes

- Add `userAISpendStatus` handler under the same feature/experiment gate
as `/api/v2/users/{user}/ai/budget`.
- Add `codersdk.UserAIBudgetSummary` (embedded into `UserAISpendStatus`)
and a `UserAISpendStatus` client method.
- Move `LimitSource` from `coderd/aibridge/budget` to `codersdk` so the
type is shared across endpoints.

Closes https://linear.app/codercom/issue/AIGOV-472

> [!NOTE]
> Initially generated by Claude Opus 4.7, modified and reviewed by
@ssncferreira
2026-07-08 12:46:47 +01:00
Ethan 195dffc651 test: migrate WorkspacesPage tests to Storybook play stories (#26958)
Migrates the WorkspacesPage tests from vitest to Storybook play-function stories.

The old `WorkspacesPage.test.tsx` rendered the page through `renderWithAuth` and MSW, which is slow and contributes to `test-js` timeout flakes. The new stories seed the react-query cache directly and assert the same behavior in `play` functions, so they run as Storybook interaction tests instead of in the vitest `unit` project.

Coverage is preserved across all flows from the old test: rendering the empty and filled pages, deleting only the selected workspaces, the three batch-update cases (skipping up-to-date workspaces, updating a running workspace after acknowledging the restart risk, and ignoring dormant workspaces), stopping only the selected running workspaces, starting only the selected stopped workspaces, filtering workspace apps by health and visibility, and hiding the start button for an outdated stopped always-update workspace. The entire `WorkspacesPage.test.tsx` file is removed since it contained no pure-logic tests to keep.

Closes CODAGT-686
2026-07-08 12:12:34 +10:00
Ethan 9a1aab7986 test: migrate template settings tests to Storybook play stories (#26957)
Migrates the TemplateSettingsPage tests from vitest to Storybook play-function stories.

The old `TemplateSettingsPage.test.tsx` rendered the full settings layout through `renderWithTemplateSettingsLayout` and MSW, which is slow and contributes to `test-js` timeout flakes. The new stories seed the react-query cache directly and assert the same behavior in `play` functions, so they run as Storybook interaction tests instead of in the vitest `unit` project.

Coverage is preserved across four flows: a successful metadata update, the validation error surfaced in the form when the name is already taken, deprecating a template when the access_control entitlement is present, and leaving the deprecation message empty when it is not. The pure-logic description validation tests stay in `TemplateSettingsPage.test.tsx`.

Relates to CODAGT-686
2026-07-08 12:12:29 +10:00
Ethan bc0f7cf0ae test: migrate schedule page tests to Storybook play stories (#26848)
Migrates the WorkspaceSchedulePage tests from vitest to Storybook play-function stories.

The old `WorkspaceSchedulePage.test.tsx` rendered the full settings layout through `renderWithWorkspaceSettingsLayout` and MSW, which is slow and contributes to `test-js` timeout flakes. The new stories seed the react-query cache directly and assert the same behavior in `play` functions, so they run as Storybook interaction tests instead of in the vitest `unit` project.

Coverage is preserved across four flows: enabling autostop seeds the template's default TTL, changing autostop on a running workspace shows the restart dialog after a successful save, a stopped workspace skips that dialog, and changing only autostart skips it as well. The pure-logic schedule and TTL conversion tests stay in `WorkspaceSchedulePage.test.tsx`.

Relates to CODAGT-686
2026-07-08 12:12:26 +10:00
Andrew Aquino 94605fa193 refactor(site): set square elements' dimensions with size- classes (#27071)
Finds all class combinations like `w-N h-N` and replaces them with
`size-N`.

- find: `/w-(\d+) h-\1(?=\s|")/g`, or `/h-(\d+) w-\1(?=\s|")/g`
- replace with: `size-$1`

The positive lookahead `/(?=\s|")/` matches whitespace or a double quote
character after the number, to prevent false matches where the numbers
aren't the same but share the same left digits:


https://github.com/coder/coder/blob/b3766d62be9487732c7e657cf737e739cc5413da/site/src/pages/WorkspacesPage/WorkspacesTable.tsx#L124

Unfortunately we don't have a way to automatically enforce this, since
there's no Biome equivalent for `eslint-plugin-tailwindcss`'s
[`enforces-shorthand`
rule](https://github.com/francoismassart/eslint-plugin-tailwindcss/blob/HEAD/docs/rules/enforces-shorthand.md)
2026-07-07 15:50:58 -07:00
McKayla はな 283e463de1 fix(site): update linear icon (#27019) 2026-07-07 14:56:27 -06:00
Andrew Aquino b3766d62be fix(site/src/pages/TemplateBuilder): top-align BaseTemplateSelection's avatar when name wraps lines (#27039)
ref DEVEX-493 (very similar issue, so tracking it here with this PR too)

## before

<img width="507" height="472" alt="image"
src="https://github.com/user-attachments/assets/660aaf76-8ec3-4e68-98cc-a06c90966374"
/>

## after

<img width="499" height="477" alt="image"
src="https://github.com/user-attachments/assets/02903bd1-0e8c-431d-92b0-b64735ba47e9"
/>
2026-07-07 16:38:21 +00:00
Andrew Aquino e5990a9143 fix(site/src/pages/TemplateBuilder): display Avatar instead of img in BaseTemplateCard/SelectionSummary (#26951)
fixes DEVEX-576

## before

<img width="230" height="159" alt="image"
src="https://github.com/user-attachments/assets/d77b1343-f13b-472d-96ba-0b91baa8b17f"
/>

<img width="202" height="254" alt="image"
src="https://github.com/user-attachments/assets/458c5ea8-91cc-4ffe-8a49-132f186a9482"
/>

## after

<img width="232" height="161" alt="image"
src="https://github.com/user-attachments/assets/620ec315-4b95-44cc-90c0-2b24f8cb073e"
/>

<img width="205" height="252" alt="image"
src="https://github.com/user-attachments/assets/4268d12c-a37f-4dff-8c08-73b5a348d082"
/>
2026-07-07 09:21:21 -07:00
George K 6af0f4d698 feat: add workspace restart functionality to API (#25757)
This models restart as durable orchestration of existing stop and
start workspace builds instead of adding a new restart transition.
Keeping restart as two existing transitions preserves the current
build/provisioner model.

The child start build is created only after the parent stop build
succeeds, rather than being inserted immediately in a pending
state. That keeps `workspace_builds` aligned with actual
provisioner-ready work and avoids introducing a second
pending-build lifecycle that the provisioner and build acquisition
paths would need to understand.

Refs: https://linear.app/codercom/issue/PLAT-143
2026-07-07 09:18:30 -07:00
Michael Suchacz 07f4c9f550 fix(site): set spend today range to end of day (#26992)
Selecting Today in the AI spend usage date range picker sent an end date
rounded to the next hour, which made refreshed usage pages show stale
partial-day data.

This lets the shared date range picker keep its existing next-hour
default for template insights while the spend usage and drill-in pickers
request an end-of-day boundary for Today.

Closes
https://linear.app/codercom/issue/CODAGT-751/date-range-picker-uses-end-of-hour-for-today

Generated by Coder Agents.
2026-07-07 11:20:16 +02:00
Andrew Aquino 517df93367 fix(site/src/pages/TemplateBuilder): allow TemplateCard to be selected if it has no variables (#27031)
Fixes a bug where if `base.variables` is not defined, clicking a
`TemplateCard` fails (no base template gets selected) and this error
gets logged:

```
Uncaught TypeError: Cannot read properties of undefined (reading 'length')
```

<img width="1840" height="1191" alt="image"
src="https://github.com/user-attachments/assets/b51a1f05-ab58-4b17-a126-350604f9832b"
/>
2026-07-06 16:33:20 -07:00
Jeremy Ruppel 8afdf0f7e1 fix(site/src/pages/TemplateBuilder): use variable defaults as field values, not just placeholders (#27032)
Variables with defaults showed the default as placeholder text in an
empty field. If the user did not type anything, an empty value was sent
instead of the default. Now the default pre-populates the field value so
it is visible and editable.

Fixes both `BaseTemplateParametersStep` and `ModuleSettingsStep` by
falling back to `defaultPlaceholder(v.default)` instead of `""` when no
user value exists.

Fixes DEVEX-584

> [!NOTE]
> This PR was authored by Coder Agents on behalf of @jeremyruppel.
2026-07-06 19:28:55 -04:00
Michael SuchaczandMathias Fredriksson 1eb5d579b0 fix: unblock manual chat title generation for unowned chats (#26963)
## Problem

The Generate button in the chat Rename dialog (POST
`/api/experimental/chats/{chat}/title/propose`) could fail in ways
unrelated to actual concurrent title generation:

- The manual title lock returned 409 for any `pending` chat and any
`running` chat without a worker. Legacy `pending` rows are never
acquired by workers, so those chats 409'd forever. Running chats are
unowned in the normal window between message submission and worker
acquisition (indefinitely when runners are down), producing spurious
409s.
- A missing default chat model config surfaced as a generic 500, and the
dialog hid the actionable cause carried in the error detail.

## Fix

Backend (`coderd/x/chatd`, `coderd`, `coderd/database`):

- Remove the manual title lock entirely. Races between title writers are
already resolved by `recordManualTitleUsage`, which re-reads the chat
under `GetChatByIDForUpdate` and only persists the generated title when
it is unchanged since the request snapshot, so concurrent regenerates
and renames settle by last write wins. The lock only suppressed
duplicate model calls (the dialog already disables the button in flight,
and usage limits bound spend), and its synthetic `worker_id` marker was
the source of the spurious 409s. The 409 responses, the marker and
staleness handling, and the now-unused
`UpdateChatStatusPreserveUpdatedAt` query are gone.
- New `ErrNoDefaultChatModelConfig` sentinel mapped to 400 "No default
chat model config is configured." in both title endpoints, matching the
POST `/chats` precedent.

Frontend (`site`):

- The Rename dialog error alert now renders the API error detail under
the message, reading `error.response.data.detail` directly so
detail-less API errors do not show the generic developer-console hint.
- Removed the dead regenerate-title UI plumbing (`onRegenerateTitle`
outlet wiring and the `regeneratingTitleChatIds` spinner pipeline). The
Rename dialog propose flow is the only live title-generation UX; the
endpoint, codersdk methods, and the `api.ts`/`queries/chats.ts` layer
are kept for API consumers.

## Tests

- chatd internal: a strict-mock test pinning the compare-and-swap guard
(a concurrently changed title must not be clobbered by a generated one),
plus the existing persist-and-broadcast coverage without lock
transactions.
- HTTP: `PendingWithoutWorker` expects 200 for both endpoints,
`NoDefaultModelConfig` (400) subtests, a stopped-workspace propose
regression, and an `Unauthenticated` propose subtest.
- Storybook: stories asserting the API error detail renders in the
dialog alert, and that detail-less API errors and plain errors do not
leak the developer-console hint.

> Authored by Mux on Mike's behalf.

---------

Co-authored-by: Mathias Fredriksson <mafredri@gmail.com>
2026-07-06 23:09:08 +00:00
Andrew Aquino 4f98fa1e03 fix(site/src/pages/OrganizationSettingsPage): enable sticky positioning for horizontal form sections (#26949)
Followup to #26907, which re-enabled sticky positioning within
`#main-content` throughout the site. From
https://github.com/coder/coder/pull/26907#issuecomment-4860536495:

>Updating [`HorizontalSection` in OrganizationSettingsPageView.tsx]'s
sticky styles will be a little more involved, so I'll save that for a
separate PR.

## before

Note that I inflated this section's height to 1000px in DevTools to be
able to demo sticky positioning not working. This page isn't tall enough
at the moment for the lack of sticky positioning to be much of a
problem. But this will future-proof it (since `FormSection`, whose
sticky styles were fixed in #26907, was built for this purpose) and
allow us to remove some redundant components/files. No other files
imported `HorizontalContainer` or `HorizontalSection`, so
OrganizationSettingsPage/Horizontal.tsx can be safely deleted.

Also note that the **Info** section is sticky as expected, since it was
already using `FormSection`. The **Workspace Sharing** and **Delete
Organization** sections are the ones whose stickiness is fixed by this
PR


https://github.com/user-attachments/assets/9ffed486-87f7-4762-a97a-4268d0328829

## after


https://github.com/user-attachments/assets/581cb33e-50b5-466e-9be9-2518dc22bfa6
2026-07-06 15:16:51 -07:00
Danielle Maywood d51762440b feat: add custom AI provider icons and instance-based model picker grouping (#27026) 2026-07-06 23:00:09 +01:00
McKayla はな feef3602a3 fix(site): create workspace form width (#27014) 2026-07-06 13:47:03 -06:00
Andrew Aquino 95fde35194 feat(site): add OSC 52 clipboard support to web terminal (#26437)
Registers an OSC 52 handler on the xterm.js terminal parser so that
programs like tmux can copy text to the browser's system clipboard via
escape sequences (e.g. `printf "\\033]52;c;$(echo -n 'Coder is Cool' |
base64)\\a"`).

The handler decodes the base64 payload and writes it using the existing
`copyToClipboard` utility, which provides an HTTP fallback for insecure
contexts. Clipboard read queries (`?`) are ignored since responding
would require writing back to the PTY.

No new dependencies are needed; xterm.js 5.5.0 already exposes
`terminal.parser.registerOscHandler`.

Closes https://github.com/coder/coder/issues/16577

Generated by Coder Agents on behalf of @aqandrew.

## relevant context from agent chat, summarized by me

ref DEVEX-465

Installing
[@xterm/addon-clipboard](https://npmx.dev/package/@xterm/addon-clipboard)
would have been another way to implement this feature. This would follow
established addon-loading patterns in `WorkspaceTerminal`:


https://github.com/coder/coder/blob/612b6d4e95ac4eb1c4227dfd5979a04c2de6e600/site/src/modules/terminal/WorkspaceTerminal.tsx#L232-L241

However, registering a custom handler instead has a few advantages re:
security and flexibility:

- security
- **ignoring OSC 52 with clipboard read queries** (`payload === "?"`)
prevents processes in the workspace from reading the user's clipboard
without consent
- flexibility
- **ignoring OSC 52 with invalid base64** -- If the addon catches an
error while decoding base64, an empty string will be copied to the
clipboard, i.e., the clipboard contents get cleared, which would be
surprising/frustrating to users. The custom handler doesn't write
anything to the clipboard in this case.
- **ignoring OSC 52 with missing separator** -- The addon returns true
in this case (marks the OSC 52 as handled); the custom handler returns
false (doesn't mark the OSC 52 as handled)

The approach/tests seem sound to me. I would just ask that someone among
reviewers manually test this with tmux, since I'm not a tmux user 🙏🏽
2026-07-06 12:27:43 -07:00
Michael Suchacz 6e0bbb5fff fix(site): hide slash skills menu when user has no personal skills (#26953) 2026-07-06 20:35:35 +02:00
Ehab Younes f444c6f585 fix(site): flush deferred Radix focus-scope timer before jsdom teardown (#26983)
Radix's FocusScope defers its unmount event dispatch and focus restore
with setTimeout(0). The cleanup() in the shared afterEach schedules that
timer, and for the last test in a file it could still be pending when
vitest tears down the jsdom environment. The callback then constructs a
CustomEvent from Node's built-in constructor instead of jsdom's, and
jsdom rejects the dispatch with "parameter 1 is not of type 'Event'" as
an unhandled error, failing the run.

Await one timer turn in afterAll, while the jsdom environment is still
alive, to deterministically drain the 0ms timers scheduled by cleanup().
Timers with the same delay run in FIFO order, so this is not a race.

Fixes coder/internal#1613
2026-07-06 12:14:44 +03:00
Jaayden HalkoandTracy Johnson 14d17abae6 chore: remove frontend related regenerate chat title code (#26867)
Co-authored-by: Tracy Johnson <tracy@coder.com>
2026-07-06 06:24:08 +01:00
Jake Howell 39da38b189 fix(site/e2e): accept 404 from external auth reset hook (#26793)
> 🤖 This PR was written by Coder Agents on behalf of Jake Howell.

Stack:
1. #26575 `fix(site/e2e): close mock external-auth servers in teardown`
2. #26793 `fix(site/e2e): accept 404 from external auth reset hook` ←
this PR
3. #26795 `fix(site/src): refresh provider state after device-flow
exchange`
4. #26798 `fix(site/e2e): reset both providers in external auth hook`
5. #26648 `chore(site/e2e): re-enable externalAuth suite`

`deleteExternalAuthByID` used to be inverted: `sql.ErrNoRows` (link
doesn't exist for this user/provider) fell through to the `500` path,
while non-`ErrNoRows` DB errors went to `httpapi.ResourceNotFound`.
#19775 (Sep 2025) refactored it to return `404` for not-found and `500`
for real DB errors, which is the contract you'd expect.

The relevant lines from #19775 in `coderd/externalauth.go`:

```diff
-	err := api.Database.DeleteExternalAuthLink(ctx, ...)
+	link, err := api.Database.GetExternalAuthLink(ctx, ...)
 	if err != nil {
-		if !errors.Is(err, sql.ErrNoRows) {
+		if errors.Is(err, sql.ErrNoRows) {
 			httpapi.ResourceNotFound(w)
 			return
 		}
 		httpapi.Write(ctx, w, http.StatusInternalServerError, ...)
 		return
 	}
```

`resetExternalAuthKey` in `site/e2e/hooks.ts` still treats `500` as the
not-found code, so the first `beforeEach` in the externalAuth suite
throws. The suite was skipped at the time #19775 landed (#17235), so
nobody noticed the contract drift until #26648 tried to re-enable it.

This just flips the accepted status codes to `200 || 404` and rewrites
the stale comment. The 401/403/500 paths still surface as failures,
which is what we want.

Refs https://linear.app/codercom/issue/DEVEX-413
Refs https://github.com/coder/coder/pull/19775

<details>
<summary>Why a separate PR</summary>

Keeps the bisection signal clean: #26575 proves the EADDRINUSE flake is
fixed, this PR fixes the hook contract drift surfaced by re-enabling the
suite, and #26648 just flips `.skip`. Squashing into #26648 would
conflate two unrelated fixes.

The CI run on #26648 already confirms the flake fix is doing its job:
`successful external auth from workspace` passes (5.6s) and the
`beforeAll`/`afterAll` mock servers come up and tear down cleanly with
no EADDRINUSE. The only failures are this 404 hook drift.

</details>
2026-07-06 03:56:50 +00:00
Itay Dafna d7ad85f7f6 feat: support multiple OIDC redirect URIs (#25408)
This PR adds a new opt-in setting, `CODER_OIDC_REDIRECT_ALLOWED_HOSTS`,
that lets a single Coder deployment complete OIDC login on more than one
hostname. When the allowlist is non-empty, Coder picks the OIDC
`redirect_uri` based on the incoming request's Host header (validated
against the list) instead of always using the static URL derived from
`CODER_ACCESS_URL`. When unset, the (default) behavior is identical to
today.

The motivation is that a single Coder deployment is frequently reachable
via multiple hostnames - for example, an internal hostname for users on
a corporate VPN and a different hostname routed through a zero-trust
gateway for users off-VPN - but OIDC login today only works on whichever
single hostname `CODER_ACCESS_URL` points to, because the `redirect_uri`
sent to the IdP is fixed at server startup. Users who reach the
deployment on any other valid hostname can see the login page but fail
the OIDC callback, since the IdP redirects them back to a hostname they
can't reach (or whose cookies they don't have).
2026-07-05 06:36:33 +02:00
Jake Howell b1ef07c79d feat(site): add linear.svg icon (#26967)
> 🤖 This PR was written by Coder Agents on behalf of Jake Howell.

Adds a bundled `linear.svg` icon at `site/static/icon/linear.svg` so
template authors can reference Linear as a first-party `/icon/`.

`site/src/theme/icons.json` is regenerated by `make
site/src/theme/icons.json` — the diff is a single-line insertion between
`lakefs.svg` and `lxc.svg`.

## Verification

- `go test ./scripts/gensite -run TestSVGIconAttributes/linear.svg` —
PASS (`width="256"`, `height="256"`, `viewBox="0 0 256 256"`)
- `make lint/site-icons` — PASS
- `go test ./scripts/gensite -count=1` — PASS (full SVG attribute sweep)
2026-07-02 15:41:27 +00:00
Yevhenii ShcherbinaandJake Howell dee41c34e6 feat: show Bedrock external ID in the provider edit form (#26919)
Surfaces the server-generated STS external ID on the Bedrock provider
edit form. When a provider assumes a role, the form shows the external
ID read-only with a copy icon and a short note to add it to the target
role's trust policy as an sts:ExternalId condition.

The value is display-only: it is passed to the form as its own prop
rather than as an editable form value, so it is never submitted back.
This matches the backend contract, where the external ID is server-owned
and a changed value is rejected.

Builds on the backend in #26869. Follow-up to #26578.

---------

Co-authored-by: Jake Howell <jake@hwll.me>
2026-07-02 10:55:50 -04:00
Paweł Banaszewski 79a74fc817 Revert "chore: hide AI Gateway key management UI/CLI/API (#26879)" (#26913)
Reverting
https://github.com/coder/coder/commit/377c1309b7a42ead9cfbd864f0af8e9b6e472851
since release 2.35 was already cut:
https://github.com/coder/coder/tree/release/2.35
2026-07-02 13:45:05 +00:00
Danielle Maywood 332e32d42b fix(site/src): keep short mobile dropdowns above the software keyboard (#26965) 2026-07-02 13:37:13 +00:00
Danielle Maywood 8bcc24f032 fix(site/src/pages/AgentsPage/components/ChatElements): keep model picker visible above mobile keyboard (#26964) 2026-07-02 12:47:01 +01:00
Atif AliandJake Howell e2e1d99485 chore(site): add Omnigent icon to static assets (#26828)
Adds the Omnigent SVG icon to the built-in site icon assets so modules
and templates can reference `/icon/omnigent.svg`.

## Validation

- `./scripts/check_site_icons.sh`
- Verified `site/src/theme/icons.json` is sorted and includes
`omnigent.svg`
- Parsed `site/static/icon/omnigent.svg` as valid XML

> 🤖 This PR was created with the help of Coder Agents, and needs a human
review. 🧑💻

---------

Co-authored-by: Jake Howell <jake@hwll.me>
2026-07-02 12:47:55 +05:00
Andrew AquinoandJeremy Ruppel 2ab3d1010f fix(site): enable sticky positioning inside #main-content (#26907)
ref DEVEX-567

Fixes a bug I noticed where #26881 claims to have made the template
builder's `SelectionSummary` sidebar sticky-positioned, but the sidebar
position wasn't actually sticky in practice:

## template builder's `SelectionSummary` (top right)

### before


https://github.com/user-attachments/assets/1e9360b5-4b0e-45b4-a859-f8edab51add7

### after


https://github.com/user-attachments/assets/3cee75e9-9813-401e-9922-d2563185f8f5

---

Why I removed `overflow-y-auto` from `#main-content`:

>`position: sticky` resolves against the nearest ancestor scroll
container — any ancestor whose overflow is not visible. `#main-content`
had `overflow-y-auto`, so it _was_ that container for the sidebar. But
because the layout wrapper is `min-h-screen`, `<main>` grows to fit its
content and never actually scrolls — the window scrolls. So the sidebar
was pinning relative to a container that never moves → no effect.

That was necessary for the main goal of this PR, which was to fix sticky
positioning for the template builder's `SelectionSummary` sidebar.
However, removing that style from `#main-content` affected 2 other
sticky-positioned elements within the site:

>Two other pages have sticky elements that, like our sidebar, were
resolving against the non-scrolling `<main>` and were therefore
effectively inert:
>- CreateWorkspacePageView.tsx:397 — `sticky top-5` side panel
>- modules/templates/TemplateFiles/TemplateFiles.tsx:64 — `sticky top-8`
file tree

^Like `SelectionSummary`, these 2 elements hadn't been behaving with
sticky positioning as expected; they would just scroll away past the top
of the screen.

For all 3 of these sticky elements, since their `top` is now relative to
the window instead of `#main-content`, they have to be positioned
farther downward so that they don't get covered by the navbar.

## `TemplateFiles`' `TemplateFileTree` (top left)

### before


https://github.com/user-attachments/assets/ee79ea36-1a47-4b1b-86ef-c65b493ea455

### after


https://github.com/user-attachments/assets/a1b0a949-b5e3-4434-acaa-ab24e82b1669

## `CreateWorkspacePageView`'s "Go back" button (top left)

### before


https://github.com/user-attachments/assets/00b076f6-8e55-4a96-8b92-d33d92c85334

### after


https://github.com/user-attachments/assets/10ae05e1-3112-4dfc-84b4-2c44fee23e06

co-authored with Claude Code

---------

Co-authored-by: Jeremy Ruppel <jeremyruppel@users.noreply.github.com>
2026-07-01 15:41:18 -07:00
Andrew Aquino 60254c85e9 feat(site): clarify module listing's empty state copy if selected base template has no modules (#26947)
ref DEVEX-578

One base template I'm aware of where you can test this state is AWS EC2
(Windows):

<img width="1840" height="1191" alt="image"
src="https://github.com/user-attachments/assets/ffd22fd2-a811-424d-a840-be6114b38560"
/>

The previous copy is still shown if you choose a different base and
search modules for a term with no matches:

<img width="1840" height="1191" alt="image"
src="https://github.com/user-attachments/assets/3db95c6a-42e6-48c1-8800-e482680cb109"
/>
2026-07-01 15:16:09 -07:00
Jeremy Ruppel 2318b0e60d feat(site/src/pages/TemplateBuilder): disable create button when no provisioners (#26938)
## Summary

Add provisioner awareness to the Template Builder wizard: disable the
Create Template button when the selected organization has no
provisioners, and reset the customizations step when navigating back.

## Changes

- Query provisioner daemons for the selected org in
`TemplateCustomizationsStep` and show a warning alert when none are
found
- Track `hasProvisioners` in wizard state via `SET_HAS_PROVISIONERS`
action; `computeCanContinue` disables the Create Template button when
`hasProvisioners === false`
- Add `RESET_CUSTOMIZATIONS` action to clear customization fields (name,
displayName, description, icon, organizationId, hasProvisioners) when
navigating back from the customizations step
- Clear the create mutation error on back navigation via
`onClearCreateError` callback

Follows up on #26935 which added the provisioner warning alert to the
Template Builder.

> 🤖 Generated by Coder Agents on behalf of @jeremyruppel
2026-07-01 17:30:09 -04:00
Andrew Aquino 04d523b9ab fix(site): set minimum width for TemplateCustomizationsStep (#26939)
fixes DEVEX-577

Now the template customizations step gets a horizontal scrollbar if the
window is too narrow to nicely display its 2 columns of inputs:


https://github.com/user-attachments/assets/888f723c-8d54-4e37-b53a-e31b6b486e88
2026-07-01 14:18:29 -07:00
Danielle Maywood 0c006a40f3 feat(site): add searchable agent model picker (#26927) 2026-07-01 21:15:35 +00:00
Yevhenii Shcherbina db7f4438b4 feat: generate STS external ID for Bedrock role assumption (#26869)
Implements:
https://linear.app/codercom/issue/AIGOV-495/add-externalid-to-prevent-confused-deputy-problem

When a Bedrock provider assumes an IAM role via STS, the gateway now
generates a unique external ID for it and sends that value on every
`AssumeRole` call. The external ID guards against the [confused deputy
problem](https://docs.aws.amazon.com/IAM/latest/UserGuide/confused-deputy.html)
on cross-account role assumption. Per [AWS's
recommendation](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-user_externalid.html),
the gateway generates and owns the value rather than accepting one from
the operator; that ownership is what makes it effective, since a party
who knows another's external ID can't induce the gateway to send it.

The external ID is server-owned and read-only over the API. It is
generated once, when a provider first has a `role_arn`, and is stable
thereafter. Clients cannot set it: create rejects any supplied
`external_id`, and update rejects a value that differs from the stored
one. An update may echo the stored value back unchanged, so the normal
read-modify-write flow (GET the provider, change a field, PATCH the full
settings object) keeps working. The value is not a secret and is
returned on GET so operators can copy it into the target role's trust
policy as an `sts:ExternalId` condition.

It is persisted in the existing JSON settings blob, so there is no
migration or audit-table change.
2026-07-01 20:44:15 +00:00
Jeremy Ruppel 1101a0f974 feat(site/src/pages/TemplateBuilder): add provisioner warning to Template Builder form (#26935)
## Summary

Show a warning in the Template Builder customizations step when the
selected organization has no provisioner daemons connected. This matches
the existing behavior in the current `CreateTemplateForm`.

## Changes

- Added a `provisionerDaemons` query in `TemplateCustomizationsStep`,
gated on the selected org
- Renders an `Alert` warning with a link to provisioner docs when no
daemons are found
- Warning is informational only and does not block form submission

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

The current `CreateTemplateForm` (used for upload/starter/duplicate
flows) shows a warning when the selected organization has no provisioner
daemons connected. The new Template Builder wizard
(`/templates/new/builder`) had an org picker in
`TemplateCustomizationsStep` but did not perform this check.

### What was done

1. Imported `provisionerDaemons` query helper, `Alert`, `Link`, and
`docs` utility into `TemplateCustomizationsStep.tsx`
2. Added a `useQuery` call for provisioner daemons, enabled only when an
org is selected
3. Computed `showProvisionerWarning` (true when the provisioners list is
empty)
4. Added a local `ProvisionerWarning` component rendering the same
warning text and docs link as `CreateTemplateForm`
5. Rendered the warning below the `OrganizationAutocomplete` picker

### Design decisions

- Warning is informational only (does not block submission), matching
the existing form behavior and the TODO rationale: a user may connect a
provisioner without refreshing
- Used the project's `Link` component (`#/components/Link/Link`) instead
of MUI Link
- Kept `ProvisionerWarning` as a local component rather than extracting
to shared, since the component is small (~8 lines)

</details>

> 🤖 Generated by Coder Agents on behalf of @jeremyruppel

<img width="2574" height="1274" alt="Screenshot 2026-07-01 at 3 56
58 PM"
src="https://github.com/user-attachments/assets/45f0d81b-1f9a-4a93-a2d3-5f037e82d1d0"
/>
2026-07-01 16:27:24 -04:00
Cian Johnston 4936ff9808 refactor: deprecate AIGatewayRoutingEnabled, remove direct chat routing (#26862)
This PR removes the now-dead direct-routing code:

- Deletes the direct routing implementation.
- Collapses the resolvedModelRoute discriminated union into aiGatewayModelRoute.
- Removes the dead providerKeys cascade.
- Deletes the preferredShortTextCandidates quickgen function.
- Simplifies the advisor override error handling.
- Deprecates the AIGatewayRoutingEnabled deployment option. It is now a no-op so as to not break existing deployments on upgrade.

Once direct routing was gone, the AI Gateway became mandatory for chat, which surfaced gaps in how the product behaves with the gateway disabled:

- Exposes ai-gateway-enabled to the frontend via embedded page metadata.
- Disables the chat composer via the existing AgentSetupNotice when the gateway is disabled, for both new and existing chats.
- Fixes nil/typed-nil chatDaemon panics on startup and shutdown when gateway is disabled.
- Fixes chat WebSocket from retrying the still-gated stream endpoint forever when the gateway is disabled.
2026-07-01 20:15:03 +01:00
McKayla はな 3e0875d236 fix(site): redirect to new organization after create (#26890) 2026-07-01 13:01:35 -06:00
Danielle Maywood 52fa49a10f fix(site): update providers docs link (#26916) 2026-07-01 18:34:12 +01:00
Andrew Aquino 88329db59c fix(site): position ModuleCard's checkmark relative to checkbox (#26925)
fixes DEVEX-571

## before


https://github.com/user-attachments/assets/e0e2fff2-4297-433f-9c99-e423c6e9e307

## after


https://github.com/user-attachments/assets/15f92377-910e-4c95-8621-c37532aec5ca
2026-07-01 17:13:18 +00:00
Jeremy Ruppel 1f04d27144 fix: link to /templates/new from Template Builder options (#26924)
- "Upload an existing template" now points to `/templates/new`
- Removes a bit of top margin for the additional actions box

<img width="1099" height="168" alt="Screenshot 2026-07-01 at 1 02 57 PM"
src="https://github.com/user-attachments/assets/12d4683e-2ee8-45e3-b94f-e04a3a583c75"
/>
2026-07-01 13:12:34 -04:00
Cian Johnston 679eb00ec4 fix: surface workspace delete failures from AgentsPage archive flow (#26900)
Right-clicking **Archive & delete workspace** on an agent chat could
leave the workspace behind without the user noticing. The archive step
ran first and removed the chat from the sidebar, so when the delete
enqueue failed the user lost the surface to retry and the workspace
lingered.

## Fix

- Delete the workspace first, archive the chat second. If the delete
enqueue fails for anything other than 404/410, the chat stays in the
list so the user can retry. 404/410 are still treated as "already gone"
so the archive proceeds.
- Errors are wrapped in `ArchiveAndDeleteError` tagged with `step:
"delete" | "archive"`. The toast branches on the tag: `delete` failures
show an actionable "Open workspace" link, `archive` failures explain the
delete already ran so no manual deletion is needed.
- Both mutation call sites navigate away on `onSuccess` only (previously
`onSettled`), so a delete failure keeps the chat's retry surface
reachable. The confirm dialog still closes on `onSettled`.
- When the archive step fails after a successful delete,
workspace-related caches are invalidated to keep the rest of the app in
sync with the ongoing deletion.
- On successful enqueue, warn when the build response's
`matched_provisioners.count` is 0. That field is populated on `POST
/workspacebuilds`; `job.queue_position` / `job.queue_size` are not.

> 🤖 This PR was generated by Coder Agents on behalf of @johnstcn.
2026-07-01 15:33:52 +01:00