Commit Graph
4204 Commits
Author SHA1 Message Date
Zach ddc0e99c69 chore: remove coder_secret Terraform integration (#25512)
Removes the coder_secret Terraform integration: the data.coder_secret
consumption path through provisionerdserver → provisioner.proto →
provisioner/terraform, the dynamic-parameter secret-requirement
validation, and the workspace-update / resolve-autostart surfaces that
depended on it. This is being done due to a product/feature direction
change (see PLAT-243). User-secret CRUD (DB, REST, CLI, UI, telemetry, audit)
and the agent-manifest secret-injection path are untouched.

The provisionerd API is bumped from v1.17 to v1.18 rather than rolled
back: v1.17 shipped in v2.33.x, so user_secrets field numbers are
reserved and the changelog documents both versions.

Generated with assistance from Coder Agents.
2026-05-21 09:19:29 -06:00
Jake Howell 4c91069479 fix: append asChild to <AgentApps /> (#25547)
This pull-request resolves a stupid issue wherein the `<DropdownItem />`
would wrap into an `<AppLink />` causing two hit targets. This now is a
single one and inline with the Figma Coder Kit after the parent merges
onto the child.

| Old | New |
| --- | --- |
| <img width="400" alt="preview-broken-button"
src="https://github.com/user-attachments/assets/b74018a7-9279-4464-b71b-a16901e844c9"
/> | <img width="308" height="141" alt="image"
src="https://github.com/user-attachments/assets/8b4a1371-e1bb-4341-9a6a-033a7b48c31d"
/> |
2026-05-22 00:21:26 +10:00
Jake Howell ef8eeb1f5d fix: resolve badge content color (#25550)
I noticed that the content on our badges for options broke, I've
restored this to using `text-content-primary` so that the content will
always be legible and not inherit from the base body css.

| Old | New |
| --- | --- |
| <img width="718" height="488" alt="CLI_PREVIEW_OLD"
src="https://github.com/user-attachments/assets/98c91591-b8b8-4ed0-bdd5-0bd2fb5f2802"
/> | <img width="718" height="488" alt="CLI_PREVIEW_NEW"
src="https://github.com/user-attachments/assets/5c07a5e0-3fbf-41a0-8454-0e7bd6ee52e8"
/> |
2026-05-22 00:21:02 +10:00
Jake Howell 1fdb8ed9a0 fix: wrap latestVersion?.message with <div /> (#25549)
This pull-request wraps the `latestVersion?.message` in a `<div />` so
that our content is affected by the `flex flex-col` class from the
parent.

| Old | New |
| --- | --- |
| <img width="441" height="363" alt="PREVIEW_1"
src="https://github.com/user-attachments/assets/0693136a-cfcc-4f17-a94d-35c14943c3bd"
/> | <img width="441" height="295" alt="PREVIEW_1_NEW"
src="https://github.com/user-attachments/assets/ce6c1126-6153-4d7f-a4b8-f01a5a98e270"
/> |
| <img width="441" height="396" alt="PREVIEW_2"
src="https://github.com/user-attachments/assets/b26e81b4-09fb-4317-9d25-3ca8b5989c54"
/> | <img width="441" height="294" alt="PREVIEW_2_NEW"
src="https://github.com/user-attachments/assets/241afe85-c278-4593-a985-bcc2400ac52f"
/> |
2026-05-22 00:20:41 +10:00
Jaayden Halko 2a45262fa2 fix: truncate search results (#25566)
1. truncates search results
2. display a loading spinner when retrieving new search results when
existing results are displayed
3. Improve dialog resizing behavior
2026-05-21 14:42:32 +01:00
Danielle Maywood 873aa0da6a fix(site/src/pages/AgentsPage/components/ChatConversation): add min-h-6 to StatusPlaceholder to prevent layout shift (#25560) 2026-05-21 12:58:02 +01:00
Danielle Maywood d9875d8902 fix(site): restore iOS backspace in agent chat input (#25531) 2026-05-21 11:42:20 +01:00
Jaayden HalkoandCian Johnston 92d67888b8 feat: modal chat search popup (#25535)
closes CODAGT-422

<img width="591" height="404" alt="Screenshot 2026-05-21 at 17 33 26"
src="https://github.com/user-attachments/assets/5ef8134a-aca7-4442-bed7-9a31698dc5d6"
/>
<img width="590" height="499" alt="Screenshot 2026-05-21 at 17 33 46"
src="https://github.com/user-attachments/assets/b8e99e53-793c-4480-8411-d90f97f9bcb6"
/>
<img width="376" height="301" alt="Screenshot 2026-05-21 at 17 36 39"
src="https://github.com/user-attachments/assets/d9c4a45c-9094-40a2-ac88-87415c45b358"
/>

---------

Co-authored-by: Cian Johnston <cian@coder.com>
2026-05-21 11:39:58 +01:00
Paweł Banaszewski 46e93e6325 chore: add ai_gateway options that alias aibridge options (#25061)
Adds options matching new AI Gateway naming.
New options are added as alias for old options. Old options are still
working.
Old options have deprecated message.
No conflict detection was added.

Updated documentation so it mentions only new options. Added note about
old options still working.

> Various AI tools where used to create this PR
2026-05-21 11:14:11 +02:00
Mathias Fredriksson f1b772928d feat: parse execute tool commands and render them in the chat UI (#25478)
When the execute tool runs a chained shell command, the UI previously
rendered the raw string. Long chains like "cd /repo && git pull &&
git add . && git commit -m fix" were hard to scan.

A new ChatMessagePart.ParsedCommands [][]string field on tool-call
parts carries one entry per simple command, parsed in chatd from args
via mvdan.cc/sh/v3/syntax. The frontend renders the joined list ("cd,
git pull, git add, git commit") in place of the raw command, and falls
back to the raw command when the field is absent.

Closes CODAGT-446
2026-05-21 08:12:34 +00:00
Rowan Smith 57ed244de1 chore: update cli install link to use local deployment URL instead of coder.com (#25548)
Updates the connect via SSH menu shown in workspaces to redirect to
`/install` on the local deployment instead of
`https://coder.com/docs/<version>/install`. This ensures consistency
with the user account dropdown menu which also references the local
deployment.

End goal is to ensure the user running install script receives the same
CLI version as is running on the Coder deployment.
2026-05-21 15:12:06 +10:00
Danielle Maywood 889add734e fix(site): use ToolCollapsible for thinking blocks (#25445) 2026-05-20 21:58:19 +01:00
Danielle Maywood e27d917135 feat: enable chat sharing (#24987) 2026-05-20 18:45:44 +01:00
Danielle Maywood e73292fc89 fix(site): recover malformed subagent chat links (#25532) 2026-05-20 13:39:49 +00:00
Jaayden Halko 1ba54e2ca6 chore: cleanup structure of left sidebar (#25528)
This cleanups up the folder structure and breaks up files into a more
atomic structure. No functional changes should exist here.
2026-05-20 14:06:54 +01:00
Atif AliandMichael Suchacz 7ffeac711c fix: correct web terminal glyph rendering and tmux display (#25059)
The web terminal was rendering Claude Code and Codex incorrectly because
xterm's custom glyph renderer draws block and quadrant characters with
its own geometry. The reconnecting PTY screen backend also exposed
`screen.xterm-256color` to the user's shell, which made tmux rendering
issues harder to reason about.

This PR:

* Disables xterm custom glyph rendering so the selected terminal font
draws block and quadrant glyphs.
* Adds a tiny Powerline-only terminal symbol fallback font so common
prompt separators still render when custom glyphs are disabled.
* Configures the screen backend to keep the inner shell `TERM` aligned
with the browser terminal emulator, including background color erase
behavior.
* Tightens reconnecting PTY tests around prompt synchronization and
`TERM` assertions.

<!-- linear:table-colwidths:200,200 -->
| Before | After |
| -- | -- |
| <img
src="https://uploads.linear.app/e62091d9-44f5-421c-8e5c-df481fc99003/3c45efce-9d7e-43b4-b24f-88d4d23d294a/ba68155e-949e-4961-b0b2-124757cb07bb?signature=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwYXRoIjoiL2U2MjA5MWQ5LTQ0ZjUtNDIxYy04ZTVjLWRmNDgxZmM5OTAwMy8zYzQ1ZWZjZS05ZDdlLTQzYjQtYjI0Zi04OGQ0ZDIzZDI5NGEvYmE2ODE1NWUtOTQ5ZS00OTYxLWIwYjItMTI0NzU3Y2IwN2JiIiwiaWF0IjoxNzc4MTgxNjUwLCJleHAiOjE4MDk3NTIyMTB9.45f1ZzBpWOF5OCJV0xHfICdpyRQ1UoGMbJjLYPqeAkg
" alt="Before: Claude Code logo rendering is distorted in the web
terminal outside and inside tmux" width="640" /> | <img
src="https://uploads.linear.app/e62091d9-44f5-421c-8e5c-df481fc99003/26b0a109-5e21-4000-b1b5-ddac87c409d4/46a301c2-a815-419a-92d2-c51cecdefe40?signature=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwYXRoIjoiL2U2MjA5MWQ5LTQ0ZjUtNDIxYy04ZTVjLWRmNDgxZmM5OTAwMy8yNmIwYTEwOS01ZTIxLTQwMDAtYjFiNS1kZGFjODdjNDA5ZDQvNDZhMzAxYzItYTgxNS00MTlhLTkyZDItYzUxY2VjZGVmZTQwIiwiaWF0IjoxNzc4MTgxNjUwLCJleHAiOjE4MDk3NTIyMTB9.SQVwUbtaf2OrpjRJPkRH3uc0nPqad0bNBVvcRyuR6NQ
" alt="After: Claude Code logo renders correctly in the web terminal
outside and inside tmux" width="640" /> |

## Validation

* `go test ./agent -run '^TestAgent_ReconnectingPTY$' -count=1`
* `pnpm --dir site test -- src/theme/constants.test.ts`
* `pnpm --dir site lint:types`
* `pnpm --dir site check`
* `pnpm --dir site build`
* `git commit` pre-commit hook passed
* `git push` pre-push hook ran and printed the repo CI monitoring hint

> Mux worked on this PR on Mike's behalf.

---------

Co-authored-by: Michael Suchacz <203725896+ibetitsmike@users.noreply.github.com>
2026-05-20 13:57:50 +02:00
Ethan e6fe9916d6 fix: pin chat timestamp chromatic ignore-mask width (#25522)
Follow-up to #25429, which wrapped `shortRelativeTime(chat.updated_at)`
in `<span data-chromatic="ignore">` to stop the chat row timestamp from
drifting `"46m" → "5m" → "now"` across Chromatic runs. Chromatic kept
flagging a sliver of change just to the left of the rendered text, even
though the area looks empty.

[Chromatic's docs](https://www.chromatic.com/docs/ignoring-elements/)
explain why: `data-chromatic="ignore"` masks pixel diffs **inside the
element's bounding rectangle**, but dimension changes of that rectangle
still trigger a diff. The wrapper span has no explicit width, so it
sizes to its text — `"46m"` (≈22px) shrinks to `"now"` (≈19px), the mask
shrinks by ~3px on its left edge, and that exposed strip is what
Chromatic was reporting.

Fix: pin the wrapper to `inline-block w-7 text-right`. `w-7` matches the
surrounding `w-7` column, and `text-right` keeps the trailing edge
anchored so the bounding rect is identical regardless of which
`shortRelativeTime` branch fired. The unread-dot branch is untouched and
keeps its own Chromatic coverage.
2026-05-20 19:55:54 +10:00
Danielle Maywood 96e3c49670 feat: add chat sharing API (#24968) 2026-05-20 10:46:35 +01:00
Danny Kopping dd3223451b feat: add AI providers HTTP CRUD handlers (#24894) 2026-05-20 10:21:36 +02:00
Michael Suchacz 9c2e0aac88 fix(site/src/pages/AgentsPage): widen inline desktop preview to fill chat (#25497) 2026-05-20 08:42:30 +02:00
Ethan 4c362499f2 fix(site): keep workspace quota meter live-updated in Agents chat (#25468)
The workspace quota meter in the Agents chat sidebar previously only
refreshed on a full page reload, while the AI cost-usage meter rendered
next to it already polled every 60 seconds.

This change gives the Agents usage indicator the same 60s
workspace-quota polling and refreshes derived workspace caches
immediately when workspace-affecting chat tools complete.
`create_workspace`, `start_workspace`, and `stop_workspace` now
invalidate the workspace quota and `workspaces` query family, so both
the credit numbers and the workspace-count detail stay in sync.

`create_workspace` still invalidates the chat record to resolve
workspace bindings, and archive-and-delete uses the shared workspace
mutation invalidation helper so deleting an agent workspace refreshes
the same derived workspace data.

Closes CODAGT-444


Tested manually and it works perfectly.
2026-05-20 12:30:28 +10:00
Michael Suchacz 5a8d0016a5 feat: add personal skill storage, API, and SDK (#25363)
> Mux updated this PR on behalf of Mike.

## Stack Context

This PR is the storage, permissions, API, and SDK layer for experimental
personal skills. #25362 has landed on `main`, so this branch is
restacked directly on `main`.

Stack order:
1. #25363 storage, permissions, API, and SDK
2. #25365 API test coverage
3. #25366 chattool and chatd integration
4. #25066 settings UI and docs
5. #25386 personal skills slash menu

## What?

Adds the `user_skills` database table, generated queries, RBAC resources
and scopes, audit resource handling, experimental user-scoped CRUD
endpoints, SDK types, and generated API/site types.

Follow-up review and restack fixes:
- Enforce a bounded personal skill description in parser and database
constraints.
- Return `403 Forbidden` for unauthorized create and update attempts.
- Return explicit conflict responses when soft-deleted users are
targeted.
- Keep user admins out of personal skills, while site owners can read
and delete but not create or update.
- Document trigger-raised constraint names and keep schema constants
covered by tests.
- Reuse `UserSkillMetadata` in the full `UserSkill` SDK response type.
- Generate user skill IDs in Go instead of relying on a database
default.
- Rebase on latest `main` and renumber the user skills migration to
`000502_user_skills`.

## Why?

Personal skills need durable user-owned storage with owner
authorization, limited site-owner moderation, and a hidden API surface
before chatd can consume them.

## Validation

- `make gen`
- `go test ./coderd/database -run '^TestUserSkillSchemaConstants$'
-count=1`
- `go test ./coderd/database/dbauthz -run
'^TestMethodTestSuite/TestUserSkills$' -count=1`
- `go test ./coderd -run '^TestPatchUserSkill$' -count=1`
- `go test ./codersdk ./coderd/database/db2sdk`
- `make lint`
- pre-commit hook on `97fd58108d`
2026-05-20 00:09:09 +02:00
Michael Suchacz 951a8e7237 feat: add intent labels to execute tool (#25482)
> Mux opened this PR on behalf of Mike.

Fixes CODAGT-451

Adds optional `model_intent` metadata to the built-in execute tool
schema so tool calls can carry a short user-facing intent label without
duplicating the command or duration.

The Agents UI now composes that intent with the existing execute command
and duration fields, displaying labels like `Checking repository state
using git fetch origin for 2.3s` while keeping the shell command visible
as the audit-relevant action.

Existing execute calls without an intent keep the previous `Ran
<command>` fallback label, so only intent-bearing calls get the new
composed label.
2026-05-19 18:47:12 +02:00
dylanhuff-at-coder 441854daa8 feat: add user secrets client utilities (#25370)
Add frontend API methods, mocks, and form helpers for user secrets CRUD. The new client methods cover list, get, create, update, and delete requests, including URL encoding for secret names used in route paths.

Add user secret form utilities for create and update payload construction, required create field checks, and structured API validation error mapping back to form fields. User secret name validation now lives in codersdk with tests, and coderd returns field-level validation errors for create, update, and uniqueness conflicts so the frontend can show backend-owned validation results consistently.
2026-05-19 09:30:31 -07:00
Cian Johnston ce7f41f56d fix: bump MaxChatFileIDs from 20 to 50 (#25492)
Fixes CODAGT-456
2026-05-19 16:53:30 +01:00
Steven MasleyandCoder Agents 1afc6d4fd0 feat: structured disconnect attribution for agent logs (#25191)
Implements
[PLAT-60](https://linear.app/codercom/issue/PLAT-60/enhance-disconnect-logs-with-structured-reason-attribution):
adds structured disconnect attribution to disconnect logs throughout the
agent and tailnet packages.

Every disconnect log site now carries structured slog fields. All
existing logs remain; existing messages are preserved with the fields
added alongside.

New fields on disconnect log lines:

- `connect_type` — which layer disconnected: `server_to_agent`,
`agent_to_client`, or `client_to_server`
- `disconnect_reason` — categorical reason: `graceful`, `network_error`,
`server_shutdown`, etc.
- `disconnect_expected` — whether the disconnect is normal operation
(`true`) or should be investigated (`false`)
- `disconnect_initiator` — who started it: `client`, `agent`, `server`,
or `network` (control-plane sites only)
- `disconnect_detail` — free-form supplemental info (where useful)

## What's covered

**Control plane (`server_to_agent`):** coordination RPC, DERP map
subscriber, agent runLoop, agent Close, `BasicCoordination.Close`,
`Controller.run`.

**Data plane (`agent_to_client`):** SSH sessions, reconnecting PTY,
JetBrains port-forwarding.

<details>
<summary>Control-plane sites</summary>

| Site | Reason | Initiator |
|---|---|---|
| `agent/agent.go` `runLoop` EOF | `network_error` | `network` |
| `agent/agent.go` `runCoordinator` deferred exit | `server_shutdown` /
`graceful` / `network_error` | `agent` / `server` / `network` |
| `agent/agent.go` `runDERPMapSubscriber` deferred exit | same (shared
`classifyCoordinatorRPCExit`) | same |
| `agent/agent.go` `Close` shutdown timeout | `server_shutdown` + detail
| `agent` |
| `agent/agent.go` `Close` clean coord disconnect | `server_shutdown` |
`agent` |
| `tailnet/controllers.go` `BasicCoordination.Close` | `graceful` or
`network_error` | `c.initiator` |
| `tailnet/controllers.go` `Controller.run` `net.ErrClosed` |
`network_error` | `network` |

</details>

<details>
<summary>Data-plane sites</summary>

| Site | Reason | Notes |
|---|---|---|
| `agent/agentssh/agentssh.go` SSH session closed | free-form
(`graceful`, `process exited with error status: N`, etc.) | Also sets
`closeCause("normal exit")` for clean exits so coderd's
`connection_log.DisconnectReason` is no longer empty |
| `agent/reconnectingpty/server.go` PTY closed | `server_shutdown`,
error string, or `graceful` | |
| `agent/agentssh/jetbrainstrack.go` channel closed | `normal close` or
error string | Previously passed empty reason |

</details>

<details>
<summary>Bug fix</summary>

The deferred `disconnected from coordination RPC` log no longer fires
when the initial `Coordinate()` RPC call fails before any connection is
established.

</details>

Refs PLAT-60.

---

_This PR was prepared by Coder Agents on behalf of @Emyrk._
**Manually QA'd a lot of common disconnects**

---------

Co-authored-by: Coder Agents <noreply@coder.com>
2026-05-19 09:47:03 -05:00
Bartek Gatz ba0df64390 chore(site): update Codernauts roster to 47 entries (#25427)
Replace the original 13-person roster with the full 47-person list,
sorted alphabetically by first name.

cc @DanielleMaywood for review

> [!NOTE]
> This PR was authored by Coder Agents.
2026-05-19 12:59:06 +02:00
Danielle Maywood 170a6e1fe9 feat: add chat sharing foundation (#25041) 2026-05-18 22:32:05 +01:00
Yevhenii Shcherbina 2732378da2 feat: audit group AI budget mutations (#25374)
Relates to
https://linear.app/codercom/issue/AIGOV-284/add-group-budgets-table-and-crud-api

Adds audit-log support for `group_ai_budget` mutations. Without it, an
admin could silently lower a spend limit from `$500` to `$50` or delete
a budget entirely, with no record of who performed the action.

Both write (`create-or-update`) and delete actions now produce audit log
entries, including before/after diffs for `spend_limit_micros`.

Depends on #25203.

## Old Version
<img width="1340" height="456" alt="image"
src="https://github.com/user-attachments/assets/e9ff52fb-a905-4aef-a4ee-7cdc58e68b75"
/>

## New Version (see
https://github.com/coder/coder/pull/25374/changes/9d22833de87cc106c24142c1d471a3f71872bf67)
<img width="1347" height="496" alt="image"
src="https://github.com/user-attachments/assets/1b9bbfa1-f86d-48e3-a0b1-266eb76f851f"
/>
2026-05-18 15:17:20 -04:00
Kyle CarberryandCoder Agent 385146000b feat: record created_at/completed_at on reasoning ChatMessageParts (#24789)
Records reasoning start and end times on persisted reasoning
`ChatMessagePart`s so reasoning duration can be computed for stored
chats. Backend-only: no SSE changes and no frontend rendering ship in
this PR.

The `created_at` field on `ChatMessagePart` is extended to also be
present on `reasoning` parts (it previously appeared only on `tool-call`
and `tool-result`), and a new `completed_at` field is added for
`reasoning` parts.

### How timestamps are recorded

- `StreamPartTypeReasoningStart`: stamp `startedAt = dbtime.Now()` on
the active reasoning state.
- `StreamPartTypeReasoningEnd`: stamp `completedAt = dbtime.Now()` and
append both into parallel `[]time.Time` slices on `stepResult`.
- Persistence reads the slices in occurrence order (reasoning has no
provider-side ID) and applies them to the matching `ChatMessagePart` via
`buildAssistantPartsForPersist`. The first reasoning block's stamps go
onto the first reasoning part, and so on.
- `flushActiveState` flushes partial reasoning interrupted before
`StreamPartTypeReasoningEnd` with `startedAt` from the active state and
`completedAt = dbtime.Now()` at the interruption.

### Why two fields, not one?

Tool calls and results are point events. The frontend computes their
duration by subtracting the call's `created_at` from the result's
`created_at`. Reasoning is one assistant part that brackets a span, so
we record both endpoints on the part itself.

### Why not stamp in `PartFromContent`?

Same rationale as #24101: `PartFromContent` is called during both SSE
publishing and persistence. Stamping there would yield incorrect
persistence-time timestamps for reasoning blocks that finished much
earlier in the step. Instead we capture in the chatloop and apply during
persistence.

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

- `codersdk/chats.go`: extend `CreatedAt`'s `variants` to include
`reasoning?`; add `CompletedAt *time.Time` with `variants:"reasoning?"`.
- `coderd/x/chatd/chatloop/chatloop.go`: extend `reasoningState` with
`startedAt`; extend `stepResult` and `PersistedStep` with parallel
`[]time.Time` reasoning slices; stamp on
`ReasoningStart`/`ReasoningEnd`; thread the slices through all
`PersistStep` call sites including the interrupt-safe path; record
partial reasoning in `flushActiveState`.
- `coderd/x/chatd/attachments.go`: walk reasoning parts in occurrence
order and apply `step.ReasoningStartedAt[i]` to `part.CreatedAt` and
`step.ReasoningCompletedAt[i]` to `part.CompletedAt`.

### Tests

- `codersdk/chats_test.go` round-trips `created_at` + `completed_at` on
reasoning parts and verifies omission when absent and partial
interrupted parts.
- `coderd/x/chatd/chatprompt/chatprompt_test.go` asserts
`PartFromContent(ReasoningContent{})` does NOT stamp timestamps.
- `coderd/x/chatd/chatloop/chatloop_test.go`
`TestRun_ReasoningTimestamps` drives a stream with two reasoning blocks
and verifies parallel slices, monotonicity, ordering, non-zero values,
and content-block ordering.
`TestRun_InterruptedReasoningFlushesTimestamps` cancels mid-reasoning
and verifies `flushActiveState` records a non-zero pair.
- `coderd/x/chatd/attachments_test.go` covers
`buildAssistantPartsForPersist` for normal interleaved reasoning,
partial (zero `completed_at`), and missing slices.

</details>

> Generated by Coder Agents.

Co-authored-by: Coder Agent <agent@coder.com>
2026-05-18 12:30:30 -04:00
Danielle Maywood 46821525f7 fix(site): refine execute tool transcript UI (#25432) 2026-05-18 14:44:43 +01:00
Danny Kopping c69dd9c5dc feat: widen ai_provider_type enum for chatd providers (#25394) 2026-05-18 15:06:30 +02:00
Danny Kopping 0770428a5c feat: add AIProvider types and client methods (#24893) 2026-05-18 11:10:30 +02:00
Ethan 6a79f5f62e fix: ignore drifting timestamp in stories (#25429)
The `SectionHeadersCollapse` story (and every other
Chromatic-snapshotted story rendering the agents sidebar) was flaky
because each chat row renders `shortRelativeTime(chat.updated_at)` —
`"now"`, `"5m"`, `"7h"`, `"1d"`, etc. The values are computed against
the live wall clock from fixed fixture timestamps, so the rendered text
drifts on every run (e.g. `"7h"` → `"now"` mid-day) and Chromatic diffs
the change.

This wraps the timestamp in `<span data-chromatic="ignore">`, the
dominant codebase convention for time-varying text.
`data-chromatic="ignore"` appears 35 times across 20 files in
`site/src`, including `utils/schedule.tsx:139,153`,
`components/LastSeen/LastSeen.tsx:42`,
`modules/provisioners/Provisioner.tsx:80`,
`pages/OrganizationSettingsPage/.../ProvisionerRow.tsx:119`,
`JobRow.tsx:139`, `ProvisionerKeyRow.tsx:81`,
`pages/AIBridgePage/RequestLogsPage/RequestLogsRow/RequestLogsRow.tsx`
(multiple rows), `pages/HealthPage/WorkspaceProxyPage.tsx:144`, and
`pages/UserSettingsPage/TokensPage/TokensPageView.tsx:117`. By
comparison, only one component (`AppStatuses`) plumbs a reference date
through props, and there is no `Date.now`-stubbing precedent that would
actually stabilize `dayjs()`-based output like `shortRelativeTime`.

The wrap is scoped tightly: only the text node returned by
`shortRelativeTime` is ignored, so the sibling unread-indicator dot and
surrounding layout still participate in snapshots.
2026-05-18 18:21:00 +10:00
Dean Sheather 5cc655806f fix(site/src/pages/AgentsPage): sanitize chat upload filenames client-side (#25421)
Filenames from the OS (e.g. `Screen Shot 2025-01-01 at 10.00.00 AM.png`
or `My Report (final).pdf`) flow unchanged through the chat-attach hooks
into chip labels, the persisted-attachment localStorage records, the
upload `Content-Disposition` header, and downstream LLM prompts.
Characters such as parentheses, brackets, quotes, shell or URL or path
metacharacters, whitespace, and control codes are all valid in HTTP
transport today but tend to break things further down the line (LLM tool
calls that quote the name, audit logs, any future S3/path interpolation,
shell-quoted tooling).

Sanitize at the boundary in `useFileAttachments.handleAttach` and
`useChatDraftAttachments.handleAttach` by mapping each incoming `File`
through a new `renameChatFileForUpload` helper. The helper replaces
`()[]{}<>'\"\`;,:*?|&#$\\/`, whitespace, and control characters with
`_`, collapses adjacent underscores, trims leading or trailing `_`, `.`,
or whitespace, and falls back to `"file"` if the result is empty. ASCII
alphanumerics, `.`, `-`, `_`, and all other Unicode letters and symbols
(CJK, emoji, accented Latin) are preserved so localized names remain
readable. Already-safe names return the same `File` reference; the chat
UI keys preview-URL, upload-state, and text-content Maps on the `File`
object, so identity must be stable.

The server's existing `chatfiles.NormalizeStoredFileName` (control-char
strip plus 255-byte truncate) is untouched. This is a client-only
hardening pass.
2026-05-18 07:07:31 +00:00
Michael Suchacz 3723f7a0c7 feat(site/src/pages/AgentsPage/components/ChatConversation): jump between user prompts via arrow buttons (#25336)
Add prev/next chevron buttons to the action row under each user message
in the agent chat transcript. Clicking jumps the scroll container to the
neighbouring user prompt's sticky sentinel (smooth scroll, no composer
mutation). Arrows disable rather than wrap when at the ends.

## Why

When a chat gets long, scrolling back to a previous prompt to see the
question that produced an answer is annoying. The transcript already has
a stable per-prompt anchor (`data-user-sentinel`) used by the
sticky-message logic, so reusing it for navigation is cheap and
consistent with the existing scroll model.

## Implementation

- `ChatMessageItem` accepts three optional props (`prevUserMessageId`,
`nextUserMessageId`, `onJumpToUserMessage`) and renders the two chevron
buttons inside the existing `message-actions` row when the message is a
user role.
- `StickyUserMessage` forwards the props to both copies of
`ChatMessageItem` (flow + sticky overlay).
- `ConversationTimeline` derives the ordered list of visible user
message IDs using the same `deriveMessageDisplayState` predicate that
controls visibility, builds a neighbour map, and supplies the jump
handler. The handler resolves the target via
`[data-user-sentinel][data-user-message-id="..."]` and smooth-scrolls
the closest `.overflow-y-auto` ancestor by the sentinel's offset
(mirroring the existing edit-flow scroll helper).
- New `data-user-message-id` attribute on the sentinel `div` to make the
lookup direct.
- New Storybook story `UserMessageJumpArrows` covers: arrow counts,
disabled-at-ends, and that clicking Next scrolls the next user sentinel
to the top of the scroller. JSDOM doesn't animate smooth scroll, so the
play function monkey-patches `scrollBy` to apply the requested top
offset synchronously.

No API, DB, or audit-table changes. Frontend only.

## Test

- `pnpm test:storybook
src/pages/AgentsPage/components/ChatConversation/ConversationTimeline.stories.tsx`
— 46 passed (incl. new story).
- `pnpm test:storybook
src/pages/AgentsPage/components/ChatConversation/` — 67 passed.
- `pnpm lint:types`, `pnpm lint:fix`, `pnpm lint:compiler`, `pnpm
format:check` — all clean.
- Local `make pre-commit` ran via the pre-commit hook on commit.

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

Plan lives at
`/home/coder/.coder/plans/PLAN-41b442d8-05bc-4b62-b1ba-155a7cef09bc.md`
in the agent workspace. Summary:

1. Add three optional props (`prevUserMessageId`, `nextUserMessageId`,
`onJumpToUserMessage`) to `ChatMessageItem` and render
`ChevronLeft`/`ChevronRight` buttons inside the existing actions row
when the message is a user role. Disable each button when its neighbour
is undefined.
2. Forward those props through `StickyUserMessage` to both
`ChatMessageItem` instances (flow + sticky overlay).
3. In `ConversationTimeline`, build the ordered list of visible user IDs
using the same `deriveMessageDisplayState` predicate, derive a neighbour
map, and implement `handleJumpToUserMessage` that looks up
`[data-user-sentinel][data-user-message-id="${id}"]`, finds the closest
`.overflow-y-auto` ancestor, and smooth-scrolls by the sentinel's
offset.
4. Add `data-user-message-id` to the sentinel so the lookup is direct.
5. Cover the behaviour with a `UserMessageJumpArrows` Storybook play
function.

</details>

---

*This PR was authored by Coder Agents on behalf of @ibetitsmike.*
2026-05-16 21:47:22 +02:00
Matt Vollmer d9976768db fix(site): copy token value from modal (#25399) 2026-05-15 16:52:55 -04:00
Jaayden Halko 2c18e07e39 feat: add theme mode dropdown (#25183)
## Summary

- Wire the Appearance settings page to the new theme mode dropdown and
sync or single theme selectors.
- Update Appearance page tests and stories for theme mode behavior.
- Update the user settings e2e test to exercise single theme selection.

## Dependencies

- Depends on #25076, #25180, #25181, and #25182.
- This PR targets helper branch `pr25077/05-theme-mode-dropdown-base`,
which contains dependency commits only, so this PR diff stays focused on
final dropdown wiring. Rebase and retarget after the dependency PRs
merge.

## Validation

- `pnpm -C site exec vitest run --project=unit
src/pages/UserSettingsPage/AppearancePage/AppearancePage.test.tsx
src/theme/themeMode.test.ts src/api/queries/users.test.ts`
- `pnpm -C site lint:types`
- `pnpm -C site storybook:ci`
- `pnpm -C site build`
- `pnpm -C site playwright:test -- e2e/tests/users/userSettings.spec.ts`
- Pre-commit hook passed on the branch commit.
2026-05-15 16:15:06 +01:00
Danielle Maywood b94cb38504 style(site): use terminal icon for shell tool (#25392) 2026-05-15 13:39:51 +00:00
Jaayden Halko 8650a2ee22 fix: rollback appearance mutation cache updates (#25182)
## Summary

- Add appearance mutation context so optimistic cache updates can be
rolled back on failure.
- Restore previous appearance settings, or remove the optimistic cache
entry when no previous data exists.
- Merge successful server responses back into the appearance cache while
preserving request fields when responses are partial.

## Dependencies

- Stacked on #25076 because the tests and generated types use the new
appearance theme mode fields.

## Validation

- `pnpm -C site exec vitest run --project=unit
src/api/queries/users.test.ts`
- `pnpm -C site lint:types`
- Pre-commit hook passed on the branch commit.
2026-05-15 10:50:29 +01:00
Jaayden Halko e8cfff40b4 feat(site): add theme mode frontend foundation (#25181)
## Summary

- Add theme mode helpers for legacy migration, active theme resolution,
draft conversion, and mode switching.
- Add `usePreferredColorScheme` and refactor `ThemeProvider` to use the
shared theme mode resolver.
- Add reusable Appearance theme picker components plus isolated
Storybook coverage.

## Dependencies

- Stacked on #25180.

## Validation

- `pnpm -C site exec vitest run --project=unit
src/theme/themeMode.test.ts src/theme/usePreferredColorScheme.test.tsx`
- `pnpm -C site lint:types`
- `pnpm -C site lint:knip`
- Pre-commit hook passed on the branch commit.
2026-05-15 10:04:48 +01:00
Yevhenii Shcherbina 238968cfa0 feat: add per-group AI budget table and endpoints (#25203)
Closes
https://linear.app/codercom/issue/AIGOV-284/add-group-budgets-table-and-crud-api

## Summary

Adds the `group_ai_budgets` table and the following endpoints:

- `GET /api/v2/groups/{group}/ai/budget`
- `PUT /api/v2/groups/{group}/ai/budget`
- `DELETE /api/v2/groups/{group}/ai/budget`

Each group may have at most one budget row. If no row exists, no budget
is enforced.

### Feature gate
  
Added `RequireFeatureMW(FeatureAIBridge)` on the `/ai/budget` sub-route.

## RBAC

Authorization reuses `rbac.ResourceGroup` with the existing
`.InOrganization(...).WithID(...)` scoping model.

The `dbauthz` wrappers load the parent `groups` row and authorize
against it.

No new resource type is introduced. As a result, anyone with
`group:update` permissions (Owner, OrgAdmin, or UserAdmin within the
organization) can manage AI budgets for that group.

## Read access for group members

`database.Group.RBACObject()` grants `policy.ActionRead` to all members
of the group through the group ACL:

```go
func (g Group) RBACObject() rbac.Object {
	return rbac.ResourceGroup.WithID(g.ID).
		InOrg(g.OrganizationID).
		// Group members can read the group.
		WithGroupACL(map[string][]policy.Action{
			g.ID.String(): {
				policy.ActionRead,
			},
		})
}
```

Because the `GET` endpoint authorizes against the same loaded `Group`
object, any group member can call:

```text
GET /api/v2/groups/{group}/ai/budget
```

`PUT` and `DELETE` remain admin-only. The group ACL grants only
`ActionRead`, so write operations continue to require role-based
`group:update` permissions.

## Alternative considered

A dedicated `rbac.ResourceGroupAiBudget` resource would allow budget
management to be separated from general group administration.

We decided not to add that complexity for now.
2026-05-14 15:54:37 -04:00
TylerandTJ d79cfcfe61 fix(site): move docs link to primary header on observability page (#25313)
Move the "Read the docs" button from the Audit Logging subsection up to
the primary Observability header's `actions` prop, matching the layout
pattern used by General, Network, and other deployment settings pages.

Also updates the docs URL from `/admin/security/audit-logs` to
`/admin/monitoring` to reflect the page-level scope.

> Generated by Coder Agents on behalf of @designertyler

---------

Co-authored-by: TJ <tracy@coder.com>
2026-05-14 13:57:01 -05:00
Danielle Maywood 68baf84b8c fix: hide empty execute tool calls (#25346) 2026-05-14 18:19:12 +01:00
Danny Kopping 841b777ccd feat: add ai_providers table, queries, dbauthz, audit, RBAC (#24892) 2026-05-14 16:10:46 +02:00
Jaayden Halko acf57b3b35 feat(site): add legacy auto sync helper and tritan cleanup (#25180)
## Summary

- Add `legacyAutoToSync` to map legacy auto-family theme preferences to
light and dark sync pairs.
- Expand colorblind theme tests for legacy auto-family handling.
- Move tritan success and git-added surfaces onto the sky-blue palette
in theme roles and CSS variables.

## Dependencies

- None. This can merge independently before the dropdown work.

## Validation

- `pnpm -C site exec vitest run --project=unit
src/theme/colorblind.test.ts`
- `pnpm -C site lint:types`
- Pre-commit hook passed on the branch commit.
2026-05-14 14:43:13 +01:00
Danielle Maywood 25a803221e feat: add shell tool display mode preference (#25029) 2026-05-14 14:25:07 +01:00
Jaayden Halko 3a070a83dd fix(site/src/pages/AgentsPage): theme diff changed-line backgrounds (#25179)
## Summary

- Update AgentsPage diff viewer CSS so changed lines use theme-aware git
added and deleted surfaces.
- Add unit coverage for the changed-line CSS variables and line-type
selectors.

## Dependencies

- None. This is independent of the theme mode API and dropdown stack.

## Validation

- `pnpm -C site exec vitest run --project=unit
src/pages/AgentsPage/components/ChatElements/tools/utils.test.ts`
- Pre-commit hook passed on the branch commit.
2026-05-14 14:11:30 +01:00
Michael Suchacz cb37047dce feat: dedicated /prompts endpoint for chat history cycle (#25083)
Follow-up to #25004. The merged change cycles only through messages
already loaded in the in-memory chat store (page size 50). Long chats
and chats whose oldest turns have rolled out of the page lose access to
their earlier prompts in the composer's up/down arrow cycle. This PR
adds a dedicated server endpoint that returns the full prompt history,
newest first, and rewires the composer to use it.

## What changed

### Endpoint

`GET /api/experimental/chats/{chat}/prompts?limit=N`

```go
type ChatPrompt struct { ID int64; Text string }
type ChatPromptsResponse struct { Prompts []ChatPrompt }
```

- `limit`: `0..2000`. `0` (the default) is treated as the server-side
default of 500; out-of-range values return `400`. Negative values are
rejected by the SDK's `PositiveInt32` parser before reaching the
handler.
- Auth: parent-chat read in `dbauthz`, mirroring
`GetChatMessagesByChatID`.
- The SQL filters `role='user'`, `deleted=false`, `visibility IN
('user','both')`, guards the lateral with `jsonb_typeof(content) =
'array'` so legacy V0 scalar-string rows are silently skipped, then
unrolls `content` JSONB with `WITH ORDINALITY` and concatenates only
`type='text'` parts in original order via `string_agg(... ORDER BY
ordinality)`. Messages whose joined text is whitespace-only are dropped
via `HAVING ... ~ '\S'` so cycling never lands on a blank entry.

### Partial index (migration `000494`)

```sql
CREATE INDEX idx_chat_messages_user_prompts
ON chat_messages (chat_id, id DESC)
WHERE deleted = false
  AND role = 'user'
  AND visibility IN ('user', 'both');
```

The partial WHERE matches the query's filter exactly and the key order
matches `ORDER BY id DESC`, so the planner gets both the filter and the
ordering from the index without a sort step.

`EXPLAIN ANALYZE` on a synthetic 51-chat × 5,000-message dataset (≈260k
rows, 10k user prompts in the target chat, `random_page_cost=1.1`):

| | Plan | Buffers hit | Time |
|---|---|---|---|
| Without index | `Index Scan Backward using chat_messages_pkey`,
**250,848 rows removed by filter** | 6,683 | 32.4 ms |
| With index | `Index Scan using idx_chat_messages_user_prompts`, no
filter | 38 | 1.3 ms |

≈25× faster, 175× fewer buffer hits.

### Frontend

- `chatPromptsKey` / `chatPromptsQuery` factories in
`site/src/api/queries/chats.ts` (`staleTime: 30s`, `enabled: chatId !==
""`, asks the server for 500 prompts).
- `ChatPageContent.tsx` replaces the in-memory derivation with
`useQuery(chatPromptsQuery(chatId ?? ""))`. The composer's existing
`cycleHistorySnapshotRef` anchors the in-flight cycle so a refetch
arriving mid-cycle cannot shift the indexed prompt out from under the
user.
- `getEditableUserMessagePayload` now concatenates user-message text
parts verbatim, mirroring the server's `string_agg(part->>'text', ''
ORDER BY ordinality)`, instead of routing through the streaming-oriented
`parseMessageContent` / `appendText` pipeline (which drops
whitespace-only chunks — correct for assistant streams, wrong for a
user's persisted message). This keeps the cycle and the edit path in
agreement on the same message. File blocks are still pulled separately
via
`parseMessageContent(...).blocks.filter(isEditableUserMessageFileBlock)`.
- Cache invalidation in `createChatMessage.onSuccess`,
`editChatMessage.onSettled`, and `useChatStore.upsertCacheMessages`
(only when an upserted message has `role === "user"`).
- Page-level stories pre-seed `chatPromptsKey(CHAT_ID)` from the same
`messagesData` to keep them offline.

## Tests

- New `TestGetChatUserPrompts` in `coderd/exp_chats_test.go` with five
subtests:
- `NewestFirstFiltering` — multi-part concatenation, non-text parts
skipped, whitespace-only filtered, soft-deleted excluded, `model`-only
visibility excluded, assistant-role excluded by `cm.role = 'user'`,
legacy V0 scalar row silently excluded by the `jsonb_typeof` guard,
ordering newest first.
- `LimitClampsResults` — explicit `limit=2` returns the two newest
prompts.
  - `InvalidLimitRejected` — `limit=5000` is `400 Bad Request`.
- `NotFoundForOtherUsers` — a separate user in the same org gets `404`,
not the prompts.
- `EmptyResultIsJSONArray` — zero-message chat and assistant-only chat
both return `Prompts: []` (non-nil, empty).
- New unit test in `messageParsing.test.ts` asserting that
`getEditableUserMessagePayload(["hello", " ", "world"])` returns `"hello
world"`, locking in the agreement with the SQL `string_agg`.
- `dbauthz_test.go` adds the
`MethodTestSuite.TestChats/GetChatUserPromptsByChatID` entry, asserting
parent-chat `policy.ActionRead`.
- `pnpm test src/pages/AgentsPage` — 1159 passed, 2 skipped.
- `make gen` produces no diff.

## Manual verification

Seeded a dev chat with Claude Sonnet 4.6 via the aibridge Anthropic
provider and posted 20 user prompts end-to-end. Verified that the
`/prompts` endpoint returns 20 rows newest-first, that `limit=10` clamps
correctly, that `limit=0` uses the server default of 500, and that the
up/down keyboard cycle in the composer walks the same sequence (and
reverses correctly back to the empty draft).

## Out of scope

- Cross-chat history.
- Per-user opt-out for the cycle.
- File-reference / attachment cycling — the cycle continues to reproduce
plain text only, by design.

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

# CODAGT-319 Follow-up — Dedicated `/prompts` endpoint

## Context

The merged feature ([#25004](https://github.com/coder/coder/pull/25004)
/ [d32842f](https://github.com/coder/coder/commit/d32842f)) cycles only
through messages already loaded in the in-memory chat store, which is
capped at the first 50 messages of the current page. Long chats and
chats whose oldest turns have rolled out of the page can no longer
recall their full prompt history. This follow-up exposes a dedicated
server endpoint that returns the user-authored prompts in a chat, newest
first, and rewires the composer to use it.

## Design

### Endpoint

`GET /api/experimental/chats/{chat}/prompts?limit=N`

Returns:

```go
type ChatPrompt struct {
    ID   int64
    Text string
}
type ChatPromptsResponse struct {
    Prompts []ChatPrompt
}
```

- `limit`: `0..2000`. `0` (the default) → server-side default of 500.
The wire-level default is encoded in SQL as `COALESCE(NULLIF($limit, 0),
500)`. Negatives are rejected upstream by `PositiveInt32`; the handler
only caps the upper bound.
- Auth: parent-chat read in `dbauthz`, mirroring
`GetChatMessagesByChatID`.
- Listed under the experimental router so we can iterate without API
guarantees.

### SQL

The query lives in `coderd/database/queries/chats.sql` as
`GetChatUserPromptsByChatID`:

- Filters `role='user'`, `deleted=false`, `visibility IN
('user','both')` to mirror the composer's "what the user actually typed
and can re-send" contract.
- Guards the lateral with `jsonb_typeof(content) = 'array'` so legacy V0
rows whose content is a scalar JSON string (predates migration `000434`)
are silently excluded instead of raising `"cannot extract elements from
a scalar"`.
- Unrolls `content` JSONB with `jsonb_array_elements WITH ORDINALITY`
and concatenates only `type='text'` parts, preserving original order via
`string_agg(... ORDER BY ordinality)`.
- Casts the result to `text` so sqlc emits a `string` field instead of
`[]byte`.
- Drops whitespace-only prompts via `HAVING string_agg(...) ~ '\S'` so
cycling never lands on a blank entry.
- Orders by `cm.id DESC` (`id` is a sequence, so this is "newest first"
without relying on `created_at`).

### Index

New partial index added in migration `000494`:

```sql
CREATE INDEX idx_chat_messages_user_prompts
ON chat_messages (chat_id, id DESC)
WHERE deleted = false
  AND role = 'user'
  AND visibility IN ('user', 'both');
```

The partial WHERE clause matches the query's filter exactly, so the
planner can use the index for both filtering and ordering without a sort
step.

### Frontend

- `chatPromptsKey(chatId)` and `chatPromptsQuery(chatId)` factories in
`site/src/api/queries/chats.ts`. `staleTime: 30s`, `enabled: chatId !==
""`. Asks the server for 500 prompts (well below the 2000 max, plenty
for the cycle).
- `ChatPageContent.tsx` replaces the in-memory derivation with
`useQuery(chatPromptsQuery(chatId ?? ""))`. The composer's
`cycleHistorySnapshotRef` already takes a stable snapshot at cycle
entry, so a refetch arriving mid-cycle cannot shift the indexed prompt
out from under the user.
- `getEditableUserMessagePayload` extracts the edit-path text from raw
user-message parts (filter `type === "text"`, join verbatim) instead of
going through `parseMessageContent` / `appendText`, which is built for
assistant streams and intentionally drops whitespace-only chunks.
Without this, cycling and clicking Edit on the same message could
produce different draft text for messages with whitespace-only
interleaved text parts.
- Cache invalidation: `createChatMessage.onSuccess`,
`editChatMessage.onSettled`, and `useChatStore.upsertCacheMessages`
(when at least one upserted message has `role === "user"`) all
invalidate `chatPromptsKey(chatId)`.

### Tests

- `TestGetChatUserPrompts` (`coderd/exp_chats_test.go`) covers:
- `NewestFirstFiltering` — multi-part concatenation, non-text parts
skipped, whitespace-only filtered, soft-deleted excluded, `model`-only
visibility excluded, assistant-role excluded by `cm.role = 'user'`,
legacy V0 scalar row silently excluded by the `jsonb_typeof` guard,
ordering newest first.
- `LimitClampsResults` — explicit `limit=2` returns the two newest
prompts.
  - `InvalidLimitRejected` — `limit=5000` is `400 Bad Request`.
- `NotFoundForOtherUsers` — a separate user in the same org gets `404`,
not the prompts.
- `EmptyResultIsJSONArray` — zero-message chat and assistant-only chat
both return `Prompts: []` (non-nil, empty).
- `messageParsing.test.ts` adds a unit test asserting that
`getEditableUserMessagePayload(["hello", " ", "world"])` returns `"hello
world"`, locking in the agreement with the SQL `string_agg`.
- `dbauthz_test.go` adds the
`MethodTestSuite.TestChats/GetChatUserPromptsByChatID` entry, asserting
the parent-chat `policy.ActionRead`.

## Out of scope

- Cross-chat history.
- Per-user opt-out for the cycle.
- File-reference / attachment cycling — the cycle still reproduces plain
text only, by design.

</details>

<details>
<summary>coder-agents-review history</summary>

Four review rounds, eight unique findings, all addressed in this PR
(approved twice). Rebased onto `main` twice after R4: first to pick up
new migrations `000491` / `000492`, then again for
`000493_idx_chat_diff_statuses_url_lower`. The prompts-index migration
was renumbered `000491 → 000493 → 000494` via
`coderd/database/migrations/fix_migration_numbers.sh`; no other diff
changes.

| Round | Head | Outcome |
|---|---|---|
| R1 | `725422ab` | `COMMENTED` — 7 findings (DEREM-1..7) |
| R2 | `ab2a8936` | `COMMENTED` — 1 new (DEREM-10) + 1 reraised
(DEREM-5) |
| R3 | `648c5d1f` | **`APPROVED`** — 7 fixed, DEREM-5 deferred via
#25125 |
| R4 | `93b6f450` | **`APPROVED`** — DEREM-5 also fixed in-PR, #25125
closed |

| ID | Where | Resolution |
|---|---|---|
| DEREM-1 | `chats.sql` | Added `jsonb_typeof(content) = 'array'` guard
against V0 scalar rows |
| DEREM-2 | `exp_chats.go` | Removed dead `limit < 0` branch (SDK
rejects upstream) |
| DEREM-3 | `useChatStore.ts` | Rewrote misleading invalidation comment
|
| DEREM-4 | `exp_chats_test.go` | `NewestFirstFiltering` now inserts an
assistant-role message so the `role='user'` filter is exercised
end-to-end |
| DEREM-5 | `messageParsing.ts` | Rewrote
`getEditableUserMessagePayload` to concatenate text parts verbatim,
mirroring the SQL `string_agg` |
| DEREM-6 | `exp_chats.go` | Tightened swagger doc + error message to
spell out the 0–2000 range |
| DEREM-7 | `exp_chats_test.go` | Added `EmptyResultIsJSONArray` subtest
|
| DEREM-10 | `exp_chats_test.go` | `NewestFirstFiltering` now inserts a
raw V0 scalar-content row; verified locally that removing the guard
makes the test fail |

</details>

---

This PR was created on behalf of @ibetitsmike by Coder Agents.
2026-05-14 12:43:12 +02:00
Danielle Maywood f7d1ecaece refactor(site): inline single-use class string constant in AgentSetupNotice (#25086) 2026-05-14 10:18:50 +01:00