Commit Graph
2747 Commits
Author SHA1 Message Date
TJ 97cb722fb8 fix: show no budget instead of unlimited for empty group AI budget (#27993)
An empty per-member AI budget on the group settings page previously
displayed "unlimited budget" with a "Members in this group have no
spending cap." alert. Unlimited spend only applies when the everyone
group is the sole group, so this messaging was misleading everywhere
else.

## Changes

- Empty budget now shows "This group has **no budget** set. View docs"
with no info alert. "View docs" links to [Effective group
resolution](https://coder.com/docs/ai-coder/ai-gateway/cost-controls#effective-group-resolution)
using the versioned `docs()` helper.
- Removed the "Members in this group have no spending cap." alert
entirely.
- Input placeholder changed from `unlimited` to `no budget`.
- Updated the `AIBudgetUncapped` story expectations to match.

## Unchanged

- Entering an explicit `$0` still shows the "A $0 limit disables AI
access for this group." alert, as before.
- Saving with an empty field still sends `null` to the budget API;
backend semantics are untouched.

Story tests pass: `pnpm test:storybook
src/pages/GroupsPage/GroupSettingsPageView.stories.tsx` (7/7).

---

*This PR was generated by Coder Agents on behalf of @tracyjohnsonux.*
2026-08-19 06:15:06 -07:00
Paweł Banaszewski 63641b98c8 fix: treat a missing serve endpoint as a fatal dial error (#27864)
Adds 404 as a terminal error for establishing DRPC connection.

A standalone AI Gateway pointed at a coderd that does not expose
`/api/v2/ai-gateway/serve` gets a 404, which the connect loop classified
as transient and retried forever. Redialing cannot fix a missing
endpoint.
404 now is treated as terminal handshake failure. `--url` is expected to
point directly at coderd, so a 404 from an intermediary is not
distinguished.

Refs https://linear.app/codercom/issue/AIGOV-320/write-connection-tests

---

Generated with Coder Agents.
2026-08-19 13:19:20 +02:00
Atif Ali 34e95c46bf docs: note JetBrains client attribution in AI Gateway (#28296)
Noticed while testing Junie that JetBrains AI Assistant sends
`User-Agent: ktor-client`, the stock Ktor default, so AI Gateway records
these sessions as `Unknown`. Admins who enable Gateway for governance
won't see their JetBrains attributed in the sessions view or the
`client` filter.

Documents the behavior on the JetBrains client page. Nothing to fix on
our side, matching `ktor-client` would misattribute any other Ktor-based
application.
2026-08-19 15:32:23 +05:00
Paweł Banaszewski a441b03d70 feat: add yaml config option to standalone AI gateway (#28258)
`coder ai-gateway start` now accepts a `--config` / `-c` flag (and
`CODER_CONFIG_PATH`) to load configuration from a YAML file.
2026-08-19 10:07:22 +00:00
Matt Vollmer ddcffd8248 docs: remove inaccurate Agent Firewall filesystem protection claim (#28286) 2026-08-19 06:44:34 +02:00
Jon Ayers 821d91fabd fix: log tailnet tunnel authorization decisions (#27819) 2026-08-18 18:21:50 -05:00
Bobby Ho 166d92ba73 fix: bound request body size on JSON API endpoints (#28168)
## Summary

`httpapi.Read` decoded request bodies with no size limit, so a single
request could allocate memory without bound. This adds a 4 MiB default
ceiling, leaves the endpoints that legitimately need more explicitly
exempted, and counts the rejections so a limit set too tight is visible.

This is the first of three PRs split out of #28048, covering the
endpoints that answer in `codersdk.Response` shape. The OAuth2 decode
paths (RFC 6749, RFC 7591) and the SCIM ones (RFC 7644) answer in their
own error shapes and follow in separate PRs, along with the lint rule
that pins the invariant.

Closes PLAT-463. Remediates SEC-416 (CWE-770, CVSS 7.5) and SEC-392.

## Problem

`httpapi.Read` calls `json.NewDecoder(r.Body).Decode(value)` with no
ceiling, and no middleware in the chain bounds body size. The exposure
is pre-authentication: login, OTP, and first-user creation all read a
body before any authorization decision is reached. The existing rate
limiter bounds request *rate*, which is orthogonal to the memory a
single admitted request may consume.

## Fix

`Read` is split into `Read` and `ReadLimit`. `ReadLimit` wraps `r.Body`
in an `http.MaxBytesReader` and keeps the existing decode and validate
logic; `Read` delegates to it with a new `DefaultMaxRequestBodyBytes` of
4 MiB, which covers the 124 remaining non-test callers at a single site.

`http.MaxBytesReader` composes as tightest-wins, so the handlers that
pre-wrapped their own bodies pass their limit to `ReadLimit` rather than
wrapping, and each keeps its previous ceiling byte for byte. That
matters most for the bulk secrets import at `8 * MaxSecretsFileBytes`:
an unconditional wrap inside `Read` would have silently halved it to the
default. `TestImportUserSecretsBodyLargerThanDefaultLimit` is the
regression guard for that specific failure, and
`TestMaxBytesReaderNesting` pins the composition behavior the whole
requirement rests on.

Every rejection site calls `httpapi.RecordRequestBodyLimit`, which names
the limit that tripped on the request's existing log line and marks the
request so `coderd_api_requests_too_large_total{reason="request_body"}`
counts body rejections apart from the 413s coderd answers for other
causes, such as agent log storage overflow. A limit set too tight for a
legitimate payload therefore surfaces without waiting for a user report.

The limit is a constant rather than a deployment option: an operator
raising it to unblock something would reopen the vulnerability as
configuration, where a security scan will not find it. A legitimate 413
is answered with a targeted `ReadLimit` on that endpoint.

## Behavior change

`POST /api/v2/files` now answers 413 rather than 400 when a request body
exceeds `HTTPFileMaxBytes`. It installed that bound already but reported
the rejection as a read failure, which leaked the stdlib `http: request
body too large` string through `Detail` and kept the largest limit in
the tree off the metric. The separate 413 for an oversized expanded
archive is unchanged.

The task log snapshot endpoint now answers 413 rather than 400 when its
64 KiB cap is exceeded. Routing it through `ReadLimit` also changes its
decode-failure message from "Failed to decode request payload." to
"Request body must be valid JSON.", which is what every other endpoint
answers. Its tests are updated to match both.

`coderd_api_requests_too_large_total` is new, so there is no existing
query to migrate. It counts the 413s coderd answers, labeled `method`,
`path`, and `reason`. `reason="request_body"` is a rejection by one of
the limits above; `reason="other"` is a 413 that has nothing to do with
body size, such as agent log storage overflow.

## Reading this

The commits are ordered to be read in sequence. Commits 1 and 2 are the
security fix; commits 3 to 5 are the observability consequences, and
commit 3 is the one that touches dashboards. Commit 7 documents the
limit on the REST API reference index. Commits 6 and 8 add and revert an
exhaustive `@Failure 413` annotation pass, which buried the fix under
its regenerated swagger, and cancel out.
2026-08-18 12:54:45 -07:00
Matt VollmerandNick Vigilante 5f6eeda588 docs: add Licensing & Usage page and reorder agents manifest (#28263)
Adds a new Licensing & Usage page under `docs/ai-coder/agents/` covering
the difference between Community and AI Premium licenses, how Agent Time
is measured, and what happens when concurrency or usage limits are
reached.

Reorders the Coder Agents section in `docs/manifest.json` to the
following sequence: Getting Started, Architecture, Platform Controls,
Extending Agents, Models, Tools, Chat Sharing, Search Syntax, Licensing
& Usage, Tasks to Chats API Migration.

---

PR generated with Coder Agents

---------

Co-authored-by: Nick Vigilante <nickvigilante@users.noreply.github.com>
2026-08-18 15:19:26 -04:00
Mathias FredrikssonandMichael Suchacz d3f08b1983 feat: audit chat system instructions changes (#27668)
Adds an audit record for administrative events on the deployment-wide
chat instruction settings (system prompt, the include-default toggle,
and the plan-mode instructions), per CODAGT-719 and operator decision
D5. Each endpoint records under a stable identity: resource type
`chat_instruction_settings`, a fixed resource ID and a human-readable
target ("System prompt", "Plan mode instructions"), so two changes to
one setting share an ID and history-by-setting works. A real change
exports a Write entry with the old-to-new text visible; a
value-identical PUT still upserts and still returns 204 but records
nothing.

Attempts are recorded, not only transitions. Identity is assigned before
the authorization check, so a denied PUT exports a 403 row with an empty
diff (no request content reaches it), a validation failure exports a 400
row, and a write failure exports a 500 row, each with an empty diff; an
operator can tell "nothing changed" from "something changed and capture
degraded" by the status code.

The write path stays authoritative. The advisory lock and, on plan-mode,
the transaction exist only to serve change-detection; if any of that
machinery fails (lock, begin, commit, rollback), the handler runs main's
idempotent write path directly and derives the response from it, so a
member-visible failure of audit-only infrastructure can never replace
main's successful response. Accepted consequence: when the lock cannot
be taken, two concurrent identical writes can produce two rows instead
of one. That is audit degradation, which is allowed; changing a member's
response is not. Write failures keep the exact response the endpoint
produced before this wiring (transaction error for the system prompt,
which was always transactional; the raw write error for plan mode, which
was not), and the full transaction error is logged so rollback failures
cannot vanish.

<details>
<summary>CODAGT-66 plan entry: S1 (verbatim)</summary>

**S1 `feat: audit chat system instructions changes`** (CODAGT-719; base:
main)

- Struct: `database.ChatSystemPromptSettings{ID uuid.UUID; SystemPrompt
string; IncludeDefaultSystemPrompt bool; PlanModeInstructions string}`
in `coderd/database/types.go` (ticket-sketched shape; one struct, both
endpoints).
- Registration: union entry (diff.go), table.go entry (`id`
ActionIgnore, other three ActionTrack), `AuditActionMap` Write-only;
four request.go cases (`ResourceTarget` "", `ResourceID` from struct,
`ResourceType` new enum value `chat_system_prompt_settings`,
`ResourceRequiresOrgID` false with the "Artificial ID / deployment
singleton" comment convention).
- Migration: `ALTER TYPE resource_type ADD VALUE IF NOT EXISTS
'chat_system_prompt_settings';` comment-only no-op down (000558 shape);
number picked at push per the numbering constraint.
- codersdk: constant + prose `FriendlyString` ("chat system prompt
settings"); `TestAuditDBEnumsCovered` forces both. `coderd/audit.go`
presentation switches: rely on safe defaults (no link, generic
description); no FE changes (filter label falls back to capitalized
value; acceptable per precedent).
- Wiring `putChatSystemPrompt` and `putChatPlanModeInstructions`:
InitRequest with Action Write; artificial `ID: uuid.New()` on `New` only
when a change is detected; no-op suppression by leaving both aReq sides
unset (nil resource IDs skip the log, request.go skip rule); the write
path itself stays byte-identical (upserts still run unconditionally).
- `putChatSystemPrompt` (writes two keys conditionally in one existing
tx): inside that tx, read the pair via `GetChatSystemPromptConfig` for
`Old`, perform the conditional writes exactly as today, then RE-READ the
pair for `New`. The re-read is load-bearing:
`include_default_system_prompt` is computed from the toggle row AND the
prompt, so a prompt-only write can flip the effective value without the
request carrying the pointer. `PlanModeInstructions` stays zero on both
sides.
- `putChatPlanModeInstructions` (no tx exists today): wrap its
read-upsert in `InTx` (behavior-preserving: same single write);
`Old`/`New` populate only `PlanModeInstructions`; the two system-prompt
fields stay zero on both sides; no cross-key reads.
- Change detection compares the populated payload fields only (never the
artificial ID).
- Tests: handler-level coderdtest with `audit.NewMock()` asserting Write
entry on change and NO entry on a value-identical PUT, for both
endpoints (this also exercises `ResourceRequiresOrgID` end to end); the
fallback-flip case (no explicit include-default row, nonempty prompt set
to empty, effective boolean flips: entry emitted with the boolean diff);
diff assertions (old->new prompt text tracked, not secret) in
`enterprise/audit/diff_internal_test.go`; `TestAuditableResources`
passes by construction.
- Bookkeeping at PR open: correct CODAGT-719's no-op premise ("matches
the existing 204-on-unchanged behavior" does not exist on main;
suppression is new, write path unchanged).
- Review focus: Old capture and the New re-read inside the tx (three of
four existing singletons never set Old; do not copy them; and the
computed include-default value makes a naive New construction wrong);
the skip-on-no-op mechanism; prompt text deliberately visible in diffs.

</details>

Note: the plan excerpt above predates operator decision D5 (2026-07-30),
which this PR implements: the resource type is
`chat_instruction_settings` (not `chat_system_prompt_settings`), each
setting carries a stable ID and a display-name target (not a per-write
artificial ID and an empty target), no-op suppression runs through
`InitRequestWithCancel` (not the nil-ID skip), and attempts (denied,
failed, capture-degraded) record rows with real statuses and empty
diffs. Ticket bookkeeping for CODAGT-719 was corrected on Linear at
kickoff: the ticket's "matches the existing 204-on-unchanged behavior"
premise does not exist on main; suppression is new, and the write path
is unchanged.

> 🤖 This PR was created with the help of Coder Agents, and _will be_
reviewed by a human. 🏂🏻

---------

Co-authored-by: Michael Suchacz <203725896+ibetitsmike@users.noreply.github.com>
2026-08-18 20:03:06 +02:00
Michael Suchacz affeeaf9c8 feat: extend agent chat MCP tools for remote UAT evidence loops (#28233)
Extends the Agent-chat MCP tools so an unattended UAT evidence loop can
fetch artifacts, monitor long runs, and find prior runs without burning
model context.

## Backend

- New `chat_files_token` crypto key feature (migration 000571) with
rotator support and a dedicated signing keycache on coderd.
- `POST /api/experimental/chats/files/{file}/download-url`
(authenticated) mints a short-lived (5 min) signed URL and returns it
with `sha256`, `size_bytes`, `name`, `mime_type`, and `expires_at`.
- `GET /api/experimental/chats/files/{file}/download?token=` (no session
token) redeems the signed URL: verifies the JWS, requires the token's
`file_id` to match the path, and re-checks the minting user's RBAC
access live at redemption. Clients can `curl -o` artifacts with zero
credentials in the URL consumer.
- `ChatFileMetadata` gains `size_bytes` (via `octet_length`, no bytes
fetched).

## MCP tools (`codersdk/toolsdk`)

- `coder_download_chat_file`: by `file_id` or `chat_id`+`file_name`;
returns the signed URL plus checksum and size instead of base64.
- `coder_await_chat`: blocks (bounded `wait_secs`, 1-120) until a chat
leaves `running`/`interrupting`, using the existing watch stream with
subscribe-before-read.
- `coder_list_chats`: label, query, and limit filtering; chat
projections now include labels.
- `coder_get_chat_messages`: `after_id` forward cursor with
`next_after_id` (exact incremental reads), plus per-message `files`
metadata so artifact-bearing messages are identifiable.
- `coder_get_chat`: file listings now include `size_bytes` and
`created_at`.
- `coder_list_templates`: exposes `agents_allowed` for pre-flight
checks.

## Testing

- coderd: mint/redeem happy path with an unauthenticated client,
expired/tampered/file-mismatched tokens, auth still required on the
plain file endpoint, non-owner mint rejection.
- toolsdk: harness + integration coverage for all new/changed tools,
including signed-URL redemption with checksum verification,
forward-cursor exactness, await transition/timeout paths, and label
filtering.
- Remote dogfood UAT (dev.coder.com Coder Agent) passed all six
acceptance scenarios end to end over both MCP transports.

Note: `go test ./codersdk/toolsdk/` has a pre-existing goleak flake on
main (leaked `agentssh` non-PTY session goroutines from SSH exec tests;
reproduced 3/3 on clean `b4971bc49f1`). It is unrelated to this diff.

> Mux acted on Mike's behalf to create this PR.

<!-- mux-attribution: model=claude-sonnet-4-6 thinking=high -->
2026-08-18 19:15:30 +02:00
Michael Suchacz 7724ee281a feat: defer MCP tool schemas behind a find_tools search (#28225)
## Summary

When the `mcp-tool-search` experiment is enabled, chatd stops inlining
connected MCP tool schemas into every generation. It instead exposes a
built-in `find_tools` tool whose description carries a compact catalog
of the deferred tools, and only ships full JSON schemas for tools the
model has activated by searching or by calling them directly.

Closes [CODAGT-760](https://linear.app/coder/issue/CODAGT-760).

## Problem

Tool-heavy agent configurations (GitHub, Linear, Notion, and dev-tooling
MCP servers) inline over 100k tokens of tool schema definitions into
every generation. Initial uncached requests reached ~216k tokens with
time-to-first-token close to nine minutes, while the model typically
invokes only a handful of tools per turn.

## How it works

- `decideMCPToolSearch` defers external and workspace `.mcp.json` MCP
tools whenever the experiment is enabled. Native, dynamic, provider,
skill, and transport tools are never deferred.
- `find_tools` embeds a server-grouped catalog in its tool description
(degrading to names-only, then counts-only, then a constant-size summary
past a context-scaled size cap) and scores keyword matches across tool
names, descriptions, parameter schemas, and server metadata. Queries can
scope to one server with a `server:` prefix, and exact `names` arguments
always activate.
- Activation state is ephemeral: it is re-derived each generation from
surviving chat history (`find_tools` results and direct calls to
deferred tools), so activations naturally lapse when compaction
summarizes them away. Aggregate activated schema weight is capped at 10%
of the context window, shedding the least recently activated schemas
first; `find_tools` shares that budget across parallel calls in one
step. No new persistence.
- Deferred tools stay registered for execution, so the model can call a
cataloged tool directly without searching first; the schema is activated
for subsequent steps.
- Fail-open: the experiment being disabled, an empty candidate set, or
an MCP tool named `find_tools` all disable deferral, leaving today's
behavior byte-identical on the wire.
- Prometheus counters/histograms track `find_tools` calls, matches,
activations, and deferred token weight.
- The conversation timeline renders `find_tools` calls with a collapsed
search summary and expandable match list, falling back to the generic
renderer on malformed payloads.

## Validation

- Unit tests for the catalog, matcher, experiment-gated decision, and
activation derivation; end-to-end chatd generation tests covering
search-then-call, direct-call activation, experiment-off wire parity,
compaction lapse, and subagent tool gating.
- Storybook interaction tests for the timeline rendering and
malformed-payload fallback.
- Remote dogfood UAT on dev.coder.com passed: deferral with a real MCP
server and Anthropic model, direct calls without prior search,
activation persistence across turns, experiment-off parity, and clean
UI/console.

> Disclosure: Mux (AI agent) authored this PR on Mike's behalf.
2026-08-18 19:12:47 +02:00
Nick Vigilante 27e3d0fb00 Change all windsurf.com links to devin.ai links (#28270)
This avoids the redirects from our docs to Devin's docs.

<!--

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.

-->
2026-08-18 16:18:17 +00:00
Nick Vigilante 0db25caad6 docs(docs/.style/style-guide): fix self-violations found by audit (#27855)
Builds on #27849 and #27852 (both merged).

Runs every style guide page through the guide's own rules, including the
STE-derived rules from #27852, and fixes the violations in the guide's
prose and **Do** examples. **Don't** examples keep their intentional
violations. Three parallel audit passes produced roughly 120 findings;
this PR applies the accepted ones.

Objective defects fixed: an unbalanced quotation mark on the audience
page, a stale "in this PR" reference in the README, a `console` **Do**
example whose command and output had been collapsed onto one line,
inline `> [!NOTE]` markers that GitHub renders as literal text instead
of callouts, "a `onClick`", and two stale Vale rule references. Two
**Do** examples modeled banned or wrong prose: the audience page's
example contained the exact `*Audience: ...*` metadata line the same
page bans, and a word-choice example had Coder running its own login
command.

Rule-adherence fixes: US-quotation comma/period placement throughout,
banned idioms and figurative language ("wall of commas", "silently
rots", "when in doubt", "stretch goal", "bleeding-edge", the Churchill
"put up with" example), simplicity words and vague qualifiers ("easy",
"straightforward", "typically", "almost always", "often"), directional
"above", framing paragraphs under bare headings, run-in bold leads split
to one sentence per source line, prose semicolons split into sentences,
6-item prose enumerations reduced, and end-of-page "Related" sections
renamed to **Learn more** per the guide's own heading rule.

**One policy call for docs-team review**: the digits-everywhere rule now
scopes out numbers that describe language itself ("a contraction joins
exactly two words") and `one` as a determiner or pronoun. The
alternative was rewriting every determiner as a digit ("give each
paragraph 1 topic"), which makes the prose worse. With the scoped rule,
the remaining real counts were converted to digits.

Deliberately not changed: "lands"/"land" as release vocabulary,
attributed claims inside the Latin-abbreviations `` block,
persona-sketch color on the audience page (writer-facing planning
vocabulary), and the "What's a workspace" heading example.

Linear: DOCS-650

---

> This PR was created with AI assistance (Coder Agents).
2026-08-18 11:14:07 -04:00
Michael Suchacz 119f2b1dd9 feat: limit concurrent chat agents with pooled admission (#27902)
Limits concurrent chat generation on capped deployments to 5 root chats
and 10 delegated subagent chats. The pools are deployment-wide and
independent, so delegated work can continue while root capacity is full.

The default caps live in AGPL code. Enterprise contributes only a
licensing unlock, so unlicensed deployments stay capped and cannot fail
open. Licensed deployments are uncapped while Agent Hours usage stays
below an explicit hard limit. Deployments without a hard limit remain
uncapped, and reaching the Agent Hours allocation only triggers
warnings.

Admission happens before a worker takes chat ownership. Capped
deployments serialize admission across replicas with a
transaction-scoped advisory lock and derive active and queued state from
current ownership plus fresh runner heartbeats, rather than persisted
queue markers or per-replica state. The acquisition query returns a
bounded, pool-interleaved candidate set instead of ranking the whole
backlog; a migration replaces the acquisition index with a pool-aware
one. Refused chats stay running but unowned, and interrupt requests
bypass admission so users can stop queued or over-cap chats.

The single-chat API derives `queued_for_capacity` from live pool state;
list endpoints do not report it. The UI polls that value every 5 seconds
while a chat is running and shows a callout when the chat is waiting for
capacity.

Updates the administrator documentation and deployment-wide Prometheus
gauges for active and queued agents. Replica-level values must be
aggregated with `max`, not `sum`.

> Mux updated this PR on Mike's behalf.
2026-08-18 16:55:43 +02:00
Atif Ali 062c0fdd3b docs: rebrand Windsurf doc page to Devin Desktop (#28205)
## Summary

Cognition (maker of Devin) rebranded the Windsurf Editor as Devin
Desktop on June 2, 2026, after acquiring it from Codeium in July 2025.
Our docs still referred to the editor as Windsurf and linked to a dead
`codeium.com` domain.

## Changes

- Renamed `docs/user-guides/workspace-access/windsurf.md` to
`devin-desktop.md`, rewritten to lead with Devin Desktop branding, note
the Codeium -> Windsurf -> Devin Desktop history, and use current links
(`windsurf.com`, `docs.windsurf.com`) instead of dead `codeium.com`
ones.
- Updated `docs/manifest.json` and
`docs/user-guides/workspace-access/index.md` to reference the new page.
- Updated remaining Windsurf mentions to Devin Desktop in
`docs/ai-coder/ide-agents.md`, `docs/ai-coder/index.md`,
`docs/reference/glossary.md`, and
`docs/ai-coder/ai-gateway/clients/index.md`.
- Added `windsurf.com`/`devin.ai` to `.github/.linkspector.yml` ignore
patterns; both rate-limit repeated automated requests with 429s (same
class of issue as the `codeium.com`/`marketplace.visualstudio.com` fix
in #28203).
- Switched every module reference from `windsurf` to the new
`devin-desktop` registry module (`docs/about/contributing/modules.md`,
the three `get-started/customize-your-template/*.md` Terraform
tutorials, and the main doc page's module link), since the new module
actually renders `display_name = "Devin Desktop"` / `slug =
"devin-desktop"` in the UI (the old `windsurf` module hardcodes
"Windsurf Editor").

<details>
<summary>Scope notes / sequencing</summary>

The `devin-desktop` module referenced here is being added in
[coder/registry#1050](https://github.com/coder/registry/pull/1050) (not
yet merged/released). That PR is itself gated on
[coder/coder#28214](https://github.com/coder/coder/pull/28214)
(whitelisting the `devin:` URI scheme) shipping in a released Coder
version first. This docs PR can merge independently, the module link
will 404 until #1050 is released, same as any
docs-ahead-of-registry-release sequencing.

The Terraform code samples now show `module "devin-desktop"` because
that module's `display_name`/`slug` are properly parameterized (unlike
`windsurf`, which hardcodes "Windsurf Editor"/`windsurf` regardless of
what's passed in), so the docs stay accurate to the rendered UI.

</details>

## Validation

- `make lint` (docs lint, markdownlint, repo checks) passes.
- Manually verified the new outbound links (`docs.windsurf.com`) return
200; `windsurf.com`/`devin.ai` are rate-limited (429) from this
environment too, hence the added ignore patterns.

Stacked on #28203 (targets that branch so the diff here stays scoped to
the rebrand; will retarget to `main` once #28203 merges).

> 🤖 This PR was created with the help of Coder Agents, and needs a human
review. 🧑💻
2026-08-18 15:17:02 +05:00
Susana Ferreira db3566c1a3 chore: correct AI Gateway metric provider label and cardinality notes (#28220)
The cardinality notes in `aibridge/metrics/metrics.go` assume the
`provider` label takes one of three values, and two for the key pool
metrics. That was accurate when the notes were written: `provider` is
the provider instance name, and the name defaulted to one of the three
provider types aibridge supports. Instances can now be given their own
names, so the label takes any configured name and the series counts
scale with the number of configured providers rather than being capped
at a fixed number.

The monitoring docs are also updated to make clear that `provider` is
the provider instance name.

Comments and documentation only, no behaviour change.

Follow-up to #28210.
2026-08-18 09:47:00 +00:00
Jaayden Halko fa8ffe4eda feat: report agent runtime hours usage in entitlements (#27985)
Populate `FeatureAgentRuntimeHours.Actual` on every entitlements refresh
for licenses that grant the feature. A new
`GetTotalUsageHBAgentRuntimeV1` query sums `runtime_ms` over the
license's usage period, reading `usage_events` directly:
`hb_agent_runtime_v1` is exactly one row per hourly bucket
deployment-wide with `created_at` at the bucket start, enforced by the
unique partial index introduced in #27983.

The measurement reuses the shared `measureUsage` policy from #27984
through a new `AgentRuntimeMsFn` closure (usage publisher subject):
failures publish the stable
`LicenseAgentRuntimeUsageUnavailableErrorText` and log the cause. Usage
is floored to whole hours, matching the unit of the
`agent_runtime_hours_*` claims, and at most one warning is emitted per
refresh: reaching the allocation supersedes the advisory soft limit. The
dashboard renders the soft-limit advisory muted without a sales link and
treats the runtime usage-unavailable text as a diagnostic.

**Precise usage.** `Feature.ActualMs` (JSON `actual_ms`), set only for
`agent_runtime_hours`, carries the exact stored milliseconds backing the
floored `Actual` so clients can render fractional hours (e.g. `10.3`).
It has the same freshness as `Actual`; the whole-hour warning thresholds
are unchanged.

**Unlimited licenses.** A license minted with the unlimited (`-1`)
allocation decodes to an enabled feature with a nil `Limit` (#27984), so
the warning write-back now guards the allocation dereference: no
thresholds can exist for an unlimited license, so no runtime hours
warning is ever emitted, while `Actual` is still measured and published.
`Feature.Compare` is unchanged; for usage-period features the
issued-at/end dates decide first, so a metered feature outranks an
unlimited one only on an exact timestamp tie, an edge pinned by a
`TestFeatureComparison` case and documented on
`decodeAgentRuntimeHours`.

**Grandfathered premium licenses.** Premium licenses without
`agent_runtime_hours_*` claims are now granted the feature disabled with
a zero limit over the license term, identical to an explicit
`allocation: 0`: usage is measured and published for every Premium
deployment, and chatd's pooled admission (#27902) caps concurrent
agentic chats until a license with a positive allocation is added. The
default carries a fixed early `UsagePeriod.IssuedAt` (2026-08-01, the
same mechanism as the managed-agents default) so any license actually
carrying the claims outranks it in the `AddFeature` merge regardless of
the licenses' relative issue dates; the constant must stay earlier than
the earliest legitimately issued claim-bearing license. Zero allocations
(explicit or grandfathered) emit no deployment-wide warning banner:
those deployments are steered by the in-page upgrade CTA and the
concurrency cap. Enterprise licenses are unchanged.

Part 3 of a 3-PR stack splitting up #27796 (see there for review
history). Stack: #27983#27984 → this PR.

Closes CODAGT-852.
2026-08-18 12:40:33 +07:00
Asher b5d18bb9c9 feat: add redirect URL override for external auth (#28082) 2026-08-17 14:09:23 -08:00
Susana Ferreira 95328f1ead fix: label unpriced token usage metric by provider name and type (#28210)
## Problem

The `provider` label was inconsistent between AI Gateway metrics. Every
metric emitted by the gateway labels `provider` with the provider
instance name, for example `anthropic-eu`, while
`coder_ai_gateway_cost_control_unpriced_token_usage_records_total` used
the provider type, for example `anthropic`. The two could not be
correlated on `provider`.

The metric was also inconsistent with itself: the path where a provider
fails to resolve labelled by instance name, and the path where a model
has no price labelled by type. The type is still worth exposing, since
prices are keyed on `(provider_type, model)` and that is what an
operator needs to add a price.

## Changes

- Label the metric with `provider` (the instance name, consistent with
the other gateway metrics) and add `provider_type` (the configured type
the price is keyed on).
- Use `unknown` for `provider_type` when the provider does not resolve
to a configured type.
- Log the unresolved-provider case at `warn` instead of `info`. A
missing price is an expected steady state, but a provider that cannot be
resolved is not.
- Update the metrics docs and the `metricsdocgen` fixture.

Closes [AIGOV-574](https://linear.app/codercom/issue/AIGOV-574)

> [!NOTE]
> Initially generated by Claude Opus 5, modified and reviewed by
@ssncferreira
2026-08-17 14:08:28 +01:00
Jake Howell ea8ba0c678 refactor: remove MUI and Emotion (#27821)
> 🤖 This PR was modified by Coder Agents on behalf of Jake Howell.

Until we meet again.

## Stack

- #27636
- #27718 
- #27719 
- #27722
- #27723
- #27724
- #27728
- #27730
- #27732
- #27762
- #27763
- #27786
- #27787
- #27788
- #27789
- #27790
- #27791 
- #27817 
- #27820
- #28009

## Final removal (`c39b664`)

Removes the last of MUI and Emotion now that every surface has been
migrated:

- **Dependencies**: drops `@mui/material` and
`@emotion/{cache,css,react,styled}` from `package.json` /
`pnpm-lock.yaml`, and deletes the `@types/emotion.d.ts` and
`@types/mui.d.ts` module augmentations.
- **Theming**: replaces the Emotion `CacheProvider`, MUI `ThemeProvider`
/ `StyledEngineProvider`, and `CssBaseline` in `ThemeProvider` with a
lightweight `theme/context.tsx` that exposes `ThemeContextProvider` and
a `useTheme` hook.
- **Global styles**: moves the base `body` styles (background, text
color, font, antialiasing) that `CssBaseline` previously provided into
`index.css`, and drops the temporary MUI modal/popover scrollbar-gutter
workaround.
- **Cleanup**: removes the MUI → shadcn / Emotion → Tailwind migration
guidance from `site/AGENTS.md`, updates the Storybook `preview.tsx`, and
adjusts assorted components (`Command`, `Slider`, `Switch`, `Tabs`,
`SyntaxHighlighter`, timing charts) and theme files to consume the new
context instead of MUI/Emotion.
2026-08-17 14:04:48 +07:00
Wyatt FryandEthan Dickson a005e5cd22 feat: add username and email user search filters (#27922)
## Summary

User search can now resolve exact `email:` and `username:` terms through
`GET /api/v2/users` instead of only supporting fuzzy free-text matches.
The database query already had exact email and username filters; this
wires the public search parser and API handler to those filters so
clients can ask for a single user by email without fetching every user
or depending on substring matching.

This is the API half of coder/terraform-provider-coderd#403: that
provider PR adds `data.coderd_user.email`, and this PR gives it an
efficient exact lookup path.

## Testing

- `go test ./coderd/searchquery -run '^TestSearchUsers$' -count=1`
- `go test ./coderd -run '^TestGetUsersFilter$' -count=1`
- Live API test:
  - Built local enterprise Coder from this branch.
- Started Coder on `http://127.0.0.1:39991` against a clean Postgres
database.
  - Created `lookup-target@example.com`.
- Verified `GET /api/v2/users?q=email:LOOKUP-TARGET@EXAMPLE.COM&limit=2`
returned exactly one user:

```json
{
  "count": 1,
  "users": [
    {
      "id": "efc6f909-ce0a-4731-bd2f-6e4df417aaa7",
      "username": "lookup-target",
      "email": "lookup-target@example.com"
    }
  ]
}
```

---

![flow.ai](https://img.shields.io/badge/Built_with-flow.ai-6366f1)
![Codex](https://img.shields.io/badge/GPT--5-000000)

---------

Co-authored-by: Ethan Dickson <ethanndickson@gmail.com>
2026-08-16 18:18:02 +05:00
Nick Vigilante 58de9ab8f8 docs: correct broken CLI commands and flags from drift sweep (#28098)
## Summary

Corrects broken CLI commands and flags surfaced by the DOCS-637
full-corpus runtime drift sweep. Each fix was verified against the
generated CLI reference (`docs/reference/cli/*`) and, where relevant,
`codersdk` source.

## Changes

| Page | Fix |
|------|-----|
| `docs/user-guides/workspace-access/index.md` | `coder port forward` →
`coder port-forward` (the space form is unrecognized; the command is
hyphenated). |
| `docs/ai-coder/github-to-tasks.md` | Remove `coder templates list
--org your-org-name` in two spots — `templates list` has no `--org` flag
(`unknown flag: --org`). |
| `docs/admin/infrastructure/scale-utility.md` | `--cleanup-timeout
15min` → `15m` — Go durations reject the `min` unit (`invalid duration:
unknown unit "min"`). |
| `docs/admin/integrations/dx-data-cloud.md` | `coder users list >
users.csv` emitted a whitespace table, not CSV. Emit JSON and convert to
real CSV with `jq`, mirroring the API tab on the same page and using the
same columns as the default table view
(`username,email,created_at,status`). |

## Notes / judgment calls

- **dx-data-cloud (CSV):** the page genuinely needs CSV (the DX CSM
imports a CSV, and the API tab already produces one via `jq ... @csv`).
`coder users list` only supports `--output table|json`, so the CLI tab
now produces real CSV via `jq` rather than switching the page to JSON.
- **scale-utility `:109` left as-is:** `--target-users 0:100` is
prefixed with "For dashboard traffic:", which correctly scopes it to the
`scaletest dashboard` subcommand, so it is not drift.
- **Excluded — sessions-tokens `--lifetime=720h`:** the sweep flagged
this because the throwaway SUT capped token lifetime at 168h, but
`--max-token-lifetime` defaults to `876600h` (~100 years), so the
example is valid on a default deployment. The `CODER_MAX_TOKEN_LIFETIME`
dependency is also already documented in the page's "Set max token
length" section. No change needed.

Linear: https://linear.app/codercom/issue/DOCS-641

> This PR was created with AI assistance (Coder Agents).
2026-08-14 12:49:11 -04:00
Nick Vigilante b0e93b6e3b docs: correct nginx X-Forwarded-Proto and certbot instructions flavor (#28086)
## What

Two fixes to the nginx reverse-proxy tutorial.

### `X-Forwarded-Proto` (line 137)
The config set:
```nginx
proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
```
`$http_x_forwarded_proto` is the value of a client-supplied request
header, which a client can spoof and which is usually empty for a direct
request. In an nginx TLS-terminating reverse proxy this should be
`$scheme`, which nginx sets from the actual connection (`https`). Using
the raw client header can break Coder's scheme detection and
secure-cookie handling.

### Certbot link flavor (line 57)
The Certbot instructions link used `?ws=apache` in an nginx guide;
changed to `?ws=nginx` so readers get nginx instructions.

Surfaced by the runtime drift sweep; verified against `main`.

Linear:
[DOCS-642](https://linear.app/codercom/issue/DOCS-642/docs-fix-reverse-proxy-nginx-x-forwarded-proto-dollarscheme-certbot)

> This PR was created with AI assistance (Coder Agents).
2026-08-14 12:47:59 -04:00
Nick Vigilante 1d189cc204 docs: fix P2/P3 typos and syntax errors from drift sweep (#28101)
## Summary

High-confidence textual subset of the DOCS-637 **P2/P3** drift batch (31
findings total). These 8 fixes are pure typo / grammar / syntax
corrections verified directly against the doc source, so they carry no
risk of misreconstructed command output.

## Changes (6 files)

| Page | Fix |
|------|-----|
| `docs/admin/templates/extending-templates/variables.md` | Remove
doubled word: "file in in the template directory" → "file in the
template directory". |
| `docs/admin/networking/port-forwarding.md` | Grammar: heading "From an
coder_app resource" → "From a coder_app resource". |
| `docs/user-guides/workspace-access/index.md` | Malformed heading
"Through with the CLI" → "Through the CLI". |
| `docs/about/contributing/modules.md` | Conventional-commit example
missing the required space: `feat(git-clone):add` → `feat(git-clone):
add`. |
| `docs/ai-coder/tasks-migration.md` | Add missing closing double-quotes
on Terraform `source`/`version` in two snippets that would fail
`terraform` parsing. |
| `docs/admin/users/idp-sync.md` | Role Sync section said "group sync
settings" (copy-paste from the Group Sync section); remove an invalid
trailing comma from a JSON output example. |

## Deferred (remaining ~23 P2/P3 items, not in this PR)

The rest of the batch is stale **command-output** samples (column/schema
changes, sample values) and items that need a content decision (e.g.
`--psk` now deprecated in favor of `--key`; `--address` deprecated; an
undocumented retention flag). Those need live-output reconstruction or a
call on direction, so they're left for follow-up work, consistent with
the issue's "handle after the P0/P1 fixes land" guidance. One catalog
row (`reverse-proxy-nginx.md:57`, certbot `ws=apache`) is already
handled by #28086 and is excluded here.

Linear: https://linear.app/codercom/issue/DOCS-646

> This PR was created with AI assistance (Coder Agents).
2026-08-14 12:44:55 -04:00
Nick Vigilante 5b97d99a48 docs: fix Helm TLS/ingress value keys in admin/setup (#28087)
## What

Fix the Helm values in the TLS setup step of
`docs/admin/setup/index.md`. The documented keys are silently ignored by
the chart, so TLS appears configured but isn't.

## Changes

- `coder.tls.secretName` (singular) → `coder.tls.secretNames` (a list).
The chart key is `secretNames`.
- `coder.ingress.secretName` / `coder.ingress.wildcardSecretName` →
nested under `coder.ingress.tls.secretName` /
`coder.ingress.tls.wildcardSecretName`, where the chart actually reads
them.
- Added `coder.ingress.tls.enable: true` so the ingress-termination
example actually enables TLS.

All keys verified against `helm/coder/values.yaml` on `main`
(`coder.tls.secretNames`,
`coder.ingress.tls.{enable,secretName,wildcardSecretName}`). Surfaced by
the runtime drift sweep. The example now parses to the correct chart
structure.

Linear:
[DOCS-643](https://linear.app/codercom/issue/DOCS-643/docs-fix-helm-tlsingress-value-keys-in-adminsetup-secretnames)

> This PR was created with AI assistance (Coder Agents).
2026-08-14 12:43:46 -04:00
Nick Vigilante 3145cc8386 docs: fix prometheus metric name and slack webhook backtick (#28085)
## What

Two small monitoring-doc fixes surfaced by the runtime drift sweep.

### `docs/admin/integrations/prometheus.md`
The native-histograms list showed
`coderd_template_coderd_template_workspace_build_duration_seconds`
(doubled `coderd_template_` prefix). The correct metric name, per the
metrics table earlier on the same page and the generated metrics, is
`coderd_template_workspace_build_duration_seconds`.

### `docs/admin/monitoring/notifications/slack.md`
The `CODER_NOTIFICATIONS_WEBHOOK_ENDPOINT` export ended with a stray
backtick:
```
export CODER_NOTIFICATIONS_WEBHOOK_ENDPOINT=http://localhost:6000/v1/webhook`
```
On paste, bash treats the trailing backtick as an unterminated command
substitution and errors. Removed it.

Both reproduced during the runtime drift sweep and verified against
`main`.

Linear:
[DOCS-647](https://linear.app/codercom/issue/DOCS-647/docs-fix-monitoring-examples-prometheus-metric-name-slack-webhook)

> This PR was created with AI assistance (Coder Agents).
2026-08-14 12:42:39 -04:00
Nick Vigilante f3fd4c4a77 docs: remove invalid --yes flag from coder template version promote (#28084)
## What

Remove the invalid `--yes` flag from the `coder template version
promote` command in the CI/CD publishing example.

## Why

`docs/tutorials/testing-templates.md` documents, in the GitHub Actions
"Promote template version" step:

```
coder template version promote --template=$TEMPLATE_NAME --template-version=... --yes
```

The `promote` subcommand has no `--yes`/confirmation flag, so the
command exits with `unknown flag: --yes` and breaks the documented CI
workflow. This is a golden-path (automation) breaker.

Verified against the generated reference
`docs/reference/cli/templates_versions_promote.md` (flags are only
`--template`, `--template-version`, `-O/--org`), and reproduced against
a live deployment during the runtime drift sweep. The command is
non-interactive, so no confirmation flag is needed.

## Change

Single line: drop ` --yes`.

Linear:
[DOCS-640](https://linear.app/codercom/issue/DOCS-640/docs-remove-invalid-yes-flag-from-coder-template-version-promote)

> This PR was created with AI assistance (Coder Agents).
2026-08-14 09:41:43 -07:00
043bebb7bc docs: add Coder Desktop stale-tunnel recovery and improve macOS log capture (#26735)
## What

Adds a **Recovering from a stale tunnel** section to the Coder Desktop
user guide, with separate macOS and Windows procedures, and tightens the
existing macOS log-collection instructions.

## Why

Users in the field have hit a state where Coder Desktop's menu bar /
tray shows **Coder Connect** as enabled but the embedded tunnel is no
longer working:

* `workspace.coder` fails to resolve (`No such host`), or
* DNS returns stale `fd60:627a:a42b::/48` addresses that no longer
route, causing `coder ssh`, file sync, and the directory picker to hang.

Related issues:

* coder/coder#26669 — `ExistsViaCoderConnect` false positives when Coder
Desktop has stale DNS
* coder/coder-desktop-windows#171 — Tray reports Coder Connect as
healthy while tunnel/DNS is broken

Until the underlying state-management gap is fixed in the apps, the docs
should give users (and support) a safe, repeatable way to recover
without rebooting.

## Changes

`docs/user-guides/desktop/index.md`:

1. **New "Recovering from a stale tunnel" section** under
Troubleshooting:
* **macOS:** stop the VPN configuration with `scutil --nc stop`, quit
the app via `osascript`, restart the helper daemon in place with
`launchctl kickstart -k system/com.coder.Coder-Desktop.Helper`, flush
DNS caches, then relaunch.
* Includes a warning to **not** use `launchctl bootout`, which removes
the daemon from launchd's system domain entirely and is not
re-bootstrapped on app relaunch.
* Includes a verification step using the built-in sentinel hostname
`is.coder--connect--enabled--right--now.coder` (defined in
`tailnet/conn.go` as `IsCoderConnectEnabledFmtString`) so users don't
need a workspace name to confirm the tunnel is healthy.
* Uses `dig @fd60:627a:a42b::53` (explicit server) and `dscacheutil -q
host -a name` because plain `dig` does not respect the macOS system
resolver.
* **Windows:** stop the app and `Coder Desktop` service, flush DNS,
restart, then verify the NRPT rule and Wintun adapter. Notes that
`ipconfig /flushdns` does not reset the embedded resolver and that
filtering agents (e.g., Zscaler) may still shadow `.coder` lookups.
2. **macOS log-collection improvements:**
* Switch the predicate from `subsystem == "com.coder.Coder-Desktop"` to
`subsystem BEGINSWITH "com.coder.Coder-Desktop"` so the export captures
the app, helper daemon, and network extension (which all log under
prefixed subsystems).
* Add a `log stream` example for live tailing while reproducing an
issue.

## Verification

* `npx markdownlint-cli2 docs/user-guides/desktop/index.md` — 0 errors.
* macOS recovery steps were validated end-to-end on a real install (the
`kickstart -k` form, in particular, was confirmed to restart the helper
without breaking the install, unlike `bootout`).

---

Created on behalf of @mdanter

---------

Co-authored-by: blink-so[bot] <211532188+blink-so[bot]@users.noreply.github.com>
Co-authored-by: Atif Ali <atif@coder.com>
Co-authored-by: Nick Vigilante <nickvigilante@users.noreply.github.com>
Co-authored-by: Matyas Danter <mdanter@gmail.com>
2026-08-14 12:31:04 -04:00
McKayla はな e1fa247e59 feat: redirect to the template builder after first time setup (#27670) 2026-08-13 11:57:29 -06:00
Michael Suchacz 8d4d0b35dd feat: add Coder Agents chat tools to the MCP toolsdk (#28025)
Exposes the experimental Coder Agents chats API through the MCP tool
registry, so MCP clients (the hosted `/api/experimental/mcp/http` server
and `coder exp mcp server`) can start and drive server-side coding
agents.

New tools in `codersdk/toolsdk`, all thin wrappers over existing
`codersdk.ExperimentalClient` methods:

| Tool | Wraps |
|---|---|
| `coder_create_chat` | `CreateChat` (prompt, optional org, model
config, labels) |
| `coder_get_chat` | `GetChat` (status, last error, last turn summary,
workspace, files) |
| `coder_get_chat_messages` | `GetChatMessages` (user-facing parts,
chronological, cursor pagination, queued prompts) |
| `coder_send_chat_message` | `CreateChatMessage` (queue or interrupt
busy behavior) |
| `coder_interrupt_chat` | `InterruptChat` |
| `coder_archive_chat` | `UpdateChat` with `archived: true` |
| `coder_list_chat_model_configs` | `ListChatModelConfigs` (enabled
configs with default flag) |

Both MCP servers register tools from `toolsdk.All`, so no additional
wiring is needed. Responses are trimmed to what an MCP caller needs (IDs
as strings, user-facing transcripts) rather than full SDK payloads. No
new endpoints and no database changes.

Also adds MCP
[prompts](https://modelcontextprotocol.io/specification/2026-07-28/server/prompts)
for the chat workflows, defined once in `codersdk/toolsdk` and
registered by both servers:

| Prompt | Purpose |
|---|---|
| `coder_agents_delegate` | delegate a task to a Coder Agents chat and
monitor it to completion |
| `coder_agents_check` | check the status and recent activity of an
existing chat |

Each prompt declares the tools its workflow needs; the stdio server
skips prompts whose tools are excluded by `--allowed-tools`.

Tests run the tools against a chat-enabled coderdtest instance (fake
OpenAI-compatible provider plus in-process AI bridge), covering the full
lifecycle, an interrupt against a blocked turn, pagination cursors,
permission-dependent model config filtering, and argument validation.
Prompt coverage spans SDK rendering, the hosted
`prompts/list`/`prompts/get` round trip, and the stdio server including
allowlist gating.

> Mux created this PR on Mike's behalf.
2026-08-13 18:32:47 +02:00
Susana FerreiraandNick Vigilante 3426f83a27 docs: use approximate spend and add Everyone group tip for AI Gateway cost controls (#28012)
## Summary

Updates the AI Governance Cost Control docs in two ways:

- **Terminology:** aligns the docs with the UI, which now labels spend
as **approximate** rather than **estimated**. Renames the `Estimated
spend` term, the "How spend is calculated" section (and its anchor and
references), and updates the surrounding prose.
- **Everyone group tip:** adds a note that, because the organization's
`Everyone` group includes every member, its **Members** tab is a quick
way for an admin to look up any user's effective group. This complements
the existing Get user AI spend API endpoint, since there is no dedicated
cost control page today.

Related: https://github.com/coder/coder/pull/27977

---

_This PR was created by Coder Agents on behalf of @ssncferreira._

---------

Co-authored-by: Nick Vigilante <nickvigilante@users.noreply.github.com>
2026-08-13 16:20:17 +00:00
Susana Ferreira 2d9b6eda8f feat: add experimental CLI to price unpriced AI models (#27926)
## Description

AI Gateway computes the cost of an interception from `ai_model_prices`,
which is seeded on every server start from a price book embedded in the
binary. A model the price book does not cover records a NULL cost, so
its spend is invisible to cost reporting and is not enforced against
budgets. The only fix was to wait for a Coder release that added the
model.

This adds an experimental CLI, backed by an experimental HTTP endpoint,
for pricing those models. Models the price book already covers are
rejected, because the seeder re-applies the book on every start and
would overwrite an operator price. Support for custom pricing will be
handled in
https://linear.app/codercom/issue/AIGOV-589/extend-experimental-cli-command-to-set-custom-ai-model-prices.

## Commands

```
coder exp ai-model-prices list [--provider] [--model]
coder exp ai-model-prices update [file|-] [--provider] [--model] [--input-price] [--output-price] [--cache-read-price] [--cache-write-price] [--yes]
```

## Changes

- Add `GET` and `POST /api/experimental/ai/model-prices`, gated behind
the AI Bridge entitlement and the existing `ai_model_price` RBAC
resource.
- Add a `GetAIModelPrices` query with optional `provider` and `model`
filters applied in SQL.
- Validate the whole request before writing anything, so one bad entry
cannot leave the table half updated, and report every problem at once.
- Reject prices for models the embedded price book already covers,
through a new `prices.IsDefaultPriced`.
- Add the `coder exp ai-model-prices` command with `list` and `update`.
`update` accepts a JSON document or the single-model flags and prints a
plan, asking to confirm unless the document is piped in or `--yes` is
passed.
- Consolidate the supported provider list into
`coderd/aibridge/prices/providers` so the price generator and the server
share one definition.
- Add `codersdk` types and client methods for both endpoints, and bound
the request body at 1 MiB.
- Document the command in the AI Gateway cost controls page.

Closes
https://linear.app/codercom/issue/AIGOV-567/experimental-cli-command-to-set-prices-for-unpriced-ai-models

> [!NOTE]
> Initially generated by Claude Opus 5, modified and reviewed by
@ssncferreira
2026-08-13 15:00:36 +01:00
Michael Suchacz e92fd8e96f chore: retire mark3labs/mcp-go dependency (#28061)
## Stack Context

PR 6 of 6 in a stack that migrates every Coder MCP surface from the
archived `github.com/mark3labs/mcp-go` library to the official
`github.com/modelcontextprotocol/go-sdk` v1.7.0.

Stack: #28056 -> #28057 -> #28058 -> #28059 -> #28060 -> #28061

## Why

With every production surface migrated, this PR removes the mark3labs
dependency entirely and converts the remaining test fixtures.

- Migrates the remaining mark3labs test fixtures (coderd MCP e2e tests,
chatd fixtures, mcpclient fixtures, and the Force On MCP policy tests)
to official stateless SDK servers.
- Removes `github.com/mark3labs/mcp-go` from `go.mod` and drops the
corresponding dependabot ignore entry. Zero references remain repo-wide.
- Updates the MCP docs for the 2026-07-28 protocol: stateless Streamable
HTTP behavior, the supported 2024-11-05 through 2026-07-28 protocol
range, and explicit non-features (resources, prompts, structured output,
elicitation, MCP Tasks).
- The e2e ping assertion is removed because MCP 2026-07-28 removed the
ping method.

> Mux created this PR on Mike's behalf.
2026-08-13 10:47:14 +00:00
Steven Masley f0c17291b3 feat: unhide --oidc-redirect-url server option (#28072)
Unhides the `--oidc-redirect-url` / `CODER_OIDC_REDIRECT_URL` server
option so it appears in `coder server --help` and the deployment
configuration docs.

- Removed `Hidden: true` from the option in `codersdk/deployment.go`
- Regenerated CLI golden files and docs via `make gen`

---

> Generated with Coder Agents on behalf of @Emyrk
2026-08-12 16:02:15 -05:00
Bobby Ho 209d1ca498 fix: reject PKCE code_verifier below RFC 7636 length floor (#28003)
The token endpoint accepted any non-empty `code_verifier`, so a
one-character verifier was enough to authenticate. RFC 7636 §4.1
requires 43 to 128 characters from the unreserved set.

That fix plus the related gaps review surfaced in the same path:

- Enforce the length and charset floor on the verifier before the S256
comparison runs.
- Validate the challenge at the authorize endpoint too. It was only
checked for non-emptiness, so a malformed challenge was stored and then
failed late at token exchange, blaming the wrong parameter.
- A malformed verifier now returns `invalid_request` (RFC 6749 §5.2); a
well-formed but wrong one still returns `invalid_grant` (RFC 7636 §4.6).
Both looked identical before, so a client had no way to tell a syntax
error from a hash mismatch and would retry the same bad verifier
forever.
- Revoke the authorization code when a PKCE check fails. Without that, a
leaked code could be replayed with unlimited verifier guesses for its
remaining lifetime, and RFC 6749 §10.5 requires codes to be single use.
- Fix verifier generation in `scripts/oauth2/*.sh` and the docs example.
They deleted reserved base64 characters instead of translating them to
the URL-safe alphabet, so most runs produced verifiers under the new
floor.

Also carries #28041, which merged into this branch: public clients may
register bare custom schemes such as `vscode://` again, with `mailto`,
`tel`, and `sms` rejected.

Split out of #27873 (public OAuth2 client support). PKCE is already
mandatory for every client, so this stands on its own.

<details>
<summary>Manual verification</summary>

Ran against a local dev server on this branch, using a session token and
a throwaway app from `scripts/oauth2/setup-test-app.sh`.

1. Happy path unchanged: HTTP 200, verifier length 43.
2. `code_verifier=short`, and a 43-character verifier ending in `!`:
both HTTP 400 `invalid_request`, so charset is enforced and not just
length.
3. `code_challenge=tooshort` at authorize: HTTP 400 `invalid_request`,
no code issued. An empty challenge still hits the older "required and
cannot be empty" message.
4. Well-formed but wrong verifier: HTTP 400 `invalid_grant`, distinct
from the cases above.
5. Retrying that same code with the correct verifier: HTTP 400, code
already revoked by the failed check.
6. `generate-pkce.sh` produces a 43-character verifier (20 out of 20
runs); the docs example produces 128.
7. `scripts/oauth2/test-mcp-oauth2.sh` passes end to end. The two
bearer-token failures in its output are a pre-existing script bug
(`09c50559f3`, July 2025) that reuses a resource-scoped token against
the real API, not a regression here.

</details>
2026-08-12 13:36:52 -07:00
Michael Suchacz 1458d27d78 fix: allow manual chat compaction from the error state (#28022)
A chat that fails generation with a context overflow (for example `Input
length 262625 exceeds the maximum allowed input length of 262112
tokens`) is stuck in a catch-22: `POST /chats/{id}/compact` returns 409
because the `RequestCompaction` transition is only allowed from the
waiting state, and the only other way out of the error state is sending
or editing a message, which re-runs generation with the same oversized
prompt and fails again. Compaction is exactly the recovery a
context-overflowed chat needs, and it is unreachable exactly when it is
needed.

Three semantic changes:

- Allow `RequestCompaction` from the error states: `E0 -> R0` and `E1 ->
R1` (queued messages are preserved and processed after the compaction
turn).
- Clear `last_error` in `Tx.RequestCompaction`, matching the
architecture rule that transitions leaving `E0`/`E1` clear the stored
error. Without this a successful compaction would land in waiting with a
stale persisted error.
- Grant the compaction turn a fresh history epoch: a
`grant_history_epoch` flag on `UpdateChatExecutionState` sets
`history_version = snapshot_version`, resets `generation_attempt`, and
clears `retry_state` in the same atomic update that clears `last_error`
(mirroring the `chat_messages` trigger postcondition). The transition
inserts no history, so without this the turn inherits the failed turn's
spent retry budget, and resetting the counter alone could collide with
message part episode keys still retained on the erroring replica.

No frontend change is required: the chat input is already enabled in the
error state and `/compact` submission already handles both the success
and 409 paths. Also updates ARCHITECTURE.md (transition matrix,
endpoint, and manual compaction sections), the endpoint's swagger
description, and SDK comments.

> Mux created this PR on Mike's behalf.

<!-- mux-attribution: model=claude-sonnet-4-6 thinking=high -->
2026-08-12 20:38:57 +02:00
Matt Vollmer 52bd05adb4 docs: remove AI Governance Add-On references (#28073)
## Summary

Replaces all remaining "AI Governance Add-On" references with language
consistent with AI Governance being included with a Premium license.

**`docs/ai-coder/ai-gateway/standalone.md`**

- Admonition: "AI Gateway requires the AI Governance Add-On...
deployments without the add-on will not be able to access" → "AI Gateway
requires a Premium license. Community deployments cannot access AI
Gateway."
- Requirements list: "A Coder license with the AI Governance Add-On" →
"A Premium license with AI Governance"

**`docs/install/airgap.md`**

- Table row: "deployments with the AI Governance Add On" → "deployments
with AI Governance"

**`docs/reference/glossary.md`**

- Agent Firewall, AI Gateway, AI Gateway Proxy entries: "This feature
requires the AI Governance Add-On" → "This feature requires a Premium
license"
- Agent Workspace Build entry: "the AI Governance Add-On expands the
allowance" → "a Premium license expands the allowance"
- Heading: "### AI Governance Add-On" → "### AI Governance"
- Definition: "A separate per-user license for Premium customers,
purchased on top of a Premium subscription..." → "Included with a
Premium license, AI Governance unlocks..."

**`docs/ai-coder/ai-governance.md`**

- Removed the "Identifying AI seat consumers" section (heading through
end of file), which described an "AI add-on column" in the UI

## Note

References to Agent Workspace Builds will be eliminated in a separate PR
that removes Coder Tasks from the docs.

---

PR generated with Coder Agents.
2026-08-12 11:35:36 -04:00
Danielle Maywood c424a76a12 feat: wire chat search box to full-text search (#27973) 2026-08-12 15:04:01 +01:00
Cian Johnston e5629126b7 docs: document prebuilds quota group behavior (#28015)
Documents the behavior of the prebuilds quota group so admins can find
it and understand why the `prebuilds` user doesn't appear in its member
list.

Clarifies that prebuilt workspaces are attributed to a group named
`coderprebuiltworkspaces` (often referred to as the **Prebuilt
Workspaces** group), which defaults to a quota allowance of 0 and should
be adjusted to match the desired prebuild pool size. Adds a note that
the `prebuilds` user is a system user and is hidden from group member
listings in the dashboard and API.

> 🤖 This change was generated by Coder Agents (https://coder.com).
2026-08-11 17:01:34 +01:00
Nick Vigilante 0a7bb80a1e feat: make CLI/API doc generators emit front-matter metadata (Phase 2) (#27246)
## Summary

Phase 2 of the H1 → front-matter migration (`DOCS-483`; parent
`DOCS-477`). Makes the two reference-doc generators emit per-page
metadata as YAML front matter instead of a leading `# H1`, so generated
pages are self-describing and `make gen` stops reverting migrated pages
(Phase 3).

Phase 1 (`DOCS-482`) made the coder.com renderers prefer a front-matter
`title` (manifest fallback).

> [!NOTE]
> Rebased onto `main` and fully regenerated, and updated across two
rounds of Coder Agents Review — see **Review follow-ups** below.

## Changes

- **`scripts/clidocgen/command.tpl` + `gen.go` + `main.go`** — front
matter now carries `title` (from `fullName`) and `description` (from the
command's `Short`), and the leading `# H1` is dropped. The CLI index
page's front matter is taken from the manifest `Command Line` route
(title/description/icon_path).
- **`scripts/apidocgen/postprocess/main.go`** — reads the manifest and,
at write time, injects front matter carrying each section's `title` plus
any curated `description`, `state`, and `icon_path`. The API index
page's front matter is taken from the manifest `REST API` route.
- **`scripts/docgenenv`** (new shared code) — one `YAMLScalar`
front-matter escaper, one `Route`/`Manifest` schema +
`LoadManifest`/`FindRoute`, and one `FrontMatter(Route)` emitter, all
imported by both generators (no duplicated helpers, types, or emitters).
- Regenerated all **166 CLI + 31 API** reference pages.

### Metadata → front matter, and what stays in the manifest

Every *per-page* manifest field is mirrored into the page's front
matter: `title`, `description`, `state`, `icon_path`. The **structural**
fields stay in `manifest.json`:

- `children` — the nav tree (explicitly out of scope).
- `path` — the manifest's pointer to the file; a page carrying its own
path is redundant/error-prone, so it's treated like `children`.

The fields are **duplicated** into front matter and **`manifest.json` is
left unchanged**, so this is a **no-op for rendering today** (coder.com
strips front matter for `llms`, and Algolia + the renderer read only
`title`). Removing the fields from the manifest is the natural
follow-up, gated on the renderer reading them from front matter first.

### Why the API side changes the postprocessor, not the `.dot` templates

The issue text suggested editing
`scripts/apidocgen/markdown-template/*`. I deliberately did **not**,
because the postprocessor derives each page's **filename, section title,
and manifest route** from the leading `# {name}` line
(`extractSectionName`). Emitting front matter from the template would
break that extraction. Instead the widdershins templates still emit `#
{name}`, the postprocessor reads it (and now verifies it), and then
swaps the heading for a front-matter block as each section is written.

## Review follow-ups (Coder Agents Review)

### Round 1 — addressed in `e53d5e03` (all threads resolved)

- **CRF-1 / CRF-4** — de-duplicated the escaper and the
`route`/`manifest` schema + traversal into `scripts/docgenenv` (shared
by both generators).
- **CRF-2** — `YAMLScalar` now quotes YAML-reserved scalars
(`true/false/null/…`, numbers); no current value is affected.
- **CRF-3** — added unit tests: a `YAMLScalar` round-trip, `FindRoute`,
and `prependFrontMatter`.
- **CRF-5** — the CLI and API **index** pages now mirror their manifest
route's title/description/icon_path instead of a hardcoded
`coder`/`API`, fixing a rendered-heading regression (`REST API`/`Command
Line` were being overwritten).
- **CRF-6** — dropped the dead `#login` anchor in
`docs/support/support-bundle.md` (the migrated `login.md` no longer
mints that heading anchor).
- **CRF-7 / CRF-8 / CRF-11** — renamed to `prependFrontMatter`, switched
to `bytes.Cut`, and it now strips the first line only when it is the `#
{name}` heading (`extractSectionName` errors otherwise).
- **CRF-9** — removed the orphan `docs/reference/api/chat.md` (not in
the manifest, not linked; the real page is `chats.md`).
- **CRF-10** — the metadata read and the manifest rewrite now share one
`FindRoute` traversal.
- **CRF-13** — moot under squash-merge; this branch is a single
scopeless commit.
- **CRF-15** — the pre-existing `sort.Slice`/`slices.IsSorted`
comparator is left as-is per the review (out of scope; safe today
because section names are unique).

### Round 2 — addressed in `ee796e7107` (all threads resolved)

- **CRF-16** (P1) — removed three em-dashes from new doc comments (the
only `make lint` failure on the prior head); the emdash gate is green.
- **CRF-17 / CRF-18** — unified front-matter emission into one shared
`docgenenv.FrontMatter(Route)`, used by the API postprocessor directly
and by `command.tpl` via a `frontMatter` template func. This retires the
hand-written template YAML and the
`indexTitle`/`indexDescription`/`indexIconPath` closures, so a new
front-matter field is wired in one place, and it gives the CLI index the
`state` arm it previously lacked. Verified byte-identical: a full CLI +
API regen produces zero page changes.
- **CRF-19** — CLI child sort switched to `slices.SortFunc` +
`cmp.Compare` (typed comparator).
- **CRF-20** — reworded the `prependFrontMatter` comment:
`extractSectionName`'s fail-fast is the load-bearing guard; the prefix
check is a defensive backstop.
- **CRF-21** — added `icon_path`/`state` coverage in `docgenenv`'s
`TestFrontMatter/AllFields` (the branch the index page relies on,
previously at 0%).
- **CRF-22** — `YAMLScalar` no longer emits a trailing-space value as a
bare scalar (YAML strips it on read, so it would not round-trip); added
test coverage.
- **CRF-24** — the shared emitter removed the duplicated `cliIndexRoute`
doc comment; the rationale now lives in one place.
- **CRF-23** (Phase 3, out of scope here) — noted: the API generator
wipes and regenerates `reference/api/` from the manifest, so removing
curated metadata from the manifest in Phase 3 needs another source first
(a generator that preserves existing front matter, or metadata carried
alongside the swagger annotations).
- **Process (Mafu-san)** — the verification set below now leads with
`make lint`, the mandatory CI gate that the earlier list omitted.

## Cross-repo dependency

**Resolved — this PR no longer has a hard merge-ordering gate** (CRF-14
was right; the earlier "must merge after #968" note was stale).

The coder.com surfaces that would otherwise leak raw front matter from
`coder/coder` `main` are already front-matter-aware on merged PRs:

- **coder.com#964** (`DOCS-554`, llms-full.txt corpus + Algolia) —
**merged**.
- **coder.com#974** (`DOCS-574`, the `.md` proxy twin + `llms.txt` index
titles) — **merged**.

coder.com#968 (`DOCS-577`) was re-scoped to only the renderer
route-metadata generalization; it's a no-op on today's corpus and its
own description confirms the "deploy before the generators" constraint
no longer applies (that was driven by the llms corpus, now in #964).
Worth a final confirmation that #964/#974 are **deployed** before merge,
but there's no branch/PR ordering blocker left.

## Verification & evidence

AI was the primary author of this PR (see disclosure below); per the [AI
Contribution
Guidelines](https://coder.com/docs/about/contributing/AI_CONTRIBUTING)
here is manual verification.

- `make lint` (golangci-lint + the emdash gate) passes; `go build` / `go
vet` / `go test` are clean for the generators + `scripts/docgenenv`;
`pnpm check-docs` passes.
- `swagger.json`, `docs.go`, and `manifest.json` are **unchanged** —
metadata is duplicated into front matter; command/section names and
routes did not move.
- The diff is purely additive front matter
(`title`/`description`/`state`/`icon_path`) + the leading H1 removal; no
body reflow. A full CLI + API regen produces **zero** page changes
beyond the two index pages.

<details>
<summary>Terminal evidence</summary>

CLI `description` from the command's `Short` (`YAMLScalar` quotes when
needed, e.g. a `Short` with a colon):

```md
---
title: server
description: Start a Coder server
---
```

API pages inherit curated manifest metadata (only Agents/Chats have any
today):

```md
---
title: Chats
description: "REST endpoints for Coder Agents Chats API (programmatic agent sessions)."
state:
  - early access
---
```

Diff scope + "no body changes" proof (uses an explicit `base..HEAD`
range, so it actually tests the claim):

```
$ git diff --shortstat origin/main
 210 files changed, 1447 insertions(+), 344 deletions(-)
# = 166 CLI + 31 API reference pages + generators + scripts/docgenenv
# swagger.json / docs.go / manifest.json: NOT modified

# Every removed line under docs/reference is a leading "# H1"; nothing else:
$ git diff origin/main..HEAD -- docs/reference/ | grep '^-' | grep -v '^---' | grep -v '^-# '
(empty)

$ pnpm check-docs
Summary: 0 error(s)
```

</details>

Linear: DOCS-483

> This PR was created with AI assistance (Coder Agents).
2026-08-11 15:22:55 +00:00
Atif Ali d7953bd046 fix(coderd): use service account wording in account notifications (#27536) 2026-08-11 13:16:25 +00:00
Matt VollmerandNick Vigilante 75e0790cd7 docs: remove beta references from Coder Agents docs and manifest (#27939)
Removes all references to "beta" from the Coder Agents documentation
under `docs/ai-coder/agents/` and the corresponding `"state": ["beta"]`
entries in `docs/manifest.json`. Also updates screenshots, video, and
hero image, and regenerates the auto-generated feature-stages table.

**Beta reference removal:**
- `getting-started.md`: Removed "Coder Agents is in Beta" from the top
note, "is in beta" from the Chats API note, and "during Beta" from the
feedback section. Removed the version/stability note pinning 2.33.1.
- `index.md`: Removed the "Product status" section that stated Coder
Agents is in Beta.
- `manifest.json`: Removed `"state": ["beta"]` from 17 entries for pages
under `docs/ai-coder/agents/`. Preserved all `premium` and `early
access` states.
- `feature-stages.md`: Regenerated the auto-generated beta features
table, which no longer lists Coder Agents.

**Asset updates:**
- `coder-agents-ui.mp4`: Updated demo video on the agents landing page.
- `llm-providers.png`: Updated LLM provider support screenshot.
- `models-list.png`: Updated models list screenshot.
- `models-add-model.png`: Updated add model form screenshot.
- `agents-hero-image.png`: Updated hero image on the `docs/ai-coder`
landing page.

Two references in `models.md` (`v1beta` in the Google Gemini API URL and
`anthropic-beta` in the Anthropic header name) were left unchanged as
they are provider API identifiers, not product status labels.

PR generated with Coder Agents

---------

Co-authored-by: Nick Vigilante <nickvigilante@users.noreply.github.com>
2026-08-11 08:57:57 -04:00
Michael Suchacz 57f38b5c24 fix: keep chat attachments while a linking chat exists
Fixes https://linear.app/codercom/issue/CODAGT-616/keep-chat-attachments-while-chats-remain-unarchived

Chat attachments could disappear even though the chat was still available. This happened when a message was saved without recording which attachments it used, or when cleanup deleted attachments before an archived chat itself was removed.

Creating a chat, sending or queuing a message, and editing a message now record both the message and which attachments it uses as one operation. If the chat is already at the 50-attachment limit, the chat change fails without being partially saved.

Concurrent attachment writes serialize the 50-file cap per chat. Cleanup locks candidates and checks again for new links before deleting. If a file becomes unavailable after input validation, create, send, and edit return a clear client error and roll back the chat change.

An attachment stays available while any chat that uses it still exists. After an archived chat reaches the end of its retention period and is deleted, an old attachment that no remaining chat uses can be cleaned up. The retention guide and unavailable-attachment UI text document this lifecycle. This change cannot restore attachments that were already deleted.

The database migration adds two indexes so attachment cleanup stays fast as attachments accumulate.

> This PR was authored by Mux (AI) on Mike's behalf.
2026-08-11 13:53:15 +02:00
Atif Ali b781be0fa2 docs: refresh JFrog Artifactory integration guide for SaaS (#28005)
## Summary

Refreshes the JFrog Artifactory integration guide to cover JFrog SaaS.
The JFrog-OAuth section previously implied the module was self-hosted
only and mixed the SaaS and self-hosted setup into one ambiguous step.

## Changes

- **JFrog-OAuth**: State the module works with both JFrog SaaS and
self-hosted (on-premises) Artifactory.
- **JFrog-OAuth**: Split setup into a SaaS UI flow (**External
Applications** > **Custom Integration**) and a self-hosted Helm
integration-template flow.
- **JFrog-OAuth**: Update the module example to
`registry.coder.com/coder/jfrog-oauth/coder`, `1.2.4`.
- **JFrog-Token**: Update the stale example to
`registry.coder.com/coder/jfrog-token/coder`, `1.2.2`.

## Validation

- `markdownlint-cli2` passes on the file.
- No emdash/endash.

Preview:
https://coder.com/docs/@matifali/jfrog-oauth-docs-saas/admin/integrations/jfrog-artifactory#jfrog-oauth

Related to the registry README refresh in coder/registry#1040.

🤖 Generated with [Claude Code](https://claude.ai/code)

> 🤖 This PR was created with the help of Coder Agents, and needs a human
review. 🧑‍💻
2026-08-11 02:18:39 +05:00
Andrew Aquino 6e07e2610f feat: add paginated API endpoint for groups (#27603)
backend-only changes from #27271; see that PR for summary of changes +
implementation details
2026-08-10 13:23:14 -07:00
J. Scott Miller 66b065323b feat: log rate-limited external auth token validation (#26754)
When `ValidateToken` keeps a token because the external auth validation
endpoint was rate-limited (a `403` with rate-limit headers or a `429`),
it returns `valid=true` without provider confirmation. Previously this
happened silently, so operators couldn't tell a provider-confirmed token
from one kept optimistically during a rate limit.

This adds a `Logger` to `externalauth.Config` and emits a `Warn` (with
`provider_id`, `provider_type`, `status_code`, and `reason`) on those
rate-limit branches. It also adds a
`coderd_oauth2_external_requests_rate_limited_total{name, source,
status_code}` counter, incremented in the instrumented round tripper
whenever a provider returns a rate-limited response. The rate-limit
detection is the shared `xhttp.IsRateLimited` (in `coderd/util/xhttp`),
used by both the tripper and `ValidateToken` so the metric and the
validation decision share one definition; no extra wiring is needed
since `ValidateToken` already routes through the instrumented client
with `source="ValidateToken"`.

One deliberate behavioral change rides along: rate-limit detection now
also recognizes the unprefixed `RateLimit-Remaining` header (GitLab, and
the IETF draft rate-limit headers), so a `403` with
`RateLimit-Remaining: 0` is treated as optimistically valid where it was
previously treated as revoked. All other valid/invalid decisions are
unchanged. `TestValidateToken` asserts the warning's fields on the
rate-limited cases and no warning for revocations, `401`, and confirmed
responses; `promoauth` and `xhttp` tests cover the detector and the new
counter.

<details>
<summary>Manual testing</summary>

The signals fire on the external-auth status check (`GET
/api/v2/external-auth/{id}`), which calls `ValidateToken`. To force a
rate-limited response, point a provider's `validate_url` at a mock that
returns the rate-limit shape:

1. Run a mock returning `429` on one path and `403` +
`X-RateLimit-Remaining: 0` on another.
2. Start `coder server` with `--prometheus-enable` and external auth
providers whose `validate_url` point at those mock paths (e.g.
`CODER_EXTERNAL_AUTH_0_VALIDATE_URL=http://127.0.0.1:5599/429`).
3. Create a stored link, either complete the OAuth flow, or insert a row
into `external_auth_links` with a future `oauth_expiry` (token contents
are irrelevant; the mock rejects regardless).
4. `curl` the status endpoint with a session token, then check:
- coderd logs for the `Warn` (`reason=status_code` for `429`,
`reason=rate_limit_headers` for `403`),
- the metrics endpoint for
`coderd_oauth2_external_requests_rate_limited_total{...,status_code="429"|"403"}`.

Notes: `scripts/testidp -429` only rate-limits `/oauth2/userinfo`, not
the `/external-auth-validate/...` path, so it does not exercise this;
use a mock `validate_url`. The default Prometheus port `2112` may
already be taken on dogfood workspaces, set `CODER_PROMETHEUS_ADDRESS`
to a free port.

</details>

🤖 Generated with the help of Coder Agents on behalf of @jscottmiller.
2026-08-10 14:43:16 -05:00
Steven Masley 9a57dfa642 feat: include agent metadata in workspace list responses (#27934)
Closes #27933. Related: #27897 (single-agent GET).

Agent metadata is only readable via a per-agent watch stream, so reading
it across N workspaces costs N+1 requests. This adds a batch read to the
list endpoint:

```text
GET /api/v2/workspaces?q=param:"pool=demo" include_agent_metadata:task_status
```

- New `include_agent_metadata` search key, repeatable and key-scoped. It
expands the response, it does not filter workspaces.
- `GetWorkspaces` aggregates the requested keys as JSON behind a `CASE`:
without opt-in the response is unchanged and the subquery never runs.
Runs only for the returned page, inside the same authorized query.
- Agents in the response gain `metadata`
(`[]codersdk.WorkspaceAgentMetadata`, `omitempty`), mapped by the
`workspace_agent_id` each element carries. The collection script is
omitted; it can be long.
- `codersdk.WorkspaceFilter` gains `IncludeAgentMetadata []string`.
- No wildcard, no schema change, no migration.

---

Authored by Coder Agents on behalf of @Emyrk.
2026-08-10 08:13:32 -05:00
Marcin Tojek cfeae56bed chore(docs): update release docs for v2.35.4 (#27972)
Automated docs update for v2.35.4 release.

Created by `releasetui`.
2026-08-10 11:53:47 +02:00
Marcin Tojek 0414948454 chore(docs): update release docs for v2.34.8 (#27970)
Automated docs update for v2.34.8 release.

Created by `releasetui`.
2026-08-10 09:52:49 +02:00
Ethan ee49107ea9 docs: document per-template agents_allowed (#27518)
Relates to CODAGT-713

Depends on #27517

This updates the Coder Agents platform controls documentation for the
per-template `agents_allowed` model. It replaces the deployment-wide
allowlist instructions with the **Agents allowed** controls in AI
Settings and template settings, documents that templates allow agents by
default, and explains that disabled templates are excluded from
`list_templates`, `read_template`, and `create_workspace`.

This is the final PR in the stack and aligns the template routing and
optimization guidance with the database, API, frontend, cleanup, and CLI
changes in the preceding PRs.
2026-08-07 17:23:28 +10:00