Commit Graph
5415 Commits
Author SHA1 Message Date
Jeremy Ruppel 47adfb3f28 feat(site): display prerequisites in base template step (#26524)
Display the base template prerequisites in the Template Builder wizard.
Stacked on #26523.

The `base-parameters` wizard step now renders the prerequisites markdown
(served by the backend `prerequisites` field) below the variable
configuration fields using `MemoizedMarkdown`. The step is shown when
the base has parameters **or** prerequisites, so Docker (no parameters,
has prerequisites) now shows this step.

## Changes

- `SelectedBaseMeta` gains `hasPrerequisites` boolean
- `toSelectedBaseMeta()` populates it from `base.prerequisites`
- `base-parameters` step skip logic: show when base has parameters or
prerequisites
- `BaseTemplateParametersStep`: render full prerequisites markdown as-is
(headings intact)
- Updated all test fixtures with the new field

*Generated with the assistance of an AI coding agent. Reviewed by
@jeremyruppel.*

Relates to https://linear.app/codercom/issue/DEVEX-446
2026-06-23 11:57:28 -04:00
Ehab Younes 0aa14c7d45 feat(site): add group AI budget column (#26566)
Adds an AI budget column to the organization Groups list showing each
group's current AI spend against its configured limit, or "unlimited"
when no limit is set.

- Column and its spend request are gated behind `aibridge` visibility
  and the `ai-gateway-cost-control` experiment
- Shows loading placeholders while spend is fetched from
  `/api/v2/organizations/{org}/groups/ai/spend`
- Spend severity thresholds extracted into shared `utils/budget.ts`:
  warning at 85%, destructive at or above the limit
- Response type defined locally with a TODO to replace with the
  generated type once the backend endpoint exists

Closes AIGOV-290
2026-06-23 18:56:54 +03:00
Jeremy Ruppel 10717572ac feat: show template prerequisites in builder UI (#26523)
Surface base template prerequisites to admins before they create a
template in the Template Builder wizard.

Today, template prerequisites (Docker socket setup, Kubernetes auth, AWS
IAM policies) are only visible in the registry README after import.
Admins hit opaque provisioner errors and have to hunt for docs. This
change extracts the prerequisites from the README and serves them via
the API so the frontend can display them inline.

## How it works

Each base template README uses HTML comment markers (`<!--
prerequisites:start -->` / `<!-- prerequisites:end -->`) to delimit the
prerequisites section. At boot time, the base catalog loader reads the
README, extracts the content between markers via `strings.Index`, and
caches both the full README and the prerequisites string.

The prerequisites are served via a new `prerequisites` field on `GET
/api/v2/templatebuilder/bases`. The full README is included in the
composed template tar bundle and stored as the template version readme.

## Changes

- Add `README.md` with prerequisite markers to
`coderd/templatebuilder/bases/{docker,kubernetes,aws-linux}/`
- New `ExtractPrerequisites()` in `prerequisites.go` using literal
string matching
- `bases.go`: load README at boot, fail loudly if missing, extract
prerequisites
- `compose.go`: include README in `ComposeResult` and tar bundle
- `codersdk`: add `Prerequisites` field to `TemplateBuilderBase`
- Handler: populate prerequisites in bases response, set readme on
template version

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

- Prerequisites extraction uses `strings.Index` for exact literal marker
matching; no regex or AST parser needed since we control the markers.
- YAML frontmatter is deliberately retained in the stored README. The
frontend `TemplateDocsPage` already strips it at render time via
`front-matter`.
- The prerequisite markers are HTML comments, invisible in rendered
markdown.
- The `RejectsMissingReadme` test enforces that every base template must
include a README.
- AWS Linux prerequisites span two H2 sections (`## Prerequisites` and
`## Required permissions / policy`), which is why heading-based parsing
was rejected in favor of explicit markers.

*Generated with the assistance of an AI coding agent. Reviewed by
@jeremyruppel.*
</details>

Relates to https://linear.app/codercom/issue/DEVEX-446
2026-06-23 11:36:07 -04:00
Jeremy Ruppel c9e94c20cc feat: TemplateCustomizationsStep and compose POST (DEVEX-287) (#26433)
Part of the Template Builder wizard PR stack.

## Frontend changes

1. **TemplateCustomizationsStep**: Final wizard step with org picker,
icon picker, name/display name/description/icon fields, and redirect on
success.

2. **Refactor**: Moved queries/mutation from PageView to Page container,
extracted `renderStepContent` switch and `computeCanContinue` switch
into standalone functions.

---
> [!NOTE]
> Generated by Coder Agents on behalf of @jeremyruppel
2026-06-23 10:23:00 -04:00
Kyle Carberry 44f2a77b34 feat(site/src/pages/AgentsPage/components): show context sizes and full .mcp.json paths (#26614) 2026-06-23 07:36:05 -06:00
Jaayden Halko 77f1731d82 fix(site): stop chat mutations waiting on refetches (#26210)
closes CODAGT-571

Chat rename and archive actions previously kept user-visible loading
states active until sidebar and detail invalidations finished.

This patches accepted rename and archive state into loaded chat caches
immediately, removes archived or unarchived root chats from incompatible
sidebar filters, and leaves canonical chat list refreshes running in the
background.
2026-06-23 13:41:48 +01:00
Danielle Maywood ecfff8a7db feat: move model settings page to ai settings 2026-06-23 12:33:28 +01:00
blinkagent[bot]andblink-so[bot] 4691ef39cd feat(site): clarify why secret name is disabled when editing (#26553)
Adds helper text under the disabled `Name` field in the Edit secret
dialog so users understand why it can't be changed:

> Unique identifier (can’t be changed).

Matches the existing pattern used elsewhere (e.g. AI settings). The
secret name is the resource identifier in the API path (`PATCH
/api/v2/users/{user}/secrets/{name}`) and `UpdateUserSecretRequest` has
no `name` field, so rename isn't supported server-side.

Created on behalf of @matifali.

---------

Co-authored-by: blink-so[bot] <211532188+blink-so[bot]@users.noreply.github.com>
2026-06-23 11:45:58 +05:00
Jake Howell e88f2a3485 feat(site): add bouncy pop animation to inbox notification badge (#26057)
> 🤖 This PR was written by Coder Agents on behalf of Jake Howell.

Replaces the basic `animate-in fade-in zoom-in duration-200` on the
inbox unread badge with a bouncier pop using an overshoot easing.

The badge now pops in from `scale(0)` with `cubic-bezier(0.34, 1.36,
0.64, 1)` over 500ms, giving it an elastic overshoot that makes it feel
alive. Uses `tailwindcss-animate` utilities (`animate-in zoom-in-0
fade-in-0`) with just one arbitrary property for the easing. Conditional
rendering is preserved as before.

Also adds a `BadgeTransition` story with toggle buttons for easy visual
verification.

<img width="150" height="150" alt="preview-notification-animation"
src="https://github.com/user-attachments/assets/70331cd2-c2e2-4fd7-8f9e-edfe876e2bab"
/>
2026-06-23 04:58:59 +00:00
Kyle Carberry 688ee63f9d feat(site/src/pages/AgentsPage): group context indicator resources by directory (#26598)
## What

Groups the **Skills** and **Context files** lists in the context-usage
popover (`ContextUsageIndicator`) by their parent directory. Resources
pulled from different roots, for example a repo-root `AGENTS.md` and a
nested `site/AGENTS.md`, or same-named skills from `.coder/skills` vs
`.agents/skills`, previously collapsed to identical basenames and lost
their provenance. Each group now renders a dimmed directory header with
a folder icon, and its items are indented beneath it.

## Why

`ChatContextResource.source` carries the real path for instruction files
and skill directories, but the popover only rendered the basename, so
multiple context roots were indistinguishable.

## Notes

- The directory header shows even for a single root, so provenance is
always visible.
- MCP is intentionally left flat for now: an `mcp_server`'s `source` is
a server name (not a path) and the row carries no reference to the
`mcp_config` that declared it, so it does not fit the directory model
without a backend change.
- Adds a `getPathDirname` helper (with tests) and switches the skill
list `key` from `skill.name` to `skill.source` to avoid collisions
across roots.

## Test plan

- `ContextUsageIndicator.stories.tsx`: new `MultipleContextRoots` story
(files + skills across several directories); `Clean` story updated to
assert the single-root header. Storybook play tests pass (4/4).
- `path.test.ts`: added `getPathDirname` cases (unit pass).
- `biome check`, `tsc -p .`, and `make pre-commit` all pass.

<details>
<summary>Design decision log</summary>

- **Header gating:** started by only showing the directory header when a
section spanned more than one directory; changed to **always show**
because the provenance is useful even for a single root.
- **Presentation:** chose a grouped layout (dimmed directory header +
folder icon, items indented beneath) over an inline dimmed path prefix,
since there is typically a lot of shared structure to convey.
- **Directory labels** currently render the full path (dimmed, truncated
with a `title` tooltip). Open follow-ups: collapse `$HOME` to `~` or
show fewer path segments if the labels feel long.
- **MCP:** evaluated applying the same grouping. An `mcp_server` is
keyed by name and has no link to its `mcp_config` (confirmed in
`codersdk.ChatContextResource` and the `pinnedContextResources`
builder), so grouping servers under a config would require a backend
data change. Left flat for this PR.

</details>

---

🤖 This PR was created by Coder Agents on behalf of @kylecarbs.
2026-06-22 21:39:34 -06:00
Kyle Carberry 0b856ef637 fix(site/src/pages/AgentsPage/components/DiffViewer): dedupe diff files to prevent CodeView duplicate id crash (#26597)
Viewing a git diff on `/agents` could crash the whole diff view with
`CodeView.addItem: duplicate id
"agent/x/agentmcp/api_internal_test.go"`. When a diff body lists the
same post-image path in more than one `diff --git` section,
`parsePatchFiles` returns one `FileDiffMetadata` per section.
`DiffViewer` then maps each file to a `CodeView` item keyed by
`file.name` (the file tree is likewise keyed by path), so the repeated
path produced two items with the same id and `CodeView.addItem` threw,
tearing down the entire view.

Deduplicate the parsed files by path in `useParsedDiff`, keeping the
first occurrence, so both the `CodeView` and the file tree always
receive unique ids. `useParsedDiff` is the single source feeding both
panels, so a malformed diff now degrades gracefully and logs one warning
instead of crashing.

<details>
<summary>Root cause and verification</summary>

Confirmed against `@pierre/diffs` `parsePatchFiles` that a single patch
with two `diff --git` sections for one path yields `patchCount: 1`,
`fileCount: 2` with both entries named
`agent/x/agentmcp/api_internal_test.go`. That is the exact input that
made `CodeView.addItem` throw. Dropping (rather than merging) duplicates
matches the renderer's model, which can only show one item per id
anyway.

Verified on the branch: `pnpm test` (DiffViewer suite, including the new
`dedupeFilesByName` tests), `tsc -p .`, and the full `pnpm lint` (biome,
types, circular-deps, React Compiler, knip) all pass.

</details>

> Opened by Coder Agents on behalf of @kylecarbs.
2026-06-22 22:34:54 -05:00
Kyle Carberry cd56ab9e33 refactor: remove legacy live-read and injected-history chat context paths (#26585)
This PR makes the agent-pushed pinned snapshot
(`chat_context_resources`) the sole source of workspace context for
chats, completing the "Release 5" cleanup. It removes legacy mechanisms
now superseded by the snapshot that agents push over dRPC
(`PushContextState`) and refresh via `chat-context/refresh`.

Removed:

- **Live-read at turn time.** MCP tool discovery, skill live-body reads,
and the instruction/skill history fallback that dialed the workspace on
every turn.
- **Context injected as message history.** The
`persist_workspace_context` generation action and its decision-loop
guard.
- **The legacy write path.** `POST`/`DELETE
/api/v2/workspaceagents/me/experimental/chat-context`, the agentsdk
`AddChatContext`/`ClearChatContext` methods, and the CLI one-shot
writer.
- **The `chats.last_injected_context` column** and all of its plumbing
(migration `000529`, queries, `db2sdk`, `dbauthz`, audit table, and the
frontend `ContextUsageIndicator` fallback).

Subagent context inheritance no longer copies parent context messages;
children now hydrate the parent's pinned `chat_context_resources` on
create, which yields an identical pin for the same workspace and agent.

What stays (still served by the live agent connection, not the
snapshot): `read_skill_file` supporting-file reads, `read_skill`
supporting-file listing, and MCP tool execution.

> [!NOTE]
> Migration `000529` drops `chats.last_injected_context` and recreates
the `chats_expanded` view without it. The down migration restores both.

<details>
<summary>Decision log (D1-D5)</summary>

- **D1 (subagent inheritance):** Re-point inheritance from the legacy
message copy to a pinned hydrate. Children call
`hydrateChatContextOnCreate` instead of copying parent context messages.
- **D2 (`persist_workspace_context`):** Remove the generation action
entirely along with the decision-loop guard it existed to satisfy, since
context is never injected into history anymore.
- **D3 (legacy HTTP + CLI):** Remove the experimental `chat-context`
POST/DELETE endpoints, the agentsdk methods, and the CLI one-shot. The
dRPC push + `chat-context/refresh` replace them.
- **D4 (frontend fallback):** Remove the `last_injected_context`
fallback in `ContextUsageIndicator`; pinned `resources` are the sole
source.
- **D5 (sequencing):** Ship as a single PR rather than a stacked pair.

</details>

---
Coder Agents generated on behalf of @kylecarbs.
2026-06-22 19:26:34 -06:00
TJ fbf31f2b2d chore(site): swap AI add-on table icons (em-dash + plain check) (#26467)
Updates the icons in the **AI add-on** column for the admin tables on
`/deployment/users` and `/organizations/.../members`.

- Not consuming a seat: `XIcon` → muted em-dash
(`text-content-disabled`).
- Consuming a seat: `CircleCheckIcon` → plain `CheckIcon` from
`lucide-react` (no circle, still `text-content-success`).

Both tables share `site/src/modules/users/AISeatCell.tsx`, so the
single-file change covers `UsersTable.tsx` and
`OrganizationMembersTable.tsx`.

## Screenshot

Users admin table on a local dogfood build of this branch
(`/deployment/users`):

<!-- Drag-and-drop the screenshot here to attach it to the PR. -->

<details>
<summary>Notes</summary>

- The em-dash is rendered via the JSX entity `&mdash;` rather than a
literal `U+2014` character, so it does not trip `make lint/emdash` (see
`AGENTS.md` / `scripts/check_emdash.sh`). The browser still renders an
em-dash.
- Kept the existing `aria-label`s (`Consuming AI seat` / `Not consuming
AI seat`) and added `role="img"` on the em-dash span so screen readers
still announce the state.
- No callers other than `UsersTable.tsx` and
`OrganizationMembersTable.tsx` use `AISeatCell` (verified via search).

</details>

> Opened by Coder Agents on behalf of @tracyjohnsonux.
2026-06-22 14:54:07 -07:00
Andrew Aquino 9b6dc4ab41 fix: left-align module names in SelectionSummary (#26583)
fixes DEVEX-514, which I accidentally introduced in #26531
2026-06-22 13:15:29 -07:00
Kyle Carberry e6a9b59abe feat(site/src): surface pinned chat workspace context in the UI (#26573)
Adds the workspace-context indicator UI for agent chats, part of
breaking the "Workspace Context Sources for Coder Agents" RFC (#26466)
into small, reviewable PRs.

## What this adds

- **Pinned context popover**: the context-usage indicator now lists the
chat's pinned resources, instruction files, skills, and MCP servers with
their tools. Unusable resources (invalid skill, unreadable/oversize
file) are surfaced in an "Issues" section with their error rather than
dropped silently.
- **Drift and error states**: when the pinned context differs from the
agent's latest snapshot, the ring shows a warning marker and the popover
explains the drift. A snapshot-level error gets a distinct treatment.
- **Refresh context**: a button re-pins the chat to the agent's latest
snapshot via `PUT /api/experimental/chats/{id}/context`.
- **Live updates**: `context_dirty` watch events apply the lightweight
dirty flags across the cached chat lists and refetch the open chat so
the full pinned detail loads.

## Not included

The context **changes/diff** view (the "View changes" affordance and its
dialog) is intentionally deferred to a later split, so this PR contains
no diff rendering.

## Testing

- `pnpm lint` (types, biome, circular deps, React Compiler, knip)
- `pnpm test src/api/queries/chats.test.ts` (cache-merge unit tests,
including the new `context_dirty` cases)
- `pnpm test:storybook
src/pages/AgentsPage/components/ContextUsageIndicator.stories.tsx` (4
stories)
- `pnpm format`

<details>
<summary>Design notes</summary>

This is **Split 3** of #26466. Split sequence:

1. #26558 - prompt pin consumption (merged)
2. #26570 - `codersdk` context resource types (merged). During review
the type was renamed `ChatContextMCPTool` -> `ChatContextTool` and the
field `mcp_tools` -> `tools`; this PR consumes those merged names.
3. **This PR** - the UI.
4. CLI `coder exp chat context` source CRUD + `refresh` (next).
5. The context diff (`changes`, `ChatContextResourceChange`, the changes
dialog, and `buildContentPatch`) - last.

Key decisions:

- The backend already publishes `ChatWatchEventKindContextDirty` and
exposes the refresh endpoint (#26389). Watch/pubsub payloads stay
lightweight: they carry only the `dirty`/`dirty_since`/`error` flags and
omit `resources`. So `mergeWatchedChatSummary` merges (not replaces) the
cached context to preserve the pinned `resources` a single-chat GET
populated, and the `AgentsPage` watch handler refetches only the open
chat to pull the full pinned detail.
- The indicator prefers the chat's pinned `resources`; while they have
not loaded it falls back to the agent's `last_injected_context`,
skipping the empty context-file placeholder so it never renders a
nameless row.
- All diff/changes rendering is excluded here and lands in the final
split to keep this PR focused on the read-only pinned view and the
refresh action.

</details>

*This PR was created by Coder Agents on behalf of @kylecarbs.*
2026-06-22 11:09:39 -06:00
Kyle Carberry c0f854c289 feat: report pinned chat context resources on chat API (#26570)
Surfaces a chat's pinned workspace-context resources on the single-chat
GET and refresh responses, so clients can show *what* context the prompt
was built from, not just whether it drifted.

## What's included
- **codersdk**: `ChatContextResource` (plus `ChatContextResourceKind`
and `ChatContextResourceStatus`) and `ChatContextMCPTool`, and a new
`Chat.Context.Resources` field (metadata only, no bodies). It is
populated only on the single-chat GET/refresh response; list and watch
payloads stay nil to remain lightweight.
- **coderd/x/chatd**: `Server.ContextResources`, which builds the
metadata-only list from the chat's pinned `chat_context_resources` rows.
Non-OK resources (invalid / unreadable / oversize / excluded) are
reported with their status and error so the UI can explain why a
resource was dropped from the prompt instead of silently omitting it.
The shared protojson body decoders are extracted so the prompt and
detail paths reuse them.
- **coderd**: `getChat` and `refreshChatContext` enrich the response
with the resource list. Failures are non-fatal (the chat stays usable
without the detail).

## Scope / what's deferred
This is an incremental split from #26466. This PR reports only the
**resource inventory**. The pinned-context drift *diff* (the per-source
`changes` set and the "View changes" dialog) is intentionally deferred
to a later split; the existing `dirty` bit already signals that context
changed. MCP resources are reported for display only; they are not
injected into the prompt (a future RFC item).

<details>
<summary>Design notes</summary>

- The resource list is the chat's full pinned inventory (instruction
files, skills, and MCP configs/servers), preserving the query's `source
ASC` order. OK-but-empty instruction files, OK skills with no name, and
untracked kinds (reserved plugin/hook/subagent/command) are skipped.
- MCP tool names are reported with the agent's `"<server>__"` prefix
stripped so they read as the server exposes them.
- The detail is computed on read and attached only on the single-chat
GET and refresh responses; list and watch payloads omit it to stay
lightweight.
- `refreshChatContext` enriches its own response (mirroring `getChat`)
so the client reflects a refresh immediately, without a full reload.
</details>

<details>
<summary>Testing</summary>

- `go test ./coderd/x/chatd/ -run
'TestPinnedContextResources|TestContextResources|TestChatContextDirtyFromAgentPush'`
(unit + integration on embedded Postgres) passes. The integration test
exercises the GET and refresh enrichment end-to-end.
- `go build`, `go vet`, `golangci-lint`, and `gofmt` are clean.
- `make gen` regenerated `apidoc`, `swagger.json`,
`docs/reference/api/*`, and `typesGenerated.ts`.
</details>

---
*This PR was created by Coder Agents on behalf of @kylecarbs.*
2026-06-22 10:00:15 -06:00
Sas Swart adad5bdd49 feat: surface agent firewall correlation in AI Bridge sessions API (#26416)
Add `agent_firewall_session_id` and `agent_firewall_sequence_number`
fields to `AIBridgeThread` in the `GET
/api/v2/aibridge/sessions/{session_id}` response. These fields link each
thread to its agent firewall confinement session so the frontend can
discover the boundary session and compute sequence ranges for
interleaving firewall events within the thread timeline.

The database columns already exist on `aibridge_interceptions`
(migration 000520) and are already selected by
`ListAIBridgeSessionThreads`. This PR surfaces them through the SDK type
and the `db2sdk` conversion.

Depends on #24814

**Naming note:** The RFC uses `boundary_session_id` /
`boundary_sequence_number`, but the codebase standardized on
`agent_firewall_*` naming in the DB migration. The API fields follow the
existing convention.

</details>

> [!NOTE]
> This PR was authored by Coder Agents.
2026-06-22 15:17:37 +02:00
Sas Swart 335d6bda1b feat: add GET /api/v2/agent-firewall/sessions/{id}/logs endpoint (#24816)
Add a `GET /api/v2/agent-firewall/sessions/{id}/logs` endpoint that
returns agent firewall audit logs for a given session, sorted by
sequence number ascending.

The endpoint supports `seq_after` and `seq_before` (exclusive bounds)
and `limit` query parameters. This enables the frontend to fetch exactly
the firewall events that fall between two AI Bridge interceptions within
a thread, as described in FR 4 of the Boundary/Bridge correlation RFC.

Authorization reuses the `boundary_log` RBAC resource (owner and auditor
can read; members cannot). Returns 404 for unauthorized users to avoid
leaking existence information.

The endpoint is enterprise-only, gated behind `FeatureBoundary`
entitlement, matching the session endpoint from #24814.

Depends on #24814

> [!NOTE]
> This PR was authored by Coder Agents.
2026-06-22 13:56:29 +02:00
Ehab Younes c0b8fa9418 feat(site): add user AI budget override UI (#26402)
Add a per-user AI budget override dialog in the group members menu, scoped to the group's organization.

Refs AIGOV-295
2026-06-22 13:58:53 +03:00
Andrew Aquino 0d573587e8 feat(site): align ModuleSelection icon with top line when text wraps (#26531)
<img width="1840" height="1191" alt="image"
src="https://github.com/user-attachments/assets/3095d1a7-0f34-4a9a-9dbd-7b9cc524af84"
/>

Do you think we should also top-align the deselect button that displays
on hover?
2026-06-19 00:50:06 +00:00
Andrew Aquino 61fa2ab878 fix(site): set external auth provider polling status individually (#26313)
fixes #22420
ref DEVEX-369
ref DEVEX-269

The bug on `CreateWorkspacePage`, where clicking one external auth
provider login button disabled all providers' login buttons, was caused
by providers all sharing a single polling status (`"idle" | "polling" |
"abandoned"`) in the `useExternalAuth` hook.

## changes

- Instead of setting one status across all providers, the polling status
in `useExternalAuth` is now tracked for each provider in a record whose
keys are the providers' IDs.
- The biggest diff is a new Storybook file
CreateWorkspacePage.stories.tsx which reproduces the bug behavior from
the issue.
- Until now we've only had CreateWorkspacePageView.stories.tsx, which
isn't able to model the user interactions / API responses needed to
verify the bugfix. This file is unchanged.
- Also deletes `CreateWorkspacePage`'s `useExternalAuth` hook in favor
of the global `useExternalAuth` hook (see #26310)

(co-written with Coder Agents)
2026-06-18 11:58:38 -07:00
Sas Swart 491a75294e feat: GET /api/v2/agent-firewall/sessions/{id} (#24814)
Add a GET endpoint at `/api/v2/agent-firewall/sessions/{id}` that
returns agent firewall session metadata (`id`, `workspace_id`,
`owner_id`, `confined_process`, `started_at`). The handler authorizes
against the `boundary_log` resource with `ActionRead` via dbauthz.

The endpoint is enterprise-only, gated behind the `FeatureBoundary`
entitlement.

The `GetBoundarySessionByID` SQL query JOINs through `workspace_agents`
→ `workspace_resources` → `workspace_builds` → `workspaces` to return
`workspace_id` and `workspace_owner_id` directly, avoiding a separate
query.

Also adds an `owner_id` column to the `boundary_logs` table (migration
000526) with a FK to `users(id)` and a backfill from
`boundary_sessions`. This enables user-scoped RBAC authorization for
`InsertBoundaryLogs` via `.WithOwner()`, ensuring workspace agents can
only insert logs for their own owner.

Depends on #24810

**RBAC behaviour:**

| Role    | Result |
|---------|--------|
| Owner   | read   |
| Auditor | read   |
| Member  | 404    |

> [!NOTE]
> This PR was authored by Coder Agents.
2026-06-18 20:50:17 +02:00
Jeremy RuppelandAndrew Aquino 20ed45cac7 feat(site/src/pages/TemplateBuilder): add ModuleSettingsStep (#26428)
Implement the module settings wizard step, which renders a
`ModuleConfiguration` card per selected module with variable
configuration fields.

- Map non-sensitive variables to `ConfigurationFieldDefinition` (switch
for bool, text input for string/number)
- Show info notice with `code` tags for sensitive variables that will be
collected from developers at workspace creation
- Disable Continue button until all required non-sensitive variables
across all selected modules have values (`moduleSettingsComplete`
helper)
- Step is automatically skipped when no selected modules have
configurable variables

Relates to [DEVEX-286](https://linear.app/codercom/issue/DEVEX-286).

> [!NOTE]
> This PR was authored with Coder Agents.

---------

Co-authored-by: Andrew Aquino <dawneraq@gmail.com>
2026-06-18 13:47:08 -04:00
Andrew Aquino 023a1c992c fix(site): truncate devcontainer name with ellipsis at all screen sizes (#26359)
ref DEVEX-449

## before

In `AgentDevcontainerCard`, the devcontainer's display name truncated
with an ellipsis--but only when the viewport is <768px (parent has
`md:w-full`), which is also when the display name width gets capped at
320px (`max-w-xs`). At larger screen sizes, the display name overlaps
with the rebuild button to the right.


https://github.com/user-attachments/assets/47af30a2-f446-4b5f-bf63-24afb0532b19

## after

Now, the devcontainer display name fills up as much width as possible at
all screen sizes (not just >=768px), and it also gets truncated with an
ellipsis at all screen sizes.


https://github.com/user-attachments/assets/68579438-6fb5-40ea-93cf-c3814dfaac1a

---

(If we have a way to automate resizing screens in Storybook, lmk!)

The trick is to set a `min-width` on the text we want to truncate, see
this CSS Tricks article: https://css-tricks.com/flexbox-truncated-text/

I changed `max-w-xs` to `min-w-[284px]`. In addition to constraining the
display name's width (see "before" above), the `max-width` caused the
rebuild button to wrap to the next line if the screen is too narrow to
show the display name at that width. This behavior on small screens is
preserved. 🙂 However, since the display name's width isn't constrained
anymore, it's written out fully if possible, which causes the rebuild
button to wrap lines on wider screens if the display name is very long.
2026-06-18 09:15:33 -07:00
Atif Ali 0f4a3df2c6 fix: respect automatic_updates policy when building workspace action list (#26362) 2026-06-18 19:57:55 +05:00
Jeremy RuppelandAndrew Aquino dca8178f8d feat(site): add ModuleSelectStep (#26427)
Implement the module selection wizard step with multi-select toggle and
conflict warnings.

- Add `getTemplateBuilderModules` API client method with optional `base`
parameter for OS filtering
- Add react-query wrapper with `staleTime: Infinity` to prevent
re-fetches on step navigation
- Render a flat grid of `ModuleCard` components with checkbox-style
multi-select
- Show non-blocking conflict warnings when selected modules declare
`conflicts_with` each other
- Map selections to `TemplateBuilderComposeModule[]` and
`SelectedModuleMeta[]` for wizard state

Relates to [DEVEX-285](https://linear.app/codercom/issue/DEVEX-285).

> [!NOTE]
> This PR was authored with Coder Agents.

---------

Co-authored-by: Andrew Aquino <dawneraq@gmail.com>
2026-06-18 09:42:03 -04:00
Jeremy Ruppel 1546a0a58f feat: add BaseTemplateParametersStep (#26426)
Implement the base template parameters wizard step, which renders a
configuration form for base template variables using the existing
`TemplateConfiguration` and `ConfigurationField` components.

- Map `TemplateBuilderModuleVariable` to `ConfigurationFieldDefinition`
(switch for bool, text input for string/number)
- Read variable definitions from the cached bases query
- Disable Continue button until all required non-sensitive variables
have values (`baseParametersComplete` helper)
- Step is automatically skipped when the selected base has no parameters
(e.g. Docker)
- Update `toComposeRequest` to include `base_variable_values` in the API
payload

Relates to [DEVEX-284](https://linear.app/codercom/issue/DEVEX-284).

> [!NOTE]
> This PR was authored with Coder Agents.
2026-06-17 14:56:52 -04:00
Jeremy Ruppel 87de6dc23e feat: add base template variables to API (#26425) 2026-06-17 12:22:35 -04:00
Nick VigilanteandCoder 6c95a618b4 fix: replace stale docs() paths with redirect destinations (DOCS-253) (#25740)
## Summary

Replaces 9 stale docs paths and 2 stale doc anchors in `site/src/`, and
adds a TS/TSX audit script (`site/scripts/audit-docs-paths.mjs`) plus
unit tests that scan the codebase for paths that resolve via
`coder.com/redirects.json`.

### Redirect-target updates (9)

Each of these paths' `/docs/...` source matches a Next.js redirect rule,
so requests today produce a 302 on `coder.com`. The audit script
identifies them by cross-referencing against `redirects.json`.

- Five update product-code references to `/ai-coder/ai-bridge` (renamed
to `/ai-coder/ai-gateway` in v2.33).
- One updates a commented-out reference to
`/templates#template-filtering` in `TemplatesFilter.tsx`.
- Three update notification-template mock data in
`testHelpers/entities.ts` that pointed at the renamed
`/docs/templates/schedule`.

### Anchor-only updates (2)

These paths are still live on `coder.com` (no redirect), but their
`#fragment` no longer matches a heading on the destination page.
Fragments are evaluated client-side and never sent to the server, so the
audit script does not catch them. Found and verified manually against
the current docs.

- `AuditFilter.tsx`: `/admin/security/audit-logs#filtering-logs` →
`/admin/security/audit-logs#how-to-filter-audit-logs`. The current
heading is `## How to Filter Audit Logs` in
[`docs/admin/security/audit-logs.md`](https://github.com/coder/coder/blob/main/docs/admin/security/audit-logs.md).
- `UserAuthSettingsPageView.tsx`: drops the stale `#openid-connect`
anchor; the path itself (`/admin/users/oidc-auth`) is unchanged. The
page H1 is now `# OpenID Connect`, so the bare path lands at the same
place the anchor used to.

None are user-visible label changes; only the doc target URLs change.

## Audit script

`site/scripts/audit-docs-paths.mjs` cross-references TS/TSX docs-URL
references against `coder.com/redirects.json` and reports anything that
resolves via a redirect (which means stale source). It catches four
forms:

- `docs("/...")` / `docs('/...')` / `` docs(`/...`) ``
- `` docs(`/.../${expr}/...`) `` (literal prefix, flagged as dynamic)
- `"https://coder.com/docs/..."` and other quoted forms
- `](https://coder.com/docs/...)` and `](/docs/...)` markdown-link forms

The full audit (26 findings: 9 in `coder/coder/site/`, 17 in
`coder/coder.com/src/`) lives in [DOCS-253 on
Linear](https://linear.app/codercom/issue/DOCS-253) rather than being
committed to the repo. Re-run locally with:

```bash
node site/scripts/audit-docs-paths.mjs \
  --redirects=/path/to/coder.com/redirects.json \
  --roots=/path/to/coder/site/src,/path/to/coder.com/src
```

Default output goes to `docs/.audit/redirects-audit-YYYY-MM-DD.md`,
which is gitignored.

## Tests

`site/scripts/audit-docs-paths.test.mjs` has 59 cases covering the four
regexes, `matchRedirect` (exact, `:path*`, `:slug(.*)`, miss),
`findMatchingRedirect`, `stripQueryAndFragment`, `literalPrefix`,
`extractReferences` end-to-end with line numbers and multi-line `docs()`
calls, `buildReport` (empty input, repo grouping and sort order,
fragment annotation, unclassified section), `walk` against a real temp
filesystem (recursion, extension filter, `SKIP_DIRS` pruning,
missing/file inputs, seeded results), and `runCli` (missing-root
warning, real-but-empty root). Run with `pnpm exec vitest run
scripts/audit-docs-paths.test.mjs --project=unit` from `site/`.

## Notes

- User-visible "AI Bridge" label text is not changed here. Renaming the
product surface from "AI Bridge" to "AI Gateway" is tracked separately
by the AI Governance team in AIGOV-233.
- A vitest assertion that no literal path in `site/src/` resolves via a
redirect will land in a follow-up PR (DOCS-257), so future drift fails
fast in CI.
- A generated `DocsPath` type for the `docs()` helper is planned in
DOCS-254.
- The `/docs/templates/schedule` drift in `entities.ts` also appears in
`coderd/notifications/testdata/*.golden` test fixtures and in historical
SQL migrations under `coderd/database/migrations/`. Those are tracked
under DOCS-256 (A2: non-TS audit) and not in scope here.

## Related work

- Linear: DOCS-253 (this PR), parent DOCS-209.
- Companion redirect rule on the coder.com side: coder/coder.com#826.
- Companion fixes for the 17 coder.com findings: coder/coder.com#876
(supersedes the closed coder/coder.com#827, which was made redundant by
coder/coder.com#832).

<details>
<summary>Implementation plan (Linear DOCS-209)</summary>

| Phase | Scope | Linear | Status |
|---|---|---|---|
| D | Versioned redirect for `/docs/@v2.33.x/ai-coder/ai-bridge` in
`coder.com/redirects.json` | DOCS-255 | coder/coder.com#826 open |
| A1 | TS/TSX audit + autofix in `coder/coder/site/` | DOCS-253 | This
PR |
| A1 follow-up | Same autofix in `coder/coder.com/src/` (3 remaining
findings after coder/coder.com#832) | DOCS-281 | coder/coder.com#876
open |
| A2 | Non-TS audit in `coder/coder` (Go, comments, markdown) | DOCS-256
| Backlog |
| A3 | code-server audit | DOCS-252 | Backlog |
| B | vitest assertion against `redirects.json` | DOCS-257 | Blocked by
A1 |
| C | Generated `DocsPath` type | DOCS-254 | Blocked by B |

</details>

---

Generated by Coder Agent on behalf of @nickvigilante.

---------

Co-authored-by: Coder <coder@users.noreply.github.com>
2026-06-17 12:19:08 -04:00
Jeremy Ruppel 286bd836f4 feat(site): add BaseInfraSelectStep (#26424)
Implement the first wizard step for selecting a base infrastructure
template.

- Add `getTemplateBuilderBases` API client method and react-query
wrapper
- Render a responsive grid of `TemplateCard` components fetched from
`GET /api/v2/templatebuilder/bases`
- Map `TemplateBuilderBase` to `SelectedBaseMeta` with `hasParameters`
derived from the base variables
- Single-choice selection that dispatches `SET_BASE` to wizard state,
preserving selection on back navigation

Relates to [DEVEX-283](https://linear.app/codercom/issue/DEVEX-283).

> [!NOTE]
> This PR was authored with Coder Agents.
2026-06-17 11:51:04 -04:00
Steven Masley be25ba24b0 fix: disable custom roles as possible default org roles (#26461) 2026-06-17 09:15:21 -05:00
Danielle Maywood e6147d082a refactor(site): share chat model/provider test factories (#26408) 2026-06-17 14:35:13 +01:00
Danielle Maywood 8d725969bf chore!: remove coder agents insights page (#26457)
Removes the coder agents PR Insights page (`/agents/settings/insights`) and all of its backend support. The page had previously been hidden and was only reachable via deep link. It had previously been hidden due to the dubious value provided in the current iteration.
2026-06-17 14:02:19 +01:00
Ehab Younes 74a7ad0929 feat: add group AI budget management UI (#26375)
Add an AI budget section to the group settings page, gated by the
aibridge feature and the ai-gateway-cost-control experiment. Saves a
per-member monthly budget via the group AI budget endpoints alongside
the group patch: empty is uncapped (deletes the budget), 0 disables,
and any value >= 0 is accepted.

Closes AIGOV-294
2026-06-17 13:22:18 +03:00
Danielle Maywood cf0c9ce16b fix(site/src/pages/AgentsPage): preserve chat input typed while loading (#26415) 2026-06-17 09:58:34 +01:00
Ethan d638b1aaed chore: gate Coder Agents app and port tabs behind experiment (#26395)
The workspace-app and port preview tabs in the Coder Agents right panel
were previously gated behind a `devel` prerelease build check, which
can't be toggled in real deployments.

This replaces that check with a proper `agent-app-tabs` deployment
experiment, registered in `ExperimentsKnown`, so the feature can be
enabled via `CODER_EXPERIMENTS=agent-app-tabs` like any other
experiment. The frontend now reads
`experiments.includes("agent-app-tabs")` from the dashboard instead of
`getPrereleaseFlag(buildInfo) === "devel"`.

Depends on #26208
2026-06-17 17:29:20 +10:00
Ethan 5ee1946b67 feat: add right panel tabs for workspace apps and ports (#26208)
Adds workspace app and forwarded port tabs to the AgentsPage right panel, so app previews and port views can sit alongside terminals while working in the chat view.

- The new add-tab dropdown lists the agent's apps and a ports submenu: embeddable apps open as iframe tabs, command apps open as renamed terminal tabs running their command, and ports open in a new port preview panel served through the wildcard access URL.
- Tabs persist per chat and are validated against the current workspace state: tabs whose app disappears, stops being embeddable, or whose agent no longer exposes the port forwarding helper are hidden rather than deleted, and reappear if the workspace exposes them again.
- A shared `usePortsData` hook keeps the add-tab control, workspace pill, and existing `PortForwardButton` on the same port queries and refresh cadence.
- ~~App and port tabs are limited to `devel` builds for now; terminal tabs remain generally available. This was done in favour of adding a backend experiment for a frontend-only feature.~~ TODO: I'm just gonna switch to a backend experiment.

Closes CODAGT-346
2026-06-17 13:59:21 +10:00
Asher 744c00f45c fix: handle blank dynamic parameter values consistently (#26122)
There was inconsistency with what the form showed and what actually was
sent to the backend. I opted to make it so that explicitly blank values
are always sent rather than have blank values silently changing to the
default value.
2026-06-16 15:05:27 -08:00
McKayla はな 3e68dd304a ci: set up pixel (#26324)
IT'S FINALLY HERE
2026-06-16 14:43:54 -06:00
Jeremy Ruppel 0fc25d38ce feat(site): add wizard shell, step registry, and feature flag gate (#26423)
Add the template builder wizard route at `/templates/new/builder` with
feature flag gating, step navigation, and wizard state management.

- Read `template_builder.disabled` from deployment config and redirect
to `/templates/new` when disabled
- Five-step wizard registry with skip logic for `base-parameters` (no
params) and `module-settings` (no configurable vars)
- Reducer managing base selection, module selection, variable values,
and template customizations with state preservation across navigation
- Two-column layout with step content area, `SelectionSummary` sidebar,
and back/forward navigation
- Page/PageView separation using `Margins`, `PageHeader`, and standard
layout components

Part 1 of the Template Builder stack. Relates to
[DEVEX-282](https://linear.app/codercom/issue/DEVEX-282).

> [!NOTE]
> This PR was authored with Coder Agents.
2026-06-16 16:38:51 -04:00
Steven Masley 0e45ded0ed feat: deployment flag to auto handle changed oidc providers (#26419)
An opt-out flag exists as an escape hatch

closes https://linear.app/codercom/issue/PLAT-343/automatically-reset-user-link-for-affected-users-when-idp-provider
2026-06-16 13:26:04 -07:00
Andrew Aquino b71bc31eec feat: change StepDivider color to green when previous step is completed (#26394)
ref DEVEX-482
2026-06-16 12:00:56 -07:00
Kyle Carberry bca0ce04ca feat: integrate agent context snapshots into chats (#26389)
Makes the chat context foundation from #26385 live. That PR added the
storage columns, writer queries, and a dormant
`agentapi.ContextDirtyMarker` trigger with no production callers; this
PR wires them together end to end.

When a workspace agent pushes a context snapshot, bound chats now
hydrate to that snapshot's hash, and a later push with a different hash
flips already-pinned chats to dirty (emitting a `context_dirty` watch
event after the transaction commits). Chat creation pins the agent's
latest snapshot when one already exists. The experimental chat API
exposes this as `Chat.Context` (`*ChatContext` with `dirty`,
`dirty_since`, `error`), and a new `PUT
/api/experimental/chats/{chat}/context` endpoint re-pins the agent's
latest snapshot and clears the dirty marker.

`context_dirty_resources` stays NULL (the resource-level diff is
deferred to the UI phase) and the live per-turn context pull is
unchanged.

The end-to-end test provisions a workspace agent via the echo
provisioner, connects it over the Agent API v2.10, and exercises the
full path: an initial push hydrates a bound chat (clean), a second push
with a different hash marks it dirty, the API reports the dirty state,
and the refresh endpoint clears it.

<details>
<summary>Decision log</summary>

- **API shape — sub-struct.** Dirty state is surfaced as
`codersdk.Chat.Context *ChatContext { Dirty bool; DirtySince *time.Time;
Error string }` rather than flat fields, matching the RFC's named
`ChatContext` type and leaving room for future fields (resource diff,
sources). `db2sdk.Chat` populates it when the chat is context-tracked
(`len(ContextAggregateHash) > 0`), dirty, or carries a snapshot error,
and leaves it nil (`omitempty`) otherwise. `Dirty` mirrors
`context_dirty_since` being set.
- **Marker wiring.** The chat daemon is injected directly as the
`agentapi.ContextDirtyMarker`. It is unconditionally constructed (only
its background worker is gated), so the marker is always non-nil and the
wiring matches every other `api.chatDaemon` call site. `agentapi` still
treats a nil marker as "chatd absent", so `PushContextState` stays a
pure write path for any future caller that does not wire chatd in.
- **Refresh is atomic.** `RefreshChatContext` reads the agent's latest
snapshot and re-pins the chat in one repeatable-read transaction, so a
concurrent push cannot land between the read and the write and leave the
chat pinned to a stale hash with the dirty marker cleared.
- **Hydrate + dirty run inside the push transaction.** The fan-out
shares the push's transaction so a concurrent refresh cannot interleave
with the version gate; `context_dirty` watch events publish only after
commit. The pinned hash on dirtied chats is intentionally left unchanged
— the refresh endpoint re-pins it.
- **Dirtied chats keep their pinned hash.** Drift is advisory: a dirty
chat stays usable, and refreshing is the only path that advances the
pinned hash.
- **Test binds `chats.agent_id` directly.** In production the binding is
set lazily during a chat turn (`chatd.persistBuildAgentBinding`); the
test sets it via `dbgen` so it exercises the context flow rather than
turn resolution.

Plan: `coderd/x/chatd` context integration + E2E (sub-struct API,
create-time + push-time hydration, refresh endpoint;
`context_dirty_resources` and the per-turn pull untouched).

</details>

🤖 Generated by Coder Agents on behalf of @kylecarbs
2026-06-16 17:46:47 +00:00
Danny Kopping 0c1c4af40a fix(site/src/pages/AISettingsPage): allow Bedrock IAM-role setup (#26400)
The Bedrock create form required both `access_key` and
`access_key_secret`, blocking deployments that authenticate against AWS
through an IAM role, instance profile, or `AWS_PROFILE`. The backend
already accepts a Bedrock provider that is configured by region alone
(see `codersdk.AIProviderBedrockSettings.IsConfigured`), so the UI was
the only thing standing between the operator and a working IAM-role
provider.

The Yup schema now treats both fields as optional while keeping the
cross-validation that forces the pair to travel together. A descriptive
note under the inputs tells the operator that leaving both blank falls
back to ambient AWS credentials, and links to the [Amazon Bedrock
section](https://coder.com/docs/ai-coder/ai-gateway/providers#amazon-bedrock)
of the AI Gateway providers docs for the credential chain and IAM
permissions. The mapping into `CreateAIProviderRequest` already omits
empty credential fields, so the wire payload sends only `region`,
`model`, and `small_fast_model`, which is enough for `IsConfigured()` on
the backend.

The model and small-fast model fields are now pre-filled with the modern
Sonnet 4.5 and Haiku 4.5 IDs from `codersdk.aiGatewayBedrockModel` /
`codersdk.aiGatewayBedrockSmallFastModel`, matching the legacy
environment seed path. A second docs note under those fields points at
the [AWS Bedrock model
cards](https://docs.aws.amazon.com/bedrock/latest/userguide/model-cards.html)
page so operators can find the canonical model IDs without leaving the
form. This also addresses the AIGOV-411 ask.

Adds `providerFormValuesToCreate` coverage for the no-credential and
whitespace-only paths, plus three new `ProviderForm` stories: one that
verifies the model fields pre-fill, one that submits without static
credentials, and one that verifies a half-typed credential pair stays
blocked.

Closes
[CODAGT-626](https://linear.app/codercom/issue/CODAGT-626/bedrock-ui-requires-access-keys-for-iam-role-setup).
Partial coverage for
[AIGOV-411](https://linear.app/codercom/issue/AIGOV-411/ai-gateway-providers-improve-bedrock-model-fields-in-ui)
(model pre-fill plus docs link; combobox, model ID pattern validation,
and docs site updates remain).

> The Slack thread also flagged a separate edit-time regression: "if I
go to edit an existing provider, all the fields I set are not on the
UI." I did not see that reproduce against the masked-credential edit
story, and the issue description focuses on the create flow, so I left
it for a separate investigation rather than bundling it into this fix.

<img width="1169" height="814" alt="image"
src="https://github.com/user-attachments/assets/08741641-da86-4acc-82ac-ef758f739f58"
/>

<sub>This PR was created by a Coder Agent on behalf of
@dannykopping.</sub>
2026-06-16 16:37:45 +02:00
Sas Swart 2716e2181c feat: purge boundary logs past retention (#24815)
Add a periodic purge job for `boundary_logs` rows past their retention
threshold, following the same pattern as the existing audit log and
connection log purge jobs in `dbpurge`.

Expose a `--boundary-log-retention` deployment flag (env
`CODER_BOUNDARY_LOG_RETENTION`, YAML `retention.boundary_logs`). Default
is `0` (keep indefinitely). When set to a positive duration, `purgeTick`
deletes rows where `captured_at` is older than the threshold in batches
of 10,000, matching other log purge operations. The `boundary_logs`
label is added to the `records_purged_total` Prometheus counter.

Also removes the random-UUID fallback for `OwnerID` in
`dbgen.BoundarySession`. The previous fallback generated a UUID that
could never satisfy the `boundary_sessions_owner_id_fkey` FK constraint,
masking test setup bugs. Callers must now provide a valid user ID or
accept NULL (the legitimate "user deleted" state).
2026-06-16 14:32:54 +02:00
Susana Ferreira 12d7ad6100 feat: add ai-gateway-cost-control experiment flag (#26399)
Adds the `ai-gateway-cost-control` experiment flag to gate new cost
control endpoints and upcoming frontend UI behind an explicit opt-in.

Currently AI Gateway cost control supports the following endpoints:
- `GET/PUT/DELETE /api/v2/organizations/{org}/groups/{group}/ai/budget`
- `GET/PUT/DELETE /api/v2/users/{user}/ai/budget`

Note: the group-level endpoints were already released in v2.34.0 and
remain ungated. Only the user-level endpoints are gated behind this
experiment. Future cost control endpoints and UI should use this
experiment for gating until the feature is stable.

> Generated by Coder Agents on behalf of @ssncferreira
2026-06-16 10:33:34 +01:00
Jeremy Ruppel 00a08f35cc revert(site): revert "chore: bump vite from 8.0.10 to 8.0.16 in /site" (#26390)
Reverts coder/coder#26388

Repeat of #26034
2026-06-16 15:28:34 +10:00
Asher b5b80dbe8e fix: do not send or use stale init dynamic parameter state (#26357)
Previously, there could be a gap where the web socket has been connected
and gets the initial message but the build parameters request was still
in flight. This caused two issues:

1. Because we only send initial parameters as a response to a message,
when the message comes first and the build parameters have not resolved
yet, we end up not sending the initial parameters, meaning the form
could be stale until the next edit the user makes.
2. And if the user does make an edit, once we get that response back we
would then send the initial parameters, essentially reverting back to
the initial state since the initial params do not include the user's
edits. So the user would need a second edit to finally sync up.

To resolve both issues, we ignore the web socket's initial message until
we get the build parameters, at which point we decide whether we can use
that initial message (when there are no build params) or if we need to
continue ignoring it and send the initial parameters to get the correct
state then finally render the form.
2026-06-15 16:38:54 -08:00
Jeremy Ruppel de31c7c18e feat: add TemplateBuilderCreateTemplate SDK types and client method (#26360)
Adds `POST /api/v2/templatebuilder/compose/template`, a synchronous
endpoint that composes a template from a base and modules, validates it
via a provisioner import job, and creates the template in a single
request.

The handler composes terraform files, bundles them as a tar, inserts the
file with hash-based dedup, creates a template version with an import
job, waits up to 2 minutes for the job to complete, classifies errors
for known failure modes (network-unreachable registry, DNS failures),
then creates the template on success. Canceled and failed jobs return
appropriate error responses.

Also adds `hclwrite.Format` to composed terraform output for canonical
HCL formatting.

Closes https://linear.app/codercom/issue/DEVEX-279

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

- SDK types and client method in `codersdk/templatebuilder.go` with
validation tags matching the standard template creation path
(`template_display_name`, `lt=128`)
- `ClassifyProvisionerError` in `coderd/templatebuilder/errors.go`
detects DNS, connection refused, i/o timeout, and TLS handshake failures
and returns actionable messages
- `waitForProvisionerJob` polls with a ramp-up interval schedule (100ms,
200ms, 500ms, then 1s steady) and accepts an `onUpdate` callback for
future SSE streaming
- Audit logging for both template and template version creation
- TOCTOU name uniqueness: early check for fast feedback, DB unique
constraint catch for the race window (returns 409, not 500)
- Swagger annotations for all error responses (400, 404, 409, 504)

</details>

> 🤖 Generated by Coder Agents
2026-06-15 18:12:55 -04:00
dependabot[bot] dc79663573 chore: bump vite from 8.0.10 to 8.0.16 in /site (#26388)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite)
from 8.0.10 to 8.0.16.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/vitejs/vite/releases">vite's
releases</a>.</em></p>
<blockquote>
<h2>v8.0.16</h2>
<p>Please refer to <a
href="https://github.com/vitejs/vite/blob/v8.0.16/packages/vite/CHANGELOG.md">CHANGELOG.md</a>
for details.</p>
<h2>v8.0.15</h2>
<p>Please refer to <a
href="https://github.com/vitejs/vite/blob/v8.0.15/packages/vite/CHANGELOG.md">CHANGELOG.md</a>
for details.</p>
<h2>v8.0.14</h2>
<p>Please refer to <a
href="https://github.com/vitejs/vite/blob/v8.0.14/packages/vite/CHANGELOG.md">CHANGELOG.md</a>
for details.</p>
<h2>v8.0.13</h2>
<p>Please refer to <a
href="https://github.com/vitejs/vite/blob/v8.0.13/packages/vite/CHANGELOG.md">CHANGELOG.md</a>
for details.</p>
<h2>v8.0.12</h2>
<p>Please refer to <a
href="https://github.com/vitejs/vite/blob/v8.0.12/packages/vite/CHANGELOG.md">CHANGELOG.md</a>
for details.</p>
<h2>v8.0.11</h2>
<p>Please refer to <a
href="https://github.com/vitejs/vite/blob/v8.0.11/packages/vite/CHANGELOG.md">CHANGELOG.md</a>
for details.</p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md">vite's
changelog</a>.</em></p>
<blockquote>
<h2><!-- raw HTML omitted --><a
href="https://github.com/vitejs/vite/compare/v8.0.15...v8.0.16">8.0.16</a>
(2026-06-01)<!-- raw HTML omitted --></h2>
<h3>Bug Fixes</h3>
<ul>
<li><strong>deps:</strong> reject UNC paths for launch-editor-middleware
(<a
href="https://redirect.github.com/vitejs/vite/issues/22571">#22571</a>)
(<a
href="https://github.com/vitejs/vite/commit/50b951225bbf6151eb84a3ad5a454908ab4a76c9">50b9512</a>)</li>
<li>reject windows alternate paths (<a
href="https://redirect.github.com/vitejs/vite/issues/22572">#22572</a>)
(<a
href="https://github.com/vitejs/vite/commit/dc245c71e5007ea4d891a025e2d69ac96c736546">dc245c7</a>)</li>
</ul>
<h2><!-- raw HTML omitted --><a
href="https://github.com/vitejs/vite/compare/v8.0.14...v8.0.15">8.0.15</a>
(2026-06-01)<!-- raw HTML omitted --></h2>
<h3>Features</h3>
<ul>
<li>send 408 on request timeout (<a
href="https://redirect.github.com/vitejs/vite/issues/22476">#22476</a>)
(<a
href="https://github.com/vitejs/vite/commit/c85c9eeb9aaf41f477b48b057146887bd5620797">c85c9ee</a>)</li>
<li>update rolldown to 1.0.3 (<a
href="https://redirect.github.com/vitejs/vite/issues/22538">#22538</a>)
(<a
href="https://github.com/vitejs/vite/commit/646dbedd2870f8ec48df0321177d8aa64bbd1575">646dbed</a>)</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li>capitalize error messages and remove spurious space in parse error
(<a
href="https://redirect.github.com/vitejs/vite/issues/22488">#22488</a>)
(<a
href="https://github.com/vitejs/vite/commit/85a0eff1c82bbb7c99a0fe8e63704316578a40d3">85a0eff</a>)</li>
<li><strong>deps:</strong> update all non-major dependencies (<a
href="https://redirect.github.com/vitejs/vite/issues/22511">#22511</a>)
(<a
href="https://github.com/vitejs/vite/commit/2686d7d0b722402204d3bcc687a87adea1bcf9fa">2686d7d</a>)</li>
<li><strong>dev:</strong> fix html-proxy cache key mismatch for /@fs/
HTML paths (<a
href="https://redirect.github.com/vitejs/vite/issues/21762">#21762</a>)
(<a
href="https://github.com/vitejs/vite/commit/47c4213f134f562c41ed7c031e4788510cf7e31e">47c4213</a>)</li>
<li><strong>glob:</strong> error on relative glob in virtual module when
no files match (<a
href="https://redirect.github.com/vitejs/vite/issues/22497">#22497</a>)
(<a
href="https://github.com/vitejs/vite/commit/5c8e98f8b584ac5d42f0f9b8580c49792213b13c">5c8e98f</a>)</li>
<li><strong>optimizer:</strong> close the rolldown bundle when write()
rejects (<a
href="https://redirect.github.com/vitejs/vite/issues/22528">#22528</a>)
(<a
href="https://github.com/vitejs/vite/commit/e3cfb9deecff563550fa1b8abd27656b8b292815">e3cfb9d</a>)</li>
<li><strong>resolve:</strong> provide onWarn for viteResolvePlugin in JS
plugin containers (<a
href="https://redirect.github.com/vitejs/vite/issues/22509">#22509</a>)
(<a
href="https://github.com/vitejs/vite/commit/40985f1c09b7696e594e6c5695fbc315d2da2c83">40985f1</a>)</li>
</ul>
<h3>Miscellaneous Chores</h3>
<ul>
<li><strong>deps:</strong> update rolldown-related dependencies (<a
href="https://redirect.github.com/vitejs/vite/issues/22566">#22566</a>)
(<a
href="https://github.com/vitejs/vite/commit/3052a67d9350f4c5076ab1c222c4a21a589cbcdd">3052a67</a>)</li>
</ul>
<h3>Code Refactoring</h3>
<ul>
<li>correct logic in <code>collectAllModules</code> function (<a
href="https://redirect.github.com/vitejs/vite/issues/22562">#22562</a>)
(<a
href="https://github.com/vitejs/vite/commit/6978a9ceb942c4f5e211d52b8a1e569f8a65c80c">6978a9c</a>)</li>
</ul>
<h2><!-- raw HTML omitted --><a
href="https://github.com/vitejs/vite/compare/v8.0.13...v8.0.14">8.0.14</a>
(2026-05-21)<!-- raw HTML omitted --></h2>
<h3>Features</h3>
<ul>
<li>update rolldown to 1.0.2 (<a
href="https://redirect.github.com/vitejs/vite/issues/22484">#22484</a>)
(<a
href="https://github.com/vitejs/vite/commit/96efc88570b6a6ddf1a910f106920cbac07b3cf0">96efc88</a>)</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li><strong>deps:</strong> update all non-major dependencies (<a
href="https://redirect.github.com/vitejs/vite/issues/22471">#22471</a>)
(<a
href="https://github.com/vitejs/vite/commit/98b81632139d51820f82036e58d6fbbf122b77b3">98b8163</a>)</li>
<li><strong>dev:</strong> handle errors when sending messages to vite
server (<a
href="https://redirect.github.com/vitejs/vite/issues/22450">#22450</a>)
(<a
href="https://github.com/vitejs/vite/commit/e8e9a34dcf2540139de558a10187630884d10217">e8e9a34</a>)</li>
<li><strong>html:</strong> handle trailing slash paths in
transformIndexHtml (<a
href="https://redirect.github.com/vitejs/vite/issues/22480">#22480</a>)
(<a
href="https://github.com/vitejs/vite/commit/5d94d1bffdb2a15de9341194d89baec86ce1f693">5d94d1b</a>)</li>
<li><strong>optimizer:</strong> pass oxc jsx options to transformSync in
dependency scan (<a
href="https://redirect.github.com/vitejs/vite/issues/22342">#22342</a>)
(<a
href="https://github.com/vitejs/vite/commit/b3132dacea9c6e0cf526cd9f0f09d850f577c262">b3132da</a>)</li>
</ul>
<h3>Miscellaneous Chores</h3>
<ul>
<li><strong>deps:</strong> update rolldown-related dependencies (<a
href="https://redirect.github.com/vitejs/vite/issues/22470">#22470</a>)
(<a
href="https://github.com/vitejs/vite/commit/7cb728eb629cc677661f1bc52a044ffc0b87fc7f">7cb728e</a>)</li>
<li>remove irrelevant commits from changelog (<a
href="https://github.com/vitejs/vite/commit/2c69495f250edf01132d4a20128de19dbe836086">2c69495</a>)</li>
</ul>
<h3>Code Refactoring</h3>
<ul>
<li><strong>glob:</strong> do not rewrite import path for absolute base
(<a
href="https://redirect.github.com/vitejs/vite/issues/22310">#22310</a>)
(<a
href="https://github.com/vitejs/vite/commit/0ae2844ab6d6d1ccf78a2975b8132769fc35b302">0ae2844</a>)</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/vitejs/vite/commit/f94df87ff03b40b65e29bacdc04cc18c7bccaa4a"><code>f94df87</code></a>
release: v8.0.16</li>
<li><a
href="https://github.com/vitejs/vite/commit/dc245c71e5007ea4d891a025e2d69ac96c736546"><code>dc245c7</code></a>
fix: reject windows alternate paths (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/22572">#22572</a>)</li>
<li><a
href="https://github.com/vitejs/vite/commit/50b951225bbf6151eb84a3ad5a454908ab4a76c9"><code>50b9512</code></a>
fix(deps): reject UNC paths for launch-editor-middleware (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/22571">#22571</a>)</li>
<li><a
href="https://github.com/vitejs/vite/commit/8d1b0195fd186d0b3297d7cd17acff6c96797420"><code>8d1b019</code></a>
release: v8.0.15</li>
<li><a
href="https://github.com/vitejs/vite/commit/2686d7d0b722402204d3bcc687a87adea1bcf9fa"><code>2686d7d</code></a>
fix(deps): update all non-major dependencies (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/22511">#22511</a>)</li>
<li><a
href="https://github.com/vitejs/vite/commit/3052a67d9350f4c5076ab1c222c4a21a589cbcdd"><code>3052a67</code></a>
chore(deps): update rolldown-related dependencies (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/22566">#22566</a>)</li>
<li><a
href="https://github.com/vitejs/vite/commit/e3cfb9deecff563550fa1b8abd27656b8b292815"><code>e3cfb9d</code></a>
fix(optimizer): close the rolldown bundle when write() rejects (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/22528">#22528</a>)</li>
<li><a
href="https://github.com/vitejs/vite/commit/6978a9ceb942c4f5e211d52b8a1e569f8a65c80c"><code>6978a9c</code></a>
refactor: correct logic in <code>collectAllModules</code> function (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/22562">#22562</a>)</li>
<li><a
href="https://github.com/vitejs/vite/commit/646dbedd2870f8ec48df0321177d8aa64bbd1575"><code>646dbed</code></a>
feat: update rolldown to 1.0.3 (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/22538">#22538</a>)</li>
<li><a
href="https://github.com/vitejs/vite/commit/85a0eff1c82bbb7c99a0fe8e63704316578a40d3"><code>85a0eff</code></a>
fix: capitalize error messages and remove spurious space in parse error
(<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/22488">#22488</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/vitejs/vite/commits/v8.0.16/packages/vite">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=vite&package-manager=npm_and_yarn&previous-version=8.0.10&new-version=8.0.16)](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)
You can disable automated security fix PRs for this repo from the
[Security Alerts page](https://github.com/coder/coder/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-15 20:35:15 +00:00