## Summary
- Updates Coder's pinned `github.com/coder/fantasy` fork to include
coder/fantasy#39.
- Exposes Anthropic `thinking_display` as a typed chat model provider
option with `summarized` and `omitted` values.
- Validates configured `thinking_display` values and maps them to
`fantasyanthropic.ProviderOptions.ThinkingDisplay`.
- Regenerates the API/UI option schemas so the admin model config form
gets a generated select field.
## Tests
- `go mod tidy`
- `make gen`
- `go test ./codersdk ./coderd/x/chatd/chatprovider ./coderd -run
'TestChatModelProviderOptions|TestAnthropicThinkingDisplayFromChat|TestProviderOptionsFromChatModelConfig_AnthropicThinkingDisplay|TestMergeMissingProviderOptions_AnthropicThinkingDisplay|TestValidateChatModelProviderOptions_AnthropicThinkingDisplay'`
- `go test ./coderd/x/chatd/... ./codersdk`
- `go test ./coderd -run
'TestValidateChatModelProviderOptions_AnthropicThinkingDisplay'`
- `pnpm --dir site exec -- biome lint --error-on-warnings
src/api/chatModelOptionsGenerated.json src/api/typesGenerated.ts`
- pre-commit hook, including fmt, lint, and slim build
> Mux working on behalf of Mike.
ref DEVEX-268
branched from #24799
^Modifies that PR to include a Storybook test to verify correct behavior
when deleting a template that's attached to a workspace
---------
Co-authored-by: Carolina Urrea <73137943+canourrea23@users.noreply.github.com>
> 🤖 This PR was modified by Coder Agents on behalf of Jake Howell.
The agent logs tab was snapping to **Startup Script** whenever
`getAgentHealthIssues` returned _any_ issue, including non-script issues
like:
- agent `status` of `connecting`, `timeout`, or `disconnected`
- lifecycle `shutting_down`, `shutdown_error`, or `shutdown_timeout`
- any script with status `timed_out`, `exit_failure`, or
`pipes_left_open`
So users routinely landed on a filtered Startup Script view (even
mid-connect) and had to click back to **All Logs** to see the full
picture.
### Change
- Default to **All Logs** on mount.
- Once logs stream in, if any script has actually failed _and_ its
source has rendered log entries, auto-select that script's tab. The
visibility check is the same one the tab list uses, so we never point
`selectedLogTab` at a tab that isn't rendered.
- A ref ensures the auto-select fires at most once and never overrides a
manual tab change by the user.
- The failure predicate (`exit_code` truthy or `status` set and not
`"ok"`) is extracted into an `isScriptFailed` helper so the auto-select
and the per-tab error indicator stay aligned.
### Behavior matrix
| Agent state | Before | After |
| ------------------------------------------------------ |
--------------- | ------------------------------ |
| Healthy, scripts running normally | All Logs | All Logs |
| `connecting` / `timeout` / `disconnected` | Startup Script | All Logs
|
| `shutting_down` / `shutdown_error` | Startup Script | All Logs |
| Startup Script failed, has logs | Startup Script | Startup Script |
| Non-startup script failed, has logs (e.g. install) | Startup Script |
The failed script's tab |
| Failed script with **no logs**, other sources have logs| Startup
Script (broken: tab not rendered) | All Logs |
### Test coverage
Four `play` functions on `AgentRow.stories.tsx`, each pinned to a
hardcoded tab name so a predicate regression can't silently pass:
- **`StartError`** — failed script with logs is auto-selected.
- **`StartErrorWithoutFailedSourceLogs`** — failed script with no logs
keeps All Logs active; we never point at an invisible tab.
- **`ConnectingWithStartupLogs`** — connecting agent with no script
failure stays on All Logs (locks in the bug fix for connection-only
issues).
- **`NonStartupScriptError`** — only a non-startup script fails (Startup
Script is OK); the auto-select tracks the failure, not position or
display name.
Each play function was verified to actually run by deliberately failing
the assertion once and confirming the test errored.
<details>
<summary>Decision log</summary>
The shape evolved during review:
1. First pass: collapse to `useState("all")`. Reviewer noted this lost
the auto-jump for legitimate failures.
2. Second pass: `useState(failedSourceId ?? "all")`. Codex flagged that
`failedSourceId` could point to a tab with no rendered entries, breaking
the Logs panel.
3. Final pass (this PR): `useState("all")` + `useEffect` + ref.
Auto-jump only fires once, only when the failed source has rendered
logs, and never overrides a manual selection.
Deferred from coder-agents-review feedback:
- **DEREM-5**: `agent.log_sources.find(...)` can disagree with the tab
bar's sort order when multiple non-startup scripts fail concurrently.
Low probability, one click to recover. Worth a follow-up if it becomes a
real complaint.
</details>
### Verification
- `pnpm check` (biome) — clean
- `pnpm lint:types` (tsc) — clean
- `pnpm vitest run --project "storybook (chromium)"
src/modules/resources/AgentRow` — 28/28 passed
---------
Co-authored-by: Atif Ali <atif@coder.com>
Co-authored-by: Jeremy Ruppel <jeremy.ruppel@gmail.com>
Renames the Agents chat stream-silence error from `startup_timeout` to
`stream_silence_timeout` now that the timeout applies to any gap between
provider stream parts, not just first-token startup.
Updates the SDK enum, generated API docs/types, chat error copy, and
Agents UI stories/status labels so the user-facing wording describes a
stalled provider response instead of startup delay.
> **Breaking change:** This is a very minor breaking change for the
Coder Agents API: the public chat error kind enum no longer includes
`startup_timeout`, so clients matching that specific value should handle
`stream_silence_timeout` instead.
The descendant selector `[&_img]:size-icon-sm` in `menuItemClass` was
matching the `<img>` nested inside `<Avatar>` on the User settings row
of the mobile menu, pinning the avatar image to `1.125rem` instead of
letting it fill its Avatar container. The same descendant rule also
applied to nested SVGs.
Switch both rules to direct-child selectors (`[&>svg]`, `[&>img]`),
matching the symmetric `shrink-0` rules already next to them and the
same pattern used in `Button.tsx`. Menu items that contain nested
`<img>`s already set their own sizes explicitly (`w-4 h-4` in
`ProxySettingsSub`, `!size-3.5` in `WorkspacePill`), so this only stops
overriding components like `Avatar` that manage their own internal
sizing.
Also removes the now-dead `[&_img]:w-full [&_img]:h-full` workaround in
`UserDropdownContent.tsx` that fought the same bug per-item; that menu
item no longer renders an `<img>`, and the root fix makes the workaround
unnecessary anyway.
Refs
[CODAGT-552](https://linear.app/codercom/issue/CODAGT-552/mobile-menu-user-avatar-image-is-undersized-inside-its-container)
<details>
<summary>Investigation notes</summary>
**Repro (before):** mobile viewport, user has an image avatar set, open
hamburger menu, observe the avatar in the User settings row is smaller
than its bordered square.
**Root cause:** `site/src/components/DropdownMenu/menuClasses.ts` set
`[&_img]:size-icon-sm` using a Tailwind descendant selector (`_`). That
matches every `<img>` *inside* a menu item, including the one nested
inside `<AvatarPrimitive.Root>`. The Avatar's inner image is supposed to
be `aspect-square size-full object-contain` and inherit its size from
the Avatar root (`--avatar-default` ≈ 24px by default), but the
descendant rule overrides that and pins it to 1.125rem.
**Prior workaround:** `UserDropdownContent.tsx` previously added
`[&_img]:w-full [&_img]:h-full` per-item to fight the same bug on
desktop. That menu item no longer renders an `<img>`, so the override
was already dead code; removed here for hygiene.
**Safety check on the wider change:** every other menu item that
contains a nested `<img>` already sets its own sizes explicitly:
- `ProxySettingsSub` (`MobileMenu.tsx`): `<ExternalImage className="w-4
h-4" />`
- `WorkspacePill.tsx`: `[&_svg]:!size-3.5 [&_img]:!size-3.5` on the menu
content
Direct-child icons (`ChevronRightIcon`, `CircleHelpIcon`, `XIcon`,
lucide icons in `UserDropdownContent.tsx`) remain direct children of the
menu item, so `[&>svg]:size-icon-sm` keeps sizing them as before.
</details>
---
_Authored by Coder Agent on behalf of @tracyjohnsonux._
> Mux created this PR on behalf of Mike.
AI provider creation previously collapsed OpenAI-compatible presets like
Google and generic OpenAI-compatible providers to `openai`, which lost
the backend provider discriminator.
Preserve selected provider types in the create payload, keep explicit
stored types authoritative when reconstructing edit form values, and add
frontend plus backend regressions for the supported preset types.
Fixes https://linear.app/codercom/issue/CODAGT-547
The "Connect a provider" link shown on `/agents/settings/models` when no
providers are configured was pointing to `/agents/settings/providers` (a
stale duplicate view) instead of `/ai/settings` (the canonical provider
configuration page).
Audited all frontend source files for references to the stale path. This
was the only link; other references to `/ai/settings` already point to
the correct page.
<details><summary>Generated by Coder Agents</summary>
This PR was generated by Coder Agents on behalf of @tracyjohnsonux.
</details>
---------
Co-authored-by: Jaayden Halko <jaayden@coder.com>
closes CODAGT-326
<img width="335" height="658" alt="Screenshot 2026-06-02 at 18 29 39"
src="https://github.com/user-attachments/assets/ee103047-67ad-403c-b67b-eb2147726db3"
/>
On viewports below the `md` Tailwind breakpoint, the agents-chat
workspace pill becomes full width via the existing
`mobile-full-width-dropdown` CSS hook. The `Ports (X)` item used a Radix
`DropdownMenuSub` which opened a flyout sub-content to the right of the
parent menu, so on mobile the sub-content had nowhere to render and
clipped off the right edge.
Mirror the inline sub-panel pattern already used by the agent chat input
plus menu: lift a `view: "main" | "ports"` state into `WorkspacePill`,
and on mobile swap the same dropdown's contents to a `Back` + ports list
panel instead of opening a flyout. Desktop keeps the existing flyout
sub-menu unchanged.
Closes
[CODAGT-326](https://linear.app/codercom/issue/CODAGT-326/port-forward-menu-is-cut-off-on-mobile-viewports).
Visual coverage is added via two new Storybook stories at the `mobile1`
(375 px) viewport: `MobilePortsInlinePanel` (full interaction +
assertions) and `MobilePortsInlinePanelOpen` (visual / Chromatic capture
stop point).
<details>
<summary>Implementation plan</summary>
### Why
The dropdown content already gets full viewport width on `< 768 px` via
the `mobile-full-width-dropdown` CSS hook in `site/src/index.css`. A
Radix `DropdownMenuSub` opens to the right of its trigger, so on mobile
it has no room and clips. Forcing the sub-content to also be full-width
would overlap the parent and break keyboard / focus flow. The existing
convention for nested menus on mobile in this codebase is the inline
sub-panel (see `plusMenuView` in `AgentChatInput.tsx`).
### `WorkspacePill.tsx`
- Add `view: "main" | "ports"` state and reset to `"main"` on close.
- Extract `usePortsData(workspace, agent, enabled)` and a shared
`PortsList` so desktop sub-content and mobile inline panel share one
renderer.
- Replace `PortsSubMenuItem` with `PortsMenuItem`, which uses
`useIsBelowMdViewport()` to render either:
- **Mobile:** a regular `DropdownMenuItem` whose `onSelect` calls
`event.preventDefault()` (keeps the dropdown open) and switches to the
inline view.
- **Desktop:** the existing `DropdownMenuSub` flyout, behavior
unchanged.
- Add `MobilePortsPanel` rendered inside the parent
`DropdownMenuContent` when `view === "ports"`. Includes a `Back` item
that returns to the main view.
- Add a small reactive `useIsBelowMdViewport()` hook around the existing
`isBelowMdViewport` helper.
</details>
> Created on behalf of @jaayden by Coder Agents.
This allows a Coder Agents chat to detach from its linked workspace
without deleting or changing the workspace, so a different workspace can
be linked later. It adds detach controls wherever the linked workspace
appears, including the workspace pill menu, fallback workspace badges,
and the workspace picker. The workspace selection state now updates
consistently across desktop and mobile.
Running workspace:
<img width="453" height="296" alt="image"
src="https://github.com/user-attachments/assets/ac5197a7-f0f4-4123-bbea-d3ddaca7a3e4"
/>
Stopped workspace:
<img width="389" height="203" alt="image"
src="https://github.com/user-attachments/assets/f5a8a90c-4bb0-405a-ade3-791146687b2d"
/>
Closes CODAGT-510
After the chat agent creates a workspace via the `create_workspace`
tool, opening the composer `+` menu and clicking "Attach workspace"
could show "No workspaces found" until a full page refresh, even though
the workspace pill already rendered the linked workspace correctly.
The picker was sourced only from the `owner:me` workspace list query,
whose cache could be stale right after `create_workspace` completed. The
fix derives the picker options at render time from both the owner
workspace list and the linked workspace already fetched by ID for the
pill, prepending or replacing the linked workspace only when the current
user owns it. This keeps the picker consistent with the pill without
broadening visibility beyond `owner:me` or invalidating workspace lists
on chat link updates.
Relates to CODAGT-510
The agents UI showed "Response startup is taking longer than expected"
after a 15s grace period while waiting on the LLM provider. The message
implied a problem was about to occur, but it does not actually lead to a
timeout. The typical underlying cause is provider slowness rather than a
client-side issue, so the warning is alarmist and unhelpful.
Drop the delayed message and its timer entirely. The `starting` phase
now keeps showing the shimmering "Thinking..." indicator until the first
stream chunk arrives. Also remove the now-dead `startingResetKey` /
`chatID` plumbing that only existed to remount the placeholder and reset
the delayed-message timer when switching chats.
Closes CODAGT-536
I ran into the `SettingsViewResets` Storybook flake twice on my branch.
The story reopens Agents settings immediately after clicking `Back to
Agents`, but the helper was synchronously checking for the desktop
`Settings` link before React Router had finished rendering `/agents`; on
desktop it could then fall through to the mobile-only `More options`
menu and fail.
Use `findByRole` for the desktop `Settings` link so the helper waits for
the accessible sidebar link before clicking it, matching the existing
Storybook interaction pattern used elsewhere in Agents stories.
Copilot is the only AI provider type that could not be added through the `/ai/settings` UI. The aibridge runtime and the env-var seeding path already supported it, but the runtime CRUD API rejected `type=copilot` and the UI omitted it entirely. The root cause is that Copilot's auth model (a per-request GitHub OAuth token, with no pre-shared key) does not fit the credential-centric add-provider flow that every other provider uses.
## Backend
Allow `type=copilot` in `CreateAIProviderRequest.Validate()`, and reject `api_keys` for Copilot on both create (validation) and update (handler sentinel), mirroring the existing Bedrock guards. Copilot carries no stored credential.
## Frontend
Add Copilot to the provider type picker (with the `github-copilot.svg` icon) and give the form a credential-free branch: name, display name, and a free-text endpoint defaulting to `https://api.business.githubcopilot.com`, with copy explaining that authentication happens via the user's GitHub token at request time. Copilot maps to the distinct `copilot` wire type rather than collapsing to `openai`, and the edit flow recovers it correctly.
The endpoint stays required with a business-tier default; users on the individual or enterprise endpoints edit the field.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Execute tool failures that only return an `error` field, such as
stopped-workspace connection failures, were rendered as a generic failed
command without showing the backend detail.
Normalize execute results into transcript blocks so shell output and
tool errors both render in the *expanded* command transcript, and add
Storybook coverage for connection errors plus output-with-error cases.
<img width="832" height="482" alt="image"
src="https://github.com/user-attachments/assets/50b04b9a-b153-48e5-ab5e-6c2fa000f21e"
/>
edit: i've dropped the red on the danger icon, though it was
pre-existing. no point alerting the user to an error the model will
handle.
Closes CODAGT-530
Fixes minor spacing issues in the agents sidebar.
## Changes
- Reduce gap between New Agent / Search nav items from `gap-1` (0.25rem)
to `gap-[0.15rem]`
- Change Chats header top spacing from `mt-4` (margin-top 1rem) to
`pt-6` (padding-top 1.5rem)
- Remove `pt-5` padding-top from the scroll content groups container
(Today, Yesterday, etc.)
- Add `pr-1` (0.25rem padding-right) to the unread indicator circle
> Generated by Coder Agents on behalf of @tracyjohnsonux
Implements https://linear.app/codercom/issue/AIGOV-285
Follow the structure established in
https://github.com/coder/coder/pull/25203
## Summary
Adds the `user_ai_budget_overrides` table and CRUD API at
`/api/v2/users/{user}/ai/budget`. An override sets a custom per-user
spend cap that supersedes group-budget resolution, attributing spend to
a specific group.
## Schema
```sql
CREATE TABLE user_ai_budget_overrides (
user_id UUID PRIMARY KEY REFERENCES users(id) ON DELETE CASCADE,
group_id UUID NOT NULL REFERENCES groups(id) ON DELETE CASCADE,
spend_limit_micros BIGINT NOT NULL CHECK (spend_limit_micros >= 0),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
```
## Membership lifecycle
The membership invariant — a user must be a member of the attributed
group, including when that group is "Everyone" — would naturally be
expressed as a composite FK on `(user_id, group_id) →
group_members_expanded(user_id, group_id)`. PostgreSQL doesn't allow
foreign keys to reference views, so enforcement is split across two
mechanisms:
- **Write-time check.** A CHECK constraint on the table
(`user_ai_budget_overrides_must_be_group_member`) calls a `STABLE`
function `is_group_member(user_id, group_id)` that queries
`group_members_expanded`. The view surfaces both regular group
memberships and the implicit "Everyone" group memberships from
`organization_members`. Any INSERT or UPDATE that violates the predicate
is rejected with a Postgres `check_violation`, which the handler maps to
a 400. `is_group_member` is defined as a general predicate, reusable by
any future table that needs the same check.
- **Cascade on removal.** Two `BEFORE DELETE` triggers handle membership
loss:
- `trigger_delete_user_ai_budget_overrides_on_group_member_delete` on
`group_members` — covers regular group removals (admin action, OIDC
sync).
- `trigger_delete_user_ai_budget_overrides_on_org_member_delete` on
`organization_members` — covers the "Everyone" group, whose membership
lives in `organization_members`.
The single-column FKs on `users(id)` and `groups(id)` remain to cascade
on user or group deletion (those paths don't pass through
`group_members`).
## Authorization
The dbauthz layer gates each operation against the `User` and (for
writes) `Group` resources:
| Operation | User resource | Group resource |
|-----------|----------------|----------------|
| `GET` | `ActionRead` | — |
| `PUT` | `ActionUpdate` | `ActionUpdate` |
| `DELETE` | `ActionUpdate` | `ActionUpdate` |
For `DELETE`, the dbauthz layer fetches the existing override first to
learn the attributed `group_id`, then runs both checks.
### Role matrix
| Role | GET | PUT | DELETE |
|--------------|-----|-----|--------|
| Owner | ✅ | ✅ | ✅ |
| UserAdmin | ✅ | ✅ | ✅ |
| OrgAdmin | ✅ | ❌ | ❌ |
| OrgUserAdmin | ✅ | ❌ | ❌ |
Internal discussion:
https://codercom.slack.com/archives/C096PFVBZKN/p1779392747885359
## Audit logs
Audit logs will be addressed in a follow-up PR.
Builds on top of https://github.com/coder/coder/pull/25794
Adds a new `provider_disabled` error classification in `chatd` with the
corresponding plumbing to classify it as non-retryable. Also adds a
story for how this particular error kind is displayed in the UI.
RFC: [Bridge ↔ Boundaries Correlation
RFC](https://www.notion.so/coderhq/Gateway-and-Firewall-Correlation-RFC-31ad579be592803aa8b3d48348ccdde9)
Register a dedicated `boundary_log` RBAC resource type with `create`,
`read`, and `delete` actions, replacing the placeholder
`rbac.ResourceAuditLog` and `rbac.ResourceSystem` references previously
used in the dbauthz layer.
Create is granted at user-level so workspace agents can only write logs
owned by their workspace owner, preventing cross-workspace log
fabrication. Delete is restricted to `DBPurge` only; no human role
(including owner) can delete boundary logs.
| Subject | Create (own) | Create (other) | Read (all) | Delete |
|---|---|---|---|---|
| Workspace agent | yes | no | no | no |
| Owner (site admin) | yes (via member) | no | yes | no |
| Auditor | no | no | yes | no |
| DBPurge | no | no | no | yes |
### Changes
- **RBAC policy & resource definition**: add `boundary_log` to
`policy.go` and generate `ResourceBoundaryLog` object, scope constants,
and codersdk/TypeScript types.
- **dbauthz authorization**: replace all
`ResourceAuditLog`/`ResourceSystem` placeholders with
`ResourceBoundaryLog`. `InsertBoundaryLog` and `InsertBoundarySession`
derive the workspace owner from the agent and authorize with
`.WithOwner()` for user-scoped create.
- **Role assignments:**
- **Owner (site):** read only. Excluded from `allPermsExcept` wildcard;
create is inherited from member at user-level.
- **Member (user-level):** create. User-scoped so agents can only write
logs they own.
- **Auditor (site):** read.
- `boundary_log` is excluded from org-admin, org-member, and
org-service-account `allPermsExcept` calls for consistency with
`ResourceBoundaryUsage`.
- **System subjects:**
- **DB Purge** (`SubjectTypeDBPurge`): delete. The only subject that can
remove boundary logs.
- **Workspace agent scope**: `ResourceBoundaryLog` with wildcard ID in
the agent scope allow-list (necessary for creation since no pre-existing
ID exists). User-level role scoping prevents deployment-wide access.
- **DB migration** (`000510_boundary_log_scopes`): add `boundary_log:*`,
`boundary_log:create`, `boundary_log:delete`, `boundary_log:read` enum
values to `api_key_scope`.
- **Test coverage**: `BoundaryLogCreate` (user-scoped, only matching
owner succeeds), `BoundaryLogDelete` (all human roles denied),
`BoundaryLogRead` (owner + auditor). dbauthz mock tests set up workspace
agent lookups for owner derivation.
- **Generated docs**: update OpenAPI specs, API reference docs, and
frontend type definitions.
---------
Co-authored-by: Muhammad Danish <mdanishkhdev@gmail.com>
Co-authored-by: Coder Agents <coder-agents-review[bot]@users.noreply.github.com>
Error messages in agent chat now expose the actual error detail
instead of hiding it entirely. Also captures API response detail
for generic errors that previously dropped it.
Updates the description text on the Agent Settings > Models page
(`/agents/settings/models`).
**Before:** "Choose which models from your configured providers are
available for users to select. You can set a default and adjust context
limits."
**After:** "Choose which models from your configured providers are
available for Coder Agents. Set a default and adjust context limits."
> Generated by Coder Agents on behalf of @tracyjohnsonux
Update the ChatSearchDialog with four interaction states:
1. **Default (empty)**: quick actions (New chat, Settings, Personal
Skills, View usage) + recent chats list
2. **Focused**: filter-by dropdown overlay with clickable chips (Unread,
Archived, PR status, Diff URL)
3. **Active search**: filter pills and/or freeform text with debounced
live results showing "N results" count
4. **Parameterized filter**: incomplete pill (dashed border) for filters
needing a value (e.g. `pr_status:`); Space or Enter commits the value
into a solid pill
Filter pills and freeform text are combinable. Backspace on empty text
removes the last pill. The filter dropdown overlays content below and
dismisses on blur or Escape.
Changes:
- `ChatSearchDialog.tsx`: manages structured filter state with separate
`incompleteFilterKey` tracking, renders filter dropdown, passes recent
chats and quick action callbacks
- `ChatSearchInput.tsx`: renders completed filter pills (solid border, x
dismiss) and incomplete pills (dashed border) inline with the text input
- `ChatSearchResults.tsx`: default view shows quick actions + recent
chats instead of the old help text; "No matching chats" is centered in
the modal
- `ChatsSidebar.tsx`: passes `recentChats`, `onNewChat`,
`onOpenSettings` to the dialog
<details>
<summary>Implementation notes</summary>
- No backend changes; reuses existing `chatSearch()` API and
`normalizeChatSearchInput()` normalizer
- Filter state uses a clean model: `filters` array for committed filters
+ `incompleteFilterKey` string for the parameterized filter being typed;
`freeText` serves dual purpose (filter value when incomplete key is set,
otherwise freeform search)
- The filter dropdown is absolutely positioned (`top-full`) below the
search input, dismisses via container `onBlur` with
`contains(relatedTarget)` check
- Quick action links (Personal Skills, View usage) use `Link` components
that close the dialog on click
> Generated with [Coder Agents](https://coder.com/agents)
</details>
Move the chat search button from a subtle icon next to the filter in the
Chats header to a prominent full-width nav item below New Agent. The
search bar shows a magnifying glass icon and "Search" label by default,
with the keyboard shortcut badge (`⌘ K` / `Ctrl K`) and background
appearing on hover/focus.
Also pull the Chats header and filter row out of the scroll area so the
scrollbar only covers the chat list content, and align the logo row with
the nav item content inset.
> 🤖 Generated by Coder Agents on behalf of @tracyjohnsonux
Chat ACL audit diffs rendered as `[object Object]` because the diff
viewer called `.toString()` on object values. Common chat operations
(archive, share) showed generic "updated chat" descriptions instead of
semantic ones.
Add `chatAuditLogDescription` to derive semantic descriptions from the
audit diff for successful chat writes: "archived/unarchived chat" for
archive toggles, "updated sharing for chat" for ACL-only changes.
Extract diff value formatting into `formatAuditDiffValue`, which renders
object values as deterministic compact JSON with sorted keys, fixing the
`[object Object]` rendering for chat ACLs and any other object-valued
fields. The previous `determineIdPSyncMappingDiff` workaround for IdP
sync mappings was removed because the generic formatting handles it.
Closes CODAGT-513
> Generated by Coder Agents on behalf of @johnstcn
Redesigns the agent desktop panel with a persistent toolbar, zoom modes,
and a detachable pop-out window.
## Changes
**Toolbar** (`DesktopToolbar`)
- Persistent top bar with right-aligned controls: Take/Release control,
Zoom toggle, Detach
- All buttons use consistent `subtle` variant with icon + label
- `h-8` height, `bg-surface-primary` background with bottom border
**Zoom modes**
- Defaults to fit-to-window (`scaleViewport = true`) so the full
1920x1080 desktop is visible
- Toggle to native 100% resolution via toolbar button or keyboard
shortcuts (`Ctrl+0` fit, `Ctrl+1` native)
- noVNC background color overridden from hardcoded `rgb(40,40,40)` to
`--surface-secondary` so letterbox margins match the app theme in light
and dark mode
**Pop-out window**
- New route at `/agents/:agentId/desktop` for a dedicated desktop window
- Opens via toolbar "Detach" button at 50% of screen size, centered
- BroadcastChannel coordination: sidebar shows placeholder with "Bring
back" button
- Closing the pop-out window automatically restores the sidebar panel
**Other**
- `useDesktopConnection` hook accepts `scaleViewport` option, synced to
the RFB instance via a secondary effect
- `DesktopPanelContext` extended with `agent` and `workspace` fields
- Replaces the previous hover-overlay take/release control UX with
toolbar buttons
> Generated by Coder Agents on behalf of @tracyjohnsonux
Changes the "Manage deployment-wide BYOK" link on the AI Providers
settings page (`/ai/settings`) to "View docs", matching the pattern used
on the provisioner keys page (`/organizations/{org}/provisioner-keys`).
### Changes
- Swapped `Link` from `react-router` to `#/components/Link/Link` (uses
`href` instead of `to`)
- Removed `target="_blank"` and `rel="noreferrer"`: the link now
navigates in the same tab, matching the provisioner keys page convention
- Changed link text from "Manage deployment-wide BYOK" to "View docs"
> Generated by Coder Agents on behalf of @tracyjohnsonux
Other-user agent chats showed a banner that implied prompts would run as
the owner, but submitting from that view is forbidden.
This updates the banner to identify the chat owner and makes chats owned
by another user read-only in the UI by disabling the composer and hiding
inline send or edit follow-up actions.
> Mux working on behalf of Mike.
_Disclosure:_ _produced_ _with_ _Claude_ _Opus_ _4\.7_
AI Gateway only supports Anthropic (+Bedrock), OpenAI, and Copilot providers at present. All other types (Vercel, Gemini, etc) will be mapped to OpenAI since they support OpenAI-compatible endpoints.
Fixes CODAGT-484.
- Removed "quota", "billing", "insufficient_quota", "payment required"
from `authStrongPatterns`
- Added `usageLimitPatterns` slice with those patterns
- Added `usageLimitMatch` signal and rule between overloaded and
authStrong in priority
- Added terminal/retry messages for `ChatErrorKindUsageLimit`
- Simplified auth message (removed billing reference)
- Frontend: conditional `!usageLimitStatus.provider` guard on the "View
Usage" Alert
- Added `TestClassify_UsageLimitBeatsAuth` with 5 cases including real
production OpenAI error
- Added `ProviderQuotaExceeded` story asserting no "View Usage" link and
correct `ChatStatusCallout` rendering
> Generated with [Coder Agents](https://coder.com/agents)