`Test_ProxyServer_Headers` never passed `--http-address`, so it bound to
the default `127.0.0.1:3000`.
`TestWorkspaceProxy_Server_PrometheusEnabled`
used `RandomPort(t)` for `--http-address` (a drive-by from #14972 which
was
fixing the Prometheus port).
Both now use `--http-address :0`. `ConfigureHTTPServers` calls
`net.Listen("tcp", ":0")` and holds the listener open, so there is no
TOCTOU window. Neither test connects to the HTTP listener, so the
assigned port is irrelevant. This matches `cli/server_test.go` where
`:0` is used throughout.
go-git has bugs in gitignore logic. With more complex gitignores, some
paths that should be ignored aren't. That caused extra, unexpected files
to appear in the git diff panel.
If the git cli isn't available in a workspace, the /git/watch endpoint
will still allow the frontend to connect, but no git changes will ever
be transmitted.
Split from #22693 per review feedback.
Fixes multiple bugs in coderd/chatd and sub-packages including race
conditions, transaction safety, stream buffer bounds, retry limits, and
enterprise relay improvements.
See commit message for full list.
Split from #22693 per review feedback.
Fixes SSE error handling and adds WebSocket reconnection with
exponential backoff to the AgentsPage chat list watcher.
The Button base styles apply `[&>svg]:p-0.5` (2px padding) to child
SVGs. In the small `size-7` rounded attach button, this extra padding
shifts the 16x16 ImageIcon off-center. Override with `[&>svg]:p-0` to
remove it.
On small viewports (below `xl`) the git/changes panel was expanding as a
bottom sheet. This changes it to always appear from the right side:
- **Mobile (<`sm`/640px):** Panel opens full-width (`w-[100vw]`) as a
right-side overlay
- **`sm`+ (640px+):** Panel uses the persisted width (`--panel-width`)
with min 360px / max 70vw, drag handle enabled
- Parent flex container is always `flex-row` instead of `flex-col
xl:flex-row`
### Changes
- `AgentDetail.tsx`: Removed `flex-col xl:flex-row` responsive switch,
always uses `flex-row`
- `RightPanel.tsx`: Replaced bottom-sheet layout (`h-[42dvh]`) with
right-side panel at all breakpoints. Full viewport width below `sm`,
resizable width at `sm`+. Drag handle activates at `sm` instead of `xl`.
This change adds support for image attachments to chat via add button
and clipboard paste. Files are stored in a new `chat_files` table and
referenced by ID in message content. File data is resolved from storage
at LLM dispatch time, keeping the message content column small.
Upload validates MIME types via content type or content sniffing against
an allowlist (png, jpeg, gif, webp). The retrieval endpoint serves files
with immutable caching headers. On the frontend, uploads start eagerly
on attach with a background fetch to pre-warm the browser HTTP cache so
the timeline renders instantly after send.
This change adds git hooks and Makefile targets that mirror CI required
checks locally, catching issues before they reach CI.
This is for use by AI agents (documented in AGENTS.md).
- **pre-commit** (every commit): gen, fmt, lint, typos, slim binary
build. Fast checks without Docker or Playwright.
- **pre-push** (before push): full CI suite including site build, tests,
sqlc-vet, offlinedocs.
To use:
```sh
git config core.hooksPath scripts/githooks
```
Works in worktrees (where `.git` is a file). Bypass with `--no-verify`.
Replaces the single-purpose PR diff right panel with a tabbed sidebar
that shows both the existing PR diff and real-time git repository
changes from the workspace agent.
There's an accompanying backend PR
[here](https://github.com/coder/coder/pull/22565).
https://github.com/user-attachments/assets/bbd53f1c-d753-4574-a159-6dad5989e5e3
## Backend surface
One endpoint drives this feature:
- **`WS /api/experimental/workspaceagents/{id}/git/watch`** —
bidirectional WebSocket. The client sends `refresh` messages; the agent
responds with `changes` messages containing per-repo branch and unified
diff. The workspace agent also automatically pushes changes as they
occur in the workspace.
Pins the `coder/coderd` Terraform provider in `dogfood/main.tf` to `>=
0.0.13`.
Previously there was no version constraint at all. The latest release
[v0.0.13](https://github.com/coder/terraform-provider-coderd/releases/tag/v0.0.13)
includes:
- `workspace_sharing` attribute on `coderd_organization` resource
- `cors_behavior` support on template resource
- Dependency updates (coder/coder SDK bumped to v2.29.2, various
Terraform plugin framework updates)
---------
Co-authored-by: blink-so[bot] <211532188+blink-so[bot]@users.noreply.github.com>
## Summary
This PR adds a follow-up flow for paused Tasks so users can submit
another prompt as part of resuming the same task/session.
https://github.com/user-attachments/assets/eabe5e91-704c-44ad-9e28-39f55e6c5923
## What changed
- **Task page UX**
- Added a new **Follow-up** action in paused task state.
- Added `FollowUpDialog` to collect and submit follow-up input.
- **Follow-up flow behavior**
- If task is paused/stopped: call resume first, then wait for polling to
observe task `active`, then send input.
- Dialog closes itself after successful send.
- Added clear error handling for:
- resume failure
- build failure/canceled while resuming
- send failure
- **Stable API route parity**
- Added `POST /tasks/{user}/{task}/pause` and `POST
/tasks/{user}/{task}/resume` to the stable `/api/v2/tasks` router block.
- **SDK alignment**
- Updated `codersdk` pause/resume methods to use stable
`/api/v2/tasks/...` endpoints instead of `/api/experimental/...`.
- **Frontend API/query alignment**
- `site` task pause/resume/send paths are on stable `/api/v2/tasks/...`.
- Updated task query helpers accordingly.
- **Storybook coverage**
- Added follow-up dialog stories for key states:
- open dialog
- active direct send
- auto-resume then send
- resuming progress visible
- resume build failure
- send failure
- empty message disabled
- Added/updated mocks for task logs and new follow-up flows.
Closes https://github.com/coder/internal/issues/1269
## Problem
Bootstrap scripts under `provisionersdk/scripts/` are inlined into
templates via `sh -c '${init_script}'`. Any single quote (apostrophe) in
these `.sh` files silently breaks the shell quoting, causing the agent
to never start — with near-invisible error output.
## Changes
- **`scripts/check_bootstrap_quotes.sh`** — new lint script that scans
all `.sh` files under `provisionersdk/scripts/` for single quotes and
fails with a clear error if any are found. Only checks shell scripts
(not `.ps1`, which legitimately uses single quotes).
- **`Makefile`** — added `lint/bootstrap` target wired into the `lint`
dependency list.
Fixes#22062
We now use Linear for Triaging
<!--
If you have used AI to produce some or all of this PR, please ensure you
have read our [AI Contribution
guidelines](https://coder.com/docs/about/contributing/AI_CONTRIBUTING)
before submitting.
-->
Adds real-time git status watching for workspace agents, so the frontend
can subscribe over WebSocket and show
git file changes in near real-time.
1. Subscription is scoped to a **chat** via `GET
/api/experimental/chats/{chat}/git/watch`.
2. The workspace agent automatically determines which paths to watch
based on tool calls made by the chat (and its ancestor chats).
3. Workspace agent polls subscribed repo working trees on a 30s
interval, on tools calls, and on explicit `refresh` from the client.
4. Scans are rate-limited to at most once per second.
5. Edited paths are tracked **in-memory** inside the workspace agent.
There is no database persistence — state is lost on agent restart. This
will be addresses in a future PR.
6. Messages sent over WebSocket include a full-repo snapshot (unified
diff, branch, origin). A new message is emitted only when the snapshot
changes.
This PR was implemented with AI with me closely controlling what it's
doing. The code follows a plan file that was updated continuously during
implementation. Here's the file if you'd like to see it:
[project.md](https://gist.github.com/hugodutka/8722cf80c92f8a56555f7bc595b770e2).
It reflects the current state of the PR.
`time.Now()` has nanosecond precision while Postgres timestamps are
microsecond precision. When tests compare `time.Now()` against
DB-sourced timestamps using `Before`/`After`/`WithinRange`/etc., there
is a non-zero flake risk from the precision mismatch.
This replaces `time.Now()` with `dbtime.Now()` (which rounds to
microsecond precision) in all test assertions that compare against
database timestamps.
Follows from #22684.
## Changes (11 files)
| File | Changes |
|---|---|
| `coderd/apikey_test.go` | 11 comparisons with `ExpiresAt` |
| `coderd/users_test.go` | 2 comparisons with `ExpiresAt` |
| `coderd/oauth2_test.go` | 1 comparison with `token.Expiry` |
| `coderd/workspaces_test.go` | 2 comparisons with `DormantAt` |
| `coderd/workspaceagents_test.go` | 3 comparisons with
`ConnectedAt`/`DisconnectedAt` |
| `coderd/workspaceapps/db_test.go` | 1 comparison with `token.Expiry` |
| `coderd/provisionerdserver/provisionerdserver_test.go` | 1 comparison
with `key.ExpiresAt` |
| `enterprise/coderd/workspaces_test.go` | 1 comparison with `DormantAt`
|
| `enterprise/coderd/license/license_test.go` | 3 `NotBefore` values |
| `enterprise/coderd/licenses_test.go` | 2 `NotBefore` values |
| `enterprise/coderd/users_test.go` | 3 `Next()` comparisons |
## Not changed (intentionally)
- `scaletest/placebo/run_test.go` — compares wall-clock elapsed time,
not DB timestamps
- `cli/server_test.go`, `coderd/jwtutils/jwt_test.go`,
`enterprise/aibridgeproxyd/aibridgeproxyd_test.go` — TLS cert fields,
not DB-stored
- `coderd/azureidentity/azureidentity_test.go` — Azure cert expiry, not
DB
🤖 Generated by Claude Opus 4.6 but reviewed manually.
This PR does three things:
- Exports derp expvars to the pprof endpoint
- Exports the expvar metrics as prometheus metrics in both coderd and
wsproxy
- Updates our tailscale to a fix I also had to make to avoid a data race
condition
I generated this with mux but I also manually tested that the metrics
were getting properly emitted
## Problem
When creating a new chat from the `/agents` page and navigating back,
the initial prompt was still displayed. The draft text persisted in
`localStorage` (`agents.empty-input`) even after the chat was
successfully created.
### Root cause
After `handleSend` synchronously clears the localStorage key, the React
re-render (caused by `isCreating` flipping to `true`) triggers Lexical's
`ContentChangePlugin`, which fires `handleContentChange` with the old
editor content — re-writing the draft back to localStorage before
navigation occurs.
## Fix
- Extract the draft persistence logic into a `useCreatePageDraft` hook
with a `sentRef` guard
- Once `markSent()` is called, `handleContentChange` skips all
localStorage writes
- This prevents the Lexical editor's change events from re-persisting
the draft during the async gap between send and navigation
## Testing
Added 6 unit tests for `useCreatePageDraft`, including a regression test
that reproduces the exact bug scenario (calling `handleContentChange`
with old content after `markSent`).
Adds a docs page under /docs/ai-coder/agents describing our philosophy
on platform team control over agent behavior: admin-level configuration,
zero developer options, enforcement over defaults. Covers what's
available today (providers, models, system prompt, template routing) and
where we're headed (usage analytics, infra-level enforcement, tool
customization).
To allow tasks to be shareable, we need to share both the `task`
resource and the `workspace` resource, and their sharing state needs to
be kept in sync. We've already implemented all of the necessary ACL
functionality for workspaces, so we can just sort of proxy those ACLs
back to the task as well.
Follow-up to #22612. Running `git status --short` in a loop during `make
-B -j gen` still showed intermediate states for several files. This PR
fixes the remaining ones.
The main issues:
- `generate.sh` ran `gofmt` and `goimports` in-place after moving files
into the source tree. Now it formats in a workdir first and only `mv`s
the final result.
- `protoc` targets wrote directly to the source tree. Wrapped with
`scripts/atomic_protoc.sh` which redirects output to a tmpdir.
- Several generators used hardcoded `/tmp/` paths. On systems where
`/tmp` is tmpfs, `mv` degrades to copy+delete. Switched to a
project-local `_gen/` directory (gitignored, same filesystem).
- `apidoc/.gen` and `cli/index.md` used `cp` for final output. Replaced
with `mv`.
- `manifest.json` was written twice (unformatted, then formatted). Now
`.gen` writes to a staging file and the manifest target does one
formatted atomic write.
- `biome_format.sh` silently skipped files in gitignored dirs. Added
`--vcs-enabled=false`.
Two helpers reduce the Makefile boilerplate: `scripts/atomic_protoc.sh`
(wraps protoc) and an `atomic_write` Make define
(stdout-to-temp-to-target pattern). `.PRECIOUS` now also covers `.pb.go`
and mock files.
Verification: `make -B -j gen` x3 with `git status` polling, no changes.
Refs #22612
When a git change has zero additions and zero deletions (like adding a
binary file/image), the diff stats were hidden entirely because of
`additions === 0 && deletions === 0` early-return guards.
This changes the behavior so that `+0 -0` is always rendered when there
are changed files, ensuring visibility in both the sidebar and the Git
tab.
### Changes
**`DiffStats.tsx`**
- `DiffStatNumbers`: Removed the `null` early return — always renders
both `+N` and `−N` counters.
- `DiffStatBadge`: Now only returns `null` when there are no changed
files AND both counts are zero. Always renders both pills.
- `DiffStatsInline`: Same guard — shows `+0 −0` clickable stats when
files changed but lines are zero.
**`AgentsSidebar.tsx`**
- `hasLineStats` now also checks `changedFiles > 0`, so the sidebar
entry shows `+0 -0` for binary-only diffs.
- Removed the `additions > 0` / `deletions > 0` conditional wrappers —
both values are always rendered.
The `--parameter-default` value is now used to pre-select the default option for a coder parameter
with option blocks when prompting interactively in CLI.
Related to: https://github.com/coder/coder/issues/22078
Closes#22140
Short simple and sweet PR to add a bunch of details to our `<Alert />`
stacks. This means we aren't simply asking the user to read the
developer console and surface things easier.
- Implement `Response data` and `Stack trace` `<details />`
- Fix overflow in `ErrorAlert` debug accordions so long `Response data`
and `Stack Trace` content stays inside the alert.
- Add horizontal scroll wrappers around both `<pre>` blocks used in
debug details.
- Update `Alert` layout with `min-w-0` on flex containers so nested
content can shrink correctly and internal scrolling works as intended.
<img width="739" height="550" alt="preview-validation"
src="https://github.com/user-attachments/assets/a6f890d3-8f1f-4fd6-b9d0-882838db04a4"
/>
The `apple-touch-icon.png` had weird padding/cropping that looked wrong
when added to the iOS home screen. This replaces it with a 180×180
resize of the canonical `pwa-icon-512.png` so the icon matches the PWA
icon exactly — no extra padding.
## Summary
When a chat's workspace is stopped, the LLM previously had no way to
start it — `create_workspace` would either create a duplicate workspace
or fail. This adds a dedicated `start_workspace` tool to the agent flow.
## Changes
### New: `start_workspace` tool
(`coderd/chatd/chattool/startworkspace.go`)
- Detects if the chat's workspace is stopped and starts it via a new
build with `transition=start`
- Reuses the existing `waitForBuild` and `waitForAgent` helpers (shared
logic)
- Shares the workspace mutex with `create_workspace` to prevent races
- Idempotent: returns immediately if the workspace is already running or
building
- Returns a `no_agent` / `not_ready` status if the agent isn't available
yet (non-fatal)
### Updated: `create_workspace` stopped-workspace hint
- `checkExistingWorkspace` now returns a `stopped` status with message
`"use start_workspace to start it"` when it detects the chat's workspace
is stopped, instead of falling through to create a new workspace
### Wiring
- `chatd.Config` / `chatd.Server`: new `StartWorkspace` /
`startWorkspaceFn` field
- `coderd/chats.go`: new `chatStartWorkspace` method that calls
`postWorkspaceBuildsInternal` with proper RBAC context
- `coderd/coderd.go`: passes `chatStartWorkspace` into chatd config
- Tool registered alongside `create_workspace` for root chats only (not
subagents)
### Tests (`startworkspace_test.go`)
- `NoWorkspace`: error when chat has no workspace
- `AlreadyRunning`: idempotent return for workspace with successful
start build
- `StoppedWorkspace`: verifies StartFn is called, build is waited on,
and success response returned
## Problem
When `coder ssh --stdio` checks for Coder Connect availability, it
constructs a hostname like `agent.workspace.owner.coder` and performs a
DNS AAAA lookup via `ExistsViaCoderConnect`. Without a trailing dot,
this hostname is not a fully-qualified domain name (FQDN), so the system
DNS resolver appends each configured search domain before querying.
Go's pure-Go DNS resolver (used when `CGO_ENABLED=0`, which is the
default for CLI builds) does **not** stop after getting NXDOMAIN on the
first name. It tries all names in the search list sequentially:
1. `agent.workspace.owner.coder.` → NXDOMAIN (fast)
2. `agent.workspace.owner.coder.corp.example.com.` → timeout
3. `agent.workspace.owner.coder.internal.company.com.` → timeout
On corporate networks where the search-domain-expanded queries hit DNS
infrastructure that drops rather than responds (common for nonsensical
hostnames with deep subdomain chains), each expanded query hits the full
DNS timeout (default 5s × 2 attempts = 10s per name). With 2-3 search
domains, this compounds to 20-30+ seconds of blocking.
## Fix
Adding a trailing dot marks the hostname as an FQDN. Go's `nameList()`
in `src/net/dnsclient_unix.go` returns a single-entry list for rooted
names, completely bypassing search domain expansion.
This is consistent with how `IsCoderConnectRunning` already handles its
DNS check — `tailnet.IsCoderConnectEnabledFmtString` includes a trailing
dot for exactly this reason.
## Verification
Tested with a fake DNS server that responds with NXDOMAIN for `.coder`
queries but drops search-domain-expanded queries:
| Hostname | Time | Queries sent |
|---|---|---|
| `main.workstation.kevin.coder` (no trailing dot) | **~15s** | 4 (as-is
+ 3 search domains) |
| `main.workstation.kevin.coder.` (trailing dot) | **<1ms** | 1 (FQDN
only) |
Closes https://github.com/coder/coder/issues/22581
_Generated by [mux](https://github.com/coder/mux) but reviewed by a
human_
## Problem
The chat input loses focus after sending a message. Users have to click
back into the input field to type their next message.
## Root Cause
When a message is sent:
1. `handleSubmit` in `AgentChatInput` calls `onSend(text)` then
immediately `focus()` — but this is premature
2. The async send sets `isLoading=true`, which disables the editor via
`EditableStatePlugin`
3. When the send resolves, `handleSendFromInput` calls `clear()` and
`focus()` — but the editor may still be disabled at this point (React
hasn't re-rendered yet)
4. When React re-renders with `isLoading=false`, the editor becomes
editable again but nobody restores focus
## Fix
Added a `useEffect` in `AgentChatInput` that watches for `isLoading`
transitioning from `true` to `false` and calls `focus()` on the editor.
This ensures focus is restored *after* React has re-enabled the editor,
not prematurely.
## Test
Added a test in `AgentDetail.test.ts` verifying that `focus()` is called
on the input ref after `handleSendFromInput` resolves.