> 🤖 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)
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>
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>
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>
## 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
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.
## 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"
/>
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.
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.
The provider type already lives authoritatively in ai_providers.type,
reachable on every active row through ai_provider_id, which the
chat_model_configs_ai_provider_required_when_active CHECK makes
mandatory. The stored provider string was a denormalized copy the system
kept in sync with a startup backfill and no longer needs.
Every surface now derives provider type from the linked ai_providers
row. Telemetry is the one exception: it keeps emitting provider, now
sourced from ai_providers.type via a JOIN, so the BigQuery column and the
Nexus dashboards that read it are unaffected. The experimental HTTP/SDK
response drops provider and makes ai_provider_id required, since those
endpoints return only active configs; consumers resolve provider type
from ai_provider_id and the AI providers listing.
This ships in a single release with no compatibility window: production
reads the table via SELECT *, so a pre-drop binary fails config reads the
moment the column is gone. Operators must scale to zero before upgrading,
and there is no rollback.
Closes CODAGT-599
The workspace-app and port preview tabs in the Coder Agents right panel
were gated behind the `agent-app-tabs` deployment experiment. This
removes the experiment entirely and renders the app and port tabs
unconditionally, so the add-panel dropdown, workspace-app tabs, and port
preview tabs are always available alongside terminals.
## Changes
- Remove the `ExperimentAgentAppTabs` constant, its `DisplayName()`
case, and its `ExperimentsKnown` registration in
`codersdk/deployment.go`, then regenerate
`site/src/api/typesGenerated.ts`, `coderd/apidoc/docs.go`,
`coderd/apidoc/swagger.json`, and `docs/reference/api/schemas.md`.
- Drop the frontend experiment gate in `AgentChatPageView.tsx`
(including the now-unused `useDashboard`/`experiments` usage) so
persisted app and port tabs are no longer filtered out.
- Remove the `appExperimentEnabled` prop from `RightPanelAddTabControl`
and render the add-panel dropdown unconditionally; update the stories
accordingly.
This reverses the gating introduced in #26395.
note: the diff is tiny if you hide whitespace changes
> 🤖 This PR was written by Coder Agents on behalf of Jake Howell.
<img width="628" height="353" alt="image"
src="https://github.com/user-attachments/assets/16fbf58f-282a-4629-b0de-45160117adb6"
/>
Linear:
[DES-22051](https://linear.app/codercom/issue/DES-22051/adjust-workspace-list-icon-and-agent-link-affordance)
## Problem
[#23374](https://github.com/coder/coder/pull/23374) swapped the
underlying `Tooltip` for a `Popover` under the `HelpTooltip` →
`HelpPopover` rename, turning the trigger from a hover surface into a
click surface.
On the workspaces list, `WorkspaceOutdatedTooltip` is rendered inside a
`<TableRow>` wired up by `useClickableTableRow`, whose `onClick`
navigates to the workspace page. Other interactive children in the same
row already stop propagation (checkbox, actions cell, agent badge). The
outdated tooltip didn't, so clicking the info icon bubbled up to the
row's `onClick` and navigated away before the popover could open.
## Fix
Stop click and keydown propagation on both trigger variants
(`HelpPopoverTrigger asChild` span and `HelpPopoverIconTrigger`) in
`WorkspaceOutdatedTooltip`. The popover still opens because Radix
composes its own click handler on top of the user-provided one via
`composeEventHandlers`; `stopPropagation()` does not set
`defaultPrevented`, so Radix's toggle still runs.
## Regression coverage
Added an `InsideClickableRow` story that mounts the tooltip inside a
`useClickableTableRow` row with a tracked `onRowClick`, clicks the
trigger, asserts the popover dialog opens, and asserts `onRowClick` was
not called. Verified locally that the new story fails on `main` (1 call
to `onRowClick`) and passes with this fix.
<details>
<summary>Decision log</summary>
- Fix lives on the component (rather than at the `WorkspacesTable.tsx`
call site) so every consumer is safe by default. The popover is
interactive in a way the parent shouldn't have to know about, and the
existing call site in `TaskPage.tsx` is unaffected because its parent
has no click handler.
- `onKeyDown` propagation is also stopped so the trigger keeps working
when activated via keyboard, and to satisfy
`lint/a11y/useKeyWithClickEvents` on the `<span>` trigger variant.
- Considered pushing the guard down into the `HelpPopoverIconTrigger` /
`HelpPopover` primitives so every consumer is covered — left as a
separate follow-up since today only `WorkspaceOutdatedTooltip` is
embedded in a `useClickableTableRow` row, and a blanket change would
need a broader audit.
</details>
- Module selection summary now has `rounded-sm` corners to fit the icon
- Modules without configuration now say so with a check mark
- Correct "Additional settings" label
Add vertical overflow scrolling to the TemplateBuilder wizard step
content areas and make the selection sidebar sticky.
## Changes
- **Scroll on inner content**: Each step's inner content area (below the
heading and any tabs/search) gets `max-h-[calc(100vh-Npx)]
overflow-y-auto`, keeping headings and tab bars visible while the
content scrolls.
- Base template select and module select grids: 420px offset (accounts
for navbar, page header, card padding, tab bar, and nav controls)
- Base template parameters and module settings: 340px offset
- Customizations step: no scroll, flows naturally
- **Sticky sidebar**: The selection summary sidebar uses `sticky top-0
self-start` to stay visible while scrolling.
- **Scroll reset on navigation**: `window.scrollTo(0, 0)` in
Back/Continue handlers so users start at the top of each step.
> 🤖 Generated by Coder Agents
<details><summary>Implementation plan</summary>
The approach uses `max-h` with `overflow-y-auto` on the inner content
divs of each step component, placed below the step heading and any
tabs/search controls. This keeps the heading fixed while the content
scrolls. The offset values account for the vertical space consumed by
the navbar, page header, card border/padding, and navigation controls.
The `DashboardFullPage` flex-fill approach was explored but abandoned
because the viewport-constrained card was too short to show content on
smaller screens. The standard `Margins` + `pb-12` layout with per-step
`max-h` provides a better balance.
</details>
The chat title menu used a horizontal ellipsis (meatball) and a
different set of items than the sidebar chat-row menu. Designers asked
to normalize both menus on the sidebar shape since the sidebar is the
more recent design.
The chat top bar now uses the same `EllipsisVerticalIcon` (kebab) as the
sidebar row and exposes the same items in the same order: Pin/Unpin
agent, Rename chat, Archive agent, and Archive & delete workspace.
Labels are sentence case throughout. The standalone "Generate new title"
item is removed because the rename dialog's Generate button covers the
same workflow, which is what the sidebar already exposes. The kebab now
sits inline with the title rather than off to the right, so it reads as
a title menu instead of a global action.
Both menus render from a single `ChatActionsMenuItems` component so they
cannot drift in the future. The shared component is polymorphic over
`Item` and `Separator`, which lets the sidebar drive both its kebab
(`DropdownMenu`) and its right-click context menu (`ContextMenu`) from
the same JSX.
<details>
<summary>Implementation notes</summary>
- `ChatActionsMenuItems.tsx`: new shared component. Renders the
Pin/Unpin, Rename, Archive, Archive & delete workspace, and Unarchive
items off a flat set of flags (`isArchived`, `isPinned`, `isChildChat`,
`hasWorkspace`, `isArchiving`) and zero-arg handlers, plus polymorphic
`Item`/`Separator` components.
- `ChatTopBar.tsx`: replace the inline `EllipsisIcon` + duplicated
dropdown body with the kebab trigger and a `<ChatActionsMenuItems>`
call. Move the dropdown trigger into the title area so it sits next to
the title text; switch `DropdownMenuContent` to `align="start"` so the
menu opens flush with the trigger's left edge. Replace the old
`onRegenerateTitle`/`isRegenerateTitleDisabled` props with `onPinAgent`,
`onUnpinAgent`, `onOpenRenameDialog`, `isPinned`, and `isChildChat`.
- `ChatTreeNode.tsx`: delete the inline `renderMenuItems` helper and
call `<ChatActionsMenuItems>` from both the `DropdownMenuContent` and
the `ContextMenuContent`. Pin handlers are pre-bound to `chat.id`;
`onOpenRenameDialog` is pre-bound to the chat object.
- `AgentsPageView.tsx`: lift the rename-chat dialog state up here and
expose it through `AgentsOutletContext.onOpenRenameDialog`, so the
sidebar row menu and the chat top bar open the same dialog instance.
- `ChatsSidebar.tsx`: accept optional controlled
`chatPendingRename`/`onChatPendingRenameChange` props with
internal-state fallback (controlled-or-uncontrolled pattern). Existing
stories and tests are untouched.
- `AgentChatPage.tsx`: consume `requestPinAgent`, `requestUnpinAgent`,
and `onOpenRenameDialog` from the outlet context, and pass new
`handlePinAgentAction`, `handleUnpinAgentAction`,
`handleOpenRenameDialog`, `isPinned`, and `isChildChat` props through
`AgentChatPageView` to the top bar.
- Stories: `ChatTopBar.stories.tsx` replaces the `GenerateTitle` story
with `RenameChatItem`, `PinAgentItem`, `UnpinAgentItem`, and
`ChildChatHidesPinAction`. Updated label expectations to sentence case
across stories. `AgentChatPage.stories.tsx` updated for the new "Archive
agent" label.
</details>
<details>
<summary>Decision log</summary>
- **Source of truth.** Per the designer, the sidebar menu is the
canonical list; the chat title menu is what should change.
- **One menu body.** A shared `ChatActionsMenuItems` component drives
both menus so they cannot drift. Reviewers only need to read the menu
items once.
- **Icon parity.** Both menus now use `EllipsisVerticalIcon` (kebab).
The old meatball was the only `EllipsisIcon` use in this surface.
- **Kebab position.** Moved next to the title so the kebab reads as a
per-chat action against the title, not a global top-bar action. Share
and Toggle-panel stay on the right of the bar.
- **"Generate new title" removed, not preserved as extra.** Keeping it
would have re-introduced the inconsistency the designer is asking to
remove. The rename dialog's Generate button already runs the propose
flow and lets the user accept or edit the suggestion. The
auto-regenerate code path (`requestRegenerateTitle`,
`regeneratingTitleChatIds`, the title spinner) is left in place because
removing it expands the diff and the regenerate plumbing may be re-used;
only the menu trigger is gone.
- **Single dialog instance.** Two separate `RenameChatDialog` instances
would have worked, but lifting the state to `AgentsPageView` keeps a
single source of truth and lets the top bar open the dialog the user
already knows. `ChatsSidebar` keeps internal-state fallback so existing
stories and tests do not need to thread state.
- **`isChildChat`.** Pin/Unpin is hidden for child chats to match
`ChatTreeNode`, which only renders the Pin item when `!isChildNode`.
</details>
<sub>Opened by Coder Agents on behalf of @tracyjohnsonux.</sub>
Adds a search input and provider filter dropdown above the Models table
on `/ai/settings/models`, and moves the `Default` badge to sit beside
the model name.
## Changes
- **Search**: text input matches against model display name, model
identifier, and provider label (case-insensitive).
- **Provider filter**: select dropdown listing every configured
provider, plus an `All providers` default.
- Filters apply before pagination and reset to page one when changed.
- New empty state when filters return no matches.
- Pagination footer now shows the filtered total, with `(filtered from
N)` when filters are active.
- `Default` badge moved from the Status column to inline next to the
model name.
- Stories cover the new search, provider filter, and no-match empty
state.
## Screenshots
Please see the Storybook stories under
`pages/AISettingsPage/ModelsPage/ModelsPageView` for `Default`,
`SearchByName`, `FilterByProvider`, and `NoMatchingModels`.
## Verification
- `pnpm --dir site exec biome check
src/pages/AISettingsPage/ModelsPage/`
- `pnpm --dir site exec tsc -p . --noEmit`
- `pnpm --dir site test:storybook -- --project=chromium
src/pages/AISettingsPage/ModelsPage/` (8/8 ModelsPageView stories pass;
the unrelated `MCP Tool Completed` failure under
`AgentsPage/components/ChatElements` reproduces on `main`)
---
> [!NOTE]
> Opened by Coder Agents on behalf of @tracyjohnsonux.
Hides UI, CLI and API related to AI Gateway key management +
`/api/v2/ai-gateway/serve` endpoint.
API endpoints and CLI commands are still working they are just not
visible.
Adds the current user's AI spend progress to the top navbar avatar
dropdown when /api/v2/users/me/ai/spend reports a configured spend
limit. The shared dropdown content accepts an optional profileExtra
slot so the Agents sidebar can opt in later without changing the
default sidebar UI.
Introduces a temporary site API type and a React Query helper for
AIGOV-473 that refetches each time the dropdown opens, plus shared
budget progress helpers used by both the new dropdown bar and the
existing Agents usage indicator. The shared AIBudgetUsage component
moves to site/src/components so the dropdown and group budget UI
format spend identically, including the unlimited case. The avatar
border polls the spend endpoint and is colored by severity while the
dropdown is closed.
Closes AIGOV-473
Adds an "Alternatives to create a template" section below the navigation
controls on the base infrastructure selection step of the Template
Builder wizard. The box is only shown on the first step and contains
four outline buttons linking to other template creation paths:
- **Start from scratch** -> Coder docs tutorial (external)
- **Upload an existing template** -> `/starter-templates` (internal)
- **Browse community templates** -> Registry templates (external)
- **Use template agent skill** -> Registry skills (external)
External links open in a new tab with an external-link icon.
Closes [DEVEX-564](https://linear.app/codercom/issue/DEVEX-564)
> [!NOTE]
> This PR was authored by Coder Agents on behalf of @jeremyruppel.
<details>
<summary>Implementation plan</summary>
### 1. `TemplateAlternatives.tsx` (new)
- Presentational component rendering a bordered container with label and
four `Button` components (variant `outline`, size `sm`)
- External links use `ExternalLinkIcon` from lucide-react and open in
`_blank`
- Internal link uses React Router `Link`
### 2. `TemplateBuilderPageView.tsx` (modified)
- Import and render `<TemplateAlternatives />` below navigation
controls, conditionally when `currentStep.id === "base-infra"`
### 3. `TemplateAlternatives.stories.tsx` (new)
- Default Storybook story for the component
</details>
Configuring only a GitHub Copilot provider left the Agents page stuck on
"set up a provider then add a model", even with a provider and models
configured. The catalog dropped any provider type that NormalizeProvider
did not recognize, so a Copilot-only deployment looked identical to an
empty one and never unlocked the page.
The Agents harness cannot use Copilot: it needs a per-request token only
an official Copilot client can mint, and the harness is not one. Instead
of dropping such providers, the catalog now reports them as unsupported
so the UI can explain the dead end and point elsewhere, rather than ask
for setup that already happened. The providers stay usable through the
AI Gateway proxy.
Support is derived from the provider type, not stored, so there is no
migration. codersdk.IsAgentsUnsupportedProviderType is the single source
of truth, consulted by the chatd catalog and, through the generated
AgentsUnsupportedProviderTypes list, the frontend.
The diff also carries unrelated modernization of nearby db2sdk and
chatprovider helpers (slices.SortFunc, strings.Cut, range-over-int).
Closes CODAGT-627
Refs CODAGT-256
Refs CODAGT-682
Closes#24731
This pull-request attempts to pull the workspace when it has been
deleted, as to not show a generic error on the `<WorkspaceBuildPage />`.
This secondary query for the workspace is only initiated if we get an
error from the build itself, this is to ascertain if there actually was
a workspace here previously or if we are actually 404ing.
Future improvements might be to `410 Gone` these workspaces/builds.
The templates list page "New template" button and the empty-state "View
all starter templates" button now link to `/templates/new/builder` when
the template builder is enabled in the deployment config, falling back
to `/starter-templates` when disabled.
The `TemplateBuilderPage` now checks `createTemplates` permission and
redirects to `/templates` if the user lacks access. The
`templateBuilderBases` query is also gated on `enabled` to avoid a 404
when the feature is disabled.
<details>
<summary>Implementation details</summary>
- `TemplatesPage` fetches `deploymentConfig()` (only when user has
`createTemplates` permission) and computes `templateBuilderEnabled`
using `=== false` to default safely while loading.
- `templateBuilderEnabled` is threaded through `TemplatesPageView` and
`EmptyTemplates` to conditionally set link targets.
- `TemplateBuilderPage` uses `useAuthenticated()` to check
`permissions.createTemplates` before rendering.
- Added Storybook story variants for the builder-enabled state.
</details>
> Generated by Coder Agents on behalf of @jeremyruppel
Adds an `official` prop to both `TemplateCard` and `ModuleCard` in the
TemplateBuilder. When true (the default), a `BadgeCheckIcon` from
lucide-react is rendered inline next to the card name.
- `official` defaults to `true` since all current templates/modules are
official
- Pass `official={false}` for community entries to hide the badge
- Added `Community` and `CommunitySelected` stories for both components
> Generated by Coder Agents on behalf of @jeremyruppel
<img width="678" height="262" alt="Screenshot 2026-06-29 at 9 38 44 AM"
src="https://github.com/user-attachments/assets/51d1ddad-2bae-4ec3-a1bb-e518c86fa50c"
/>
Adds a curated priority ordering for modules in the template builder
module picker. Modules in the priority list appear first in the
specified order; unlisted modules appear after in their original order.
## Changes
- Added `MODULE_PRIORITY` constant in `ModuleSelectStep.tsx` with the
preferred display order
- Added `sortByPriority()` helper to sort modules against the priority
list
- Not-yet-imported module IDs (`codex`, `antigravity`, `kasmvnc`) are
included but commented out
- Both the module grid and category tabs respect the new ordering
- Search results still use fuzzy relevance ordering
> Generated by Coder Agents on behalf of @jeremyruppel
closes DEVEX-517
alternative to #26759
This PR is mainly about the type style overrides I added to
`MemoizedMarkdown` in BaseTemplateParametersStep.tsx.
I did also shoehorn some other misc styling updates into this PR, but
they're only scoped to template builder so I'll keep it as 1 PR instead
of making a Graphite stack:
- 1b98ac374f: adjusted `StepDivider`'s
left margin which I missed during #26757
- f1eb921048: refactored
templates/modules' avatars to use an `AvatarData` component, so that the
text appears to the right of the avatar instead of below it
Groups the agent-related AI settings pages under a new **Coder Agents**
parent in the sidebar, with a continuous left rule connecting the
children and an active-segment indicator that lights up the rule where
the current sub-item sits.
The new nav order:
- AI Governance
- AI Gateway keys
- Providers
- Coder Agents
- Models
- MCP servers
- Templates
- Spend
- Instructions
- Lifecycle
All target pages already exist on main (Danielle's recent migrations of
Models, MCP servers, Templates, Instructions, Lifecycle, Spend, and
Coder Agents into AI Settings). This PR only changes the sidebar visual
structure: the children move into an indented group with a `border-l
border-l-border` rule, and the active child paints a
`border-l-content-primary` segment over that rule via `-ml-px` so the
rule and indicator share a column instead of stacking.
<details>
<summary>Design notes</summary>
Concept 1 from the earlier exploration: always-expanded with indents,
the parent is its own page. Chosen because it adds no expand/collapse
state, no "which child is the default" question, and no animation work;
the parent reuses the existing nav-item, and the children sit in a
wrapper `div` with a left rule. The site bundle ships without Tailwind's
preflight, so the wrapper and sub-item borders are paired with
`border-solid` to actually paint, matching the pattern already in
`Sidebar.tsx`.
</details>
---
_This PR was prepared by Coder Agents on behalf of @tracyjohnsonux._