Commit Graph
14969 Commits
Author SHA1 Message Date
Jake Howell e88f2a3485 feat(site): add bouncy pop animation to inbox notification badge (#26057)
> 🤖 This PR was written by Coder Agents on behalf of Jake Howell.

Replaces the basic `animate-in fade-in zoom-in duration-200` on the
inbox unread badge with a bouncier pop using an overshoot easing.

The badge now pops in from `scale(0)` with `cubic-bezier(0.34, 1.36,
0.64, 1)` over 500ms, giving it an elastic overshoot that makes it feel
alive. Uses `tailwindcss-animate` utilities (`animate-in zoom-in-0
fade-in-0`) with just one arbitrary property for the easing. Conditional
rendering is preserved as before.

Also adds a `BadgeTransition` story with toggle buttons for easy visual
verification.

<img width="150" height="150" alt="preview-notification-animation"
src="https://github.com/user-attachments/assets/70331cd2-c2e2-4fd7-8f9e-edfe876e2bab"
/>
2026-06-23 04:58:59 +00:00
Kyle Carberry 27ecd17991 refactor: consolidate agent MCP onto a single persistent engine (#26599)
Two MCP code paths both spawned the servers declared in a workspace's
`.mcp.json`: the persistent engine in `agent/x/agentmcp` (which owns
tool-call execution via `CallTool`) and an ephemeral one-shot runner in
`agent/agentcontext` (`mcprunner.go`) that connected, listed tools, and
immediately closed each server purely for discovery. Every declared
server was launched twice, and the discovery path duplicated the
engine's `.mcp.json` parse, transport-build, env-resolve, and connect
logic.

This makes `agent/x/agentmcp` the single persistent MCP engine. The
`agentcontext` manager now reads that engine's per-server catalog
in-process through an injected `MCPCatalog` option and surfaces each
server as a `KindMCPServer` resource. The engine wires `SetOnReload` to
the manager's `Trigger`, so a reload (startup connect or `.mcp.json`
edit) re-resolves and re-pushes the pinned resources. Tool-call
execution is unchanged: it still flows through the engine's `CallTool`
over `POST /api/v0/mcp/call-tool`.

The now-dead HTTP discovery surface is removed: the agent `GET
/api/v0/mcp/tools` route with `agentmcp.API.handleListTools`, and
`workspacesdk.AgentConn.ListMCPTools` with `ListMCPToolsResponse` (mock
regenerated). The change nets roughly `-1370` lines, mostly the deleted
duplicate runner and its tests.

<details>
<summary>Decision log</summary>

The merge of #26585 made pinned `chat_context_resources` the sole source
of workspace context, which surfaced the duplicate spawning. Two options
were considered:

- **Option A + dependency injection (chosen):** keep `agent/x/agentmcp`
as the single persistent engine; `agentcontext` consumes its catalog
in-process and stays the orchestrator/owner at the API boundary (it
still pushes `KindMCPServer` resources). This is low-risk because
`agentcontext` already exposed the `resolver.MCPResources` seam, so the
change just rebinds it from the ephemeral runner to the shared engine.
- **Option B (rejected):** reimplement persistent pooling, reconnect,
singleflight, and race handling inside `agentcontext` and delete
`agentmcp`. Too broad, and it discards the engine's tested lifecycle for
no behavioral gain.

`agentcontext`'s discovery was never what kept servers alive; its runner
closed each server immediately after listing tools. The component
holding persistent connections was always `agentmcp`, which is why
execution already lived there. Consolidating onto it removes the
duplicated stack rather than a whole package: both packages survive with
distinct roles (`agentmcp` is the engine, `agentcontext` is the
orchestrator/owner).

</details>

Coder Agents generated on behalf of @kylecarbs
2026-06-22 22:21:58 -06:00
Kyle Carberry 0f37522e6f refactor(agent/agentcontext): fixed-location shallow context discovery (#26596)
## Problem

`agent/agentcontext` resolved workspace context by walking the working
directory recursively (depth 8) and matching files by basename. This
over-injected context:

- Instruction-file matching was case-insensitive, so the generated
`docs/reference/api/agents.md` was treated as an instruction file.
- Symlinked instruction files (`CLAUDE.md`, `.cursorrules` ->
`AGENTS.md`) shipped as duplicate resources.
- Nested `AGENTS.md` (e.g. `site/AGENTS.md`) were collected from
anywhere in the tree.
- Skills were discovered from *any* `skills/` directory anywhere in the
tree, and `.mcp.json` from any depth.

Resolving the repo root produced six instruction sources for what was
effectively one file of guidance, plus skills/MCP found by an open-ended
walk.

## What changed

Replace the recursive scan with **fixed-location, shallow** discovery.
Each scan root is inspected at its top level only: the resolver never
descends into subdirectories and never climbs to a parent. Additional
directories are added explicitly as sources (HTTP API) or via the
`CODER_AGENT_EXP_*_DIRS` seeding env vars.

- **Single working-dir scan root.** The working directory is one scan
root. Instruction files and `.mcp.json` are read only at its top level.
- **Fixed-location skills.** Skills are discovered only from `skills`,
`.agents/skills`, `.claude/skills`, `.codex/skills` (one skill per
immediate subdir with a `SKILL.md`), not from arbitrary `skills/`
directories.
- **Case-sensitive instruction names.** Exact
`AGENTS.md`/`CLAUDE.md`/`.cursorrules`; a lower-case `agents.md` is
ignored.
- **Symlink dedup.** Resources are attributed to their resolved target,
so symlinked `CLAUDE.md`/`.cursorrules` collapse into the single
`AGENTS.md`.
- **Watcher** mirrors the same fixed-location set instead of recursively
watching every scan root (no more walking `node_modules`).
- The recursive `walkDir`, `skipDirNames`, `MaxScanDepth`, and
`isSkillsContainer` are removed.

Resolving the repo root now yields `AGENTS.md`, `.mcp.json`, and the
`.agents/skills`/`.claude/skills` skills, with no nested
instruction-file noise.

## Behavior change

`site/AGENTS.md` is no longer auto-injected when the working dir is the
repo root. It loads when the working dir **is** `site/` (its top level),
or when `site/` is added as an explicit source. There is intentionally
**no walk-up** to a `.git` project root: an agent started in a
subdirectory does not auto-inherit ancestor `AGENTS.md`; those
directories are added explicitly.

<details>
<summary>Verification &amp; decision log</summary>

**codex research (confirmed via source).** Instruction files: codex
walks up to the first `.git` ancestor and reads root-&gt;cwd, one file
per directory, exact-cased names (`codex-rs/core/src/agents_md.rs`).
Skills: fixed roots (`.agents/skills`, `.codex/skills`,
`$CODEX_HOME/skills`, ...) with bounded in-root recursion
(`core-skills/src/loader.rs`). MCP: `.codex/config.toml` via walk-up; a
project `.mcp.json` is not a runtime source in codex. No resource type
triggers an unbounded downward walk.

**Decisions.**
- Adopt codex's fixed-location, shallow discovery (case-sensitive names,
symlink dedup, top-level-only files, container-only skills).
- **Deliberately omit codex's walk-up to the `.git` project root.** In
Coder the working dir is the scan root and extra directories are added
explicitly (HTTP API / `CODER_AGENT_EXP_*_DIRS`), so the implicit
ancestor climb added surprise without benefit (e.g. `context add ./site`
should scan `./site`, not the repo root).
- Keep `.mcp.json` (codex uses `config.toml`, intentionally not added).
- Include `.claude/skills` and `.codex/skills` in the container list so
the repo's existing `.claude/skills` skills are not regressed; skills
recurse one level inside a container.

**Tests.** `TestManager_WorkingDirScannedShallow` (working dir read at
top level; ancestor root and nested subdir both excluded);
`TestResolver_SkillsOnlyFromFixedContainers`,
`TestResolver_MCPConfigOnlyAtScanRoot`,
`TestResolver_SymlinkedInstructionFilesDeduplicated`,
`TestResolver_InstructionFilesOnlyAtScanRoot`,
`TestResolver_InstructionNamesAreCaseSensitive`. Cap tests use multiple
scan roots.

**Local checks.** `gofmt`, `go vet`, `golangci-lint`, `go test -race`,
and `make lint/emdash` pass for the package.
</details>

---
🤖 Generated by Coder Agents on behalf of @kylecarbs.
2026-06-23 03:42:40 +00:00
Kyle Carberry 688ee63f9d feat(site/src/pages/AgentsPage): group context indicator resources by directory (#26598)
## What

Groups the **Skills** and **Context files** lists in the context-usage
popover (`ContextUsageIndicator`) by their parent directory. Resources
pulled from different roots, for example a repo-root `AGENTS.md` and a
nested `site/AGENTS.md`, or same-named skills from `.coder/skills` vs
`.agents/skills`, previously collapsed to identical basenames and lost
their provenance. Each group now renders a dimmed directory header with
a folder icon, and its items are indented beneath it.

## Why

`ChatContextResource.source` carries the real path for instruction files
and skill directories, but the popover only rendered the basename, so
multiple context roots were indistinguishable.

## Notes

- The directory header shows even for a single root, so provenance is
always visible.
- MCP is intentionally left flat for now: an `mcp_server`'s `source` is
a server name (not a path) and the row carries no reference to the
`mcp_config` that declared it, so it does not fit the directory model
without a backend change.
- Adds a `getPathDirname` helper (with tests) and switches the skill
list `key` from `skill.name` to `skill.source` to avoid collisions
across roots.

## Test plan

- `ContextUsageIndicator.stories.tsx`: new `MultipleContextRoots` story
(files + skills across several directories); `Clean` story updated to
assert the single-root header. Storybook play tests pass (4/4).
- `path.test.ts`: added `getPathDirname` cases (unit pass).
- `biome check`, `tsc -p .`, and `make pre-commit` all pass.

<details>
<summary>Design decision log</summary>

- **Header gating:** started by only showing the directory header when a
section spanned more than one directory; changed to **always show**
because the provenance is useful even for a single root.
- **Presentation:** chose a grouped layout (dimmed directory header +
folder icon, items indented beneath) over an inline dimmed path prefix,
since there is typically a lot of shared structure to convey.
- **Directory labels** currently render the full path (dimmed, truncated
with a `title` tooltip). Open follow-ups: collapse `$HOME` to `~` or
show fewer path segments if the labels feel long.
- **MCP:** evaluated applying the same grouping. An `mcp_server` is
keyed by name and has no link to its `mcp_config` (confirmed in
`codersdk.ChatContextResource` and the `pinnedContextResources`
builder), so grouping servers under a config would require a backend
data change. Left flat for this PR.

</details>

---

🤖 This PR was created by Coder Agents on behalf of @kylecarbs.
2026-06-22 21:39:34 -06:00
Kyle Carberry 0b856ef637 fix(site/src/pages/AgentsPage/components/DiffViewer): dedupe diff files to prevent CodeView duplicate id crash (#26597)
Viewing a git diff on `/agents` could crash the whole diff view with
`CodeView.addItem: duplicate id
"agent/x/agentmcp/api_internal_test.go"`. When a diff body lists the
same post-image path in more than one `diff --git` section,
`parsePatchFiles` returns one `FileDiffMetadata` per section.
`DiffViewer` then maps each file to a `CodeView` item keyed by
`file.name` (the file tree is likewise keyed by path), so the repeated
path produced two items with the same id and `CodeView.addItem` threw,
tearing down the entire view.

Deduplicate the parsed files by path in `useParsedDiff`, keeping the
first occurrence, so both the `CodeView` and the file tree always
receive unique ids. `useParsedDiff` is the single source feeding both
panels, so a malformed diff now degrades gracefully and logs one warning
instead of crashing.

<details>
<summary>Root cause and verification</summary>

Confirmed against `@pierre/diffs` `parsePatchFiles` that a single patch
with two `diff --git` sections for one path yields `patchCount: 1`,
`fileCount: 2` with both entries named
`agent/x/agentmcp/api_internal_test.go`. That is the exact input that
made `CodeView.addItem` throw. Dropping (rather than merging) duplicates
matches the renderer's model, which can only show one item per id
anyway.

Verified on the branch: `pnpm test` (DiffViewer suite, including the new
`dedupeFilesByName` tests), `tsc -p .`, and the full `pnpm lint` (biome,
types, circular-deps, React Compiler, knip) all pass.

</details>

> Opened by Coder Agents on behalf of @kylecarbs.
2026-06-22 22:34:54 -05:00
Kyle Carberry cd56ab9e33 refactor: remove legacy live-read and injected-history chat context paths (#26585)
This PR makes the agent-pushed pinned snapshot
(`chat_context_resources`) the sole source of workspace context for
chats, completing the "Release 5" cleanup. It removes legacy mechanisms
now superseded by the snapshot that agents push over dRPC
(`PushContextState`) and refresh via `chat-context/refresh`.

Removed:

- **Live-read at turn time.** MCP tool discovery, skill live-body reads,
and the instruction/skill history fallback that dialed the workspace on
every turn.
- **Context injected as message history.** The
`persist_workspace_context` generation action and its decision-loop
guard.
- **The legacy write path.** `POST`/`DELETE
/api/v2/workspaceagents/me/experimental/chat-context`, the agentsdk
`AddChatContext`/`ClearChatContext` methods, and the CLI one-shot
writer.
- **The `chats.last_injected_context` column** and all of its plumbing
(migration `000529`, queries, `db2sdk`, `dbauthz`, audit table, and the
frontend `ContextUsageIndicator` fallback).

Subagent context inheritance no longer copies parent context messages;
children now hydrate the parent's pinned `chat_context_resources` on
create, which yields an identical pin for the same workspace and agent.

What stays (still served by the live agent connection, not the
snapshot): `read_skill_file` supporting-file reads, `read_skill`
supporting-file listing, and MCP tool execution.

> [!NOTE]
> Migration `000529` drops `chats.last_injected_context` and recreates
the `chats_expanded` view without it. The down migration restores both.

<details>
<summary>Decision log (D1-D5)</summary>

- **D1 (subagent inheritance):** Re-point inheritance from the legacy
message copy to a pinned hydrate. Children call
`hydrateChatContextOnCreate` instead of copying parent context messages.
- **D2 (`persist_workspace_context`):** Remove the generation action
entirely along with the decision-loop guard it existed to satisfy, since
context is never injected into history anymore.
- **D3 (legacy HTTP + CLI):** Remove the experimental `chat-context`
POST/DELETE endpoints, the agentsdk methods, and the CLI one-shot. The
dRPC push + `chat-context/refresh` replace them.
- **D4 (frontend fallback):** Remove the `last_injected_context`
fallback in `ContextUsageIndicator`; pinned `resources` are the sole
source.
- **D5 (sequencing):** Ship as a single PR rather than a stacked pair.

</details>

---
Coder Agents generated on behalf of @kylecarbs.
2026-06-22 19:26:34 -06:00
dependabot[bot] ce190b3e62 chore: bump the coder-modules group across 2 directories with 2 updates (#26593)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-23 00:27:23 +00:00
TJ fbf31f2b2d chore(site): swap AI add-on table icons (em-dash + plain check) (#26467)
Updates the icons in the **AI add-on** column for the admin tables on
`/deployment/users` and `/organizations/.../members`.

- Not consuming a seat: `XIcon` → muted em-dash
(`text-content-disabled`).
- Consuming a seat: `CircleCheckIcon` → plain `CheckIcon` from
`lucide-react` (no circle, still `text-content-success`).

Both tables share `site/src/modules/users/AISeatCell.tsx`, so the
single-file change covers `UsersTable.tsx` and
`OrganizationMembersTable.tsx`.

## Screenshot

Users admin table on a local dogfood build of this branch
(`/deployment/users`):

<!-- Drag-and-drop the screenshot here to attach it to the PR. -->

<details>
<summary>Notes</summary>

- The em-dash is rendered via the JSX entity `&mdash;` rather than a
literal `U+2014` character, so it does not trip `make lint/emdash` (see
`AGENTS.md` / `scripts/check_emdash.sh`). The browser still renders an
em-dash.
- Kept the existing `aria-label`s (`Consuming AI seat` / `Not consuming
AI seat`) and added `role="img"` on the em-dash span so screen readers
still announce the state.
- No callers other than `UsersTable.tsx` and
`OrganizationMembersTable.tsx` use `AISeatCell` (verified via search).

</details>

> Opened by Coder Agents on behalf of @tracyjohnsonux.
2026-06-22 14:54:07 -07:00
Nick Vigilante ed908ed019 fix(docs): repoint 7 broken external and anchor links (DOCS-415) (#26572)
Closes [DOCS-415](https://linear.app/codercom/issue/DOCS-415).

## TL;DR

Repoints 7 broken links across 5 docs files that the 2026-06-22 weekly
`check-docs.yml` Linkspector run flagged. Two other links from the same
run (the dead `nix` ref and the dead `reflectoring.io` ref in
`CONTRIBUTING.md`) were already folded into
[#26341](https://github.com/coder/coder/pull/26341).

## Why

Broken external and anchor links degrade reader trust, leak SEO juice,
and make the docs look stale. The weekly `check-docs` job exists
precisely to catch this kind of rot before customers do; the
surfacing-to-fix turnaround on these 7 is one PR. Run that surfaced
them: [actions/runs/27948011619 job
82697664858](https://github.com/coder/coder/actions/runs/27948011619/job/82697664858).

## Scope

| File | Line(s) | Old target | New target | Why |
|------|---------|-----------|------------|-----|
| `docs/tutorials/best-practices/organizations.md` | 62 | anchor
`#update-template-metadata-by-id` | `#update-template-settings-by-id` |
API endpoint renamed in
[#19228](https://github.com/coder/coder/pull/19228) (Aug 2025). New
heading at line 1105 of `docs/reference/api/templates.md`. |
| `docs/install/registry-mirror-artifactory.md` | 197 | JFrog
`terraform-registry` |
`terraform-opentofu-and-terraform-backend-repositories` | JFrog
consolidated their Terraform / OpenTofu / Backend docs into a single
page. |
| `docs/admin/templates/extending-templates/modules.md` | 76, 206 |
JFrog `set-up-a-terraform-module/provider-registry` and
`terraform-registry` | same consolidated JFrog page (root, no anchor) |
Same JFrog consolidation. Anchor dropped, see decision log. |
| `docs/admin/integrations/dx-data-cloud.md` | 84 |
`https://help.getdx.com/en/` | `https://docs.getdx.com/` | DX migrated
their help center to a separate docs domain. |
| `docs/about/contributing/frontend.md` | 37, 71 |
`https://reactrouter.com/en/main` | `https://reactrouter.com/` | React
Router dropped the `/en/main` prefix. |

## Validation

- All 7 replacement URLs return HTTP 200 (manual `curl -L -o /dev/null
-w '%{http_code}'` per URL; linkspector's puppeteer crashed in the agent
env, so it was run case-by-case)
- `make lint/markdown lint/emdash` clean locally
- Pre-commit hook (`scripts/githooks/pre-commit` -> `make
pre-commit-light`) clean
- No `/docs/` route changes; pure markdown content

## Not triggering `/coder-agents-review`

Docs-only markdown edit, no CI or build config changes; per `AGENTS.md`
the bot review is reserved for product / CI changes. `doc-check` handles
this category.

## Pre-mortem

| Concern | Mitigation |
|---|---|
| Replacement URL also turns out to be broken later | All 7 verified
HTTP 200 today; next weekly `check-docs` run will catch any future
regression. |
| JFrog anchor drop on `modules.md` (76, 206) loses navigation context |
Verified the consolidated JFrog page has no clean section anchor for the
original target; linking the root page is the honest fix. If JFrog ships
a better TOC anchor later, a follow-up can reattach. |
| Anchor rename in `organizations.md` was actually a different rename |
Confirmed via PR #19228 (Aug 2025) which is the exact rename that
produced `## Update template settings by ID`. |

<details>
<summary>Decision log</summary>

**Why drop the anchor on the JFrog `modules.md` links (76 + 206)**:
JFrog's new consolidated page
(`/terraform-opentofu-and-terraform-backend-repositories`) doesn't
expose the original `set-up-a-terraform-module/provider-registry`
section as a fragment-link target. The honest fix is to link the page
root; readers can scroll. The `registry-mirror-artifactory.md:197`
reference uses the same root link for symmetry.

**Why DX `docs.getdx.com` over `help.getdx.com`**: DX's help center at
`help.getdx.com/en/` now returns 404. They moved to a separate
`docs.getdx.com` domain with a different content structure. Linking the
docs root is the closest analog to the original "browse our docs"
intent.

**Why React Router root over `/en/main`**: React Router unified their
docs under the root URL. The `/en/main` prefix is no longer routable.
The root URL is the canonical successor.

</details>


<details>
<summary>CI: <code>audit-docs-paths</code> failure (pre-existing,
unrelated)</summary>

The `audit-docs-paths` job in `.github/workflows/weekly-docs.yaml` fails
on this PR because its `Fetch redirects.json` step issues an
unauthenticated `curl` to a file in private `coder/coder.com` and gets a
404 (exit code 22). Same failure on every recent PR in this repo.
Tracked in [DOCS-409](https://linear.app/codercom/issue/DOCS-409) and
fixed in [#26571](https://github.com/coder/coder/pull/26571), which
authenticates the fetch through the Contents API. My changes are
docs-content only (5 markdown files, 7 line changes) and don't touch the
TS/TSX paths or `redirects.json` that the audit examines, so this is a
pre-existing CI break, not a regression introduced here.

</details>

---

*Generated by Coder Agents on @nickvigilante's behalf.*
2026-06-22 17:04:19 -04:00
Andrew Aquino 9b6dc4ab41 fix: left-align module names in SelectionSummary (#26583)
fixes DEVEX-514, which I accidentally introduced in #26531
2026-06-22 13:15:29 -07:00
Spike Curtis 73acb2d5b4 test: accept 0 duration in TestAwaitDeliveryExactCount (#26582)
fixes https://github.com/coder/internal/issues/1603

On Windows you can get 0 duration from subsequent time.Now() calls.
2026-06-22 16:15:17 -04:00
Nick Vigilante c13dd06d2f ci(.github/workflows): disable audit-docs-paths pending cross-repo auth (#26571)
The `audit-docs-paths` job in `weekly-docs.yaml` fetches a config file
from a private upstream source. An anonymous read returns 404 and the
job fails on every weekly run, firing a misleading "Stale docs paths
found in site/src/" Slack notification (a pre-existing bug in the
notification copy, tracked separately).

We originally tried to authenticate the fetch with the existing CI token
used for cross-repo work (the same one used by `contrib.yaml`), but that
token does not have read access to the upstream source. The proper fix
is a GitHub App scoped to cross-repo `Contents: Read`; the docs team is
tracking the App provisioning internally.

Until the App is provisioned, this PR disables the job behind a
`vars.AUDIT_DOCS_PATHS_ENABLED` repository variable. The variable is
unset, so the job skips on the weekly cron and on `workflow_dispatch`.
The other two jobs in this workflow (`prepare-linkspector-browser`,
`check-docs`) keep running normally, so docs PRs still get link-checked.

Re-enabling once the App is provisioned is a one-line change: set
`AUDIT_DOCS_PATHS_ENABLED` to `'true'` on this repo, no workflow edit
required.

<details>
<summary>Investigation log (why the App is needed)</summary>

Initial attempt (commits `5cca548`, `fc0ff59`, now discarded)
authenticated the fetch via the GitHub Contents API with `Accept:
application/vnd.github.raw` and an existing CI token already used for
cross-repo writes. `coder-agents-review` approved that approach in Round
2 ([review
4546533491](https://github.com/coder/coder/pull/26571#pullrequestreview-4546533491)),
and all 29 CI checks passed.

Validation via `workflow_dispatch` (run
[27973114839](https://github.com/coder/coder/actions/runs/27973114839))
failed at the fetch step with `curl: (22) The requested URL returned
error: 404`. The bare `curl` against the same URL with a personal access
token returned HTTP 200 and valid JSON, so the call shape was correct;
the CI token just lacks the necessary scope on the upstream source. The
Contents API returns 404 (not 403) when a token cannot see a private
repository, which is why the original failure mode was hard to
attribute.

Options considered:

1. **Extend the existing CI token** to include the missing read access.
Cheapest in lines of code, but the token is org-CI-owned and changing
its scope has blast radius beyond this job.
2. **New fine-grained PAT.** Tightest scope, but PATs are user-owned. If
the issuing user leaves the org, the token auto-revokes and the audit
silently breaks again, which is exactly the failure mode this PR is
trying to make less likely.
3. **GitHub App owned by the org.** Tied to the org, not a user;
survives staff turnover; least-privileged per repo. Heaviest setup
because creation, installation, and secret provisioning all need org
admin.

Option 3 is the right long-term answer but is not same-day. Disabling
the job is the smallest change that stops the noise immediately, and the
feature-flag variable keeps the re-enable path to one step.

</details>

<details>
<summary>Validation</summary>

* `actionlint` clean on `.github/workflows/weekly-docs.yaml`.
* Branch passed all 29 CI checks under the previous authentication
approach; this revision is strictly smaller (one job-level `if` guard +
comments), no new failure surface introduced.
* The disable cannot be tested end-to-end without merging, since the
affected job runs on `schedule` / `workflow_dispatch` against `main`.
Once merged: confirm the next weekly run (or a manual
`workflow_dispatch`) shows `audit-docs-paths` as skipped, with no Slack
notification.

</details>

---

> Generated by [Coder Agents](https://coder.com) on behalf of
@nickvigilante.
2026-06-22 15:45:41 -04:00
Kyle Carberry 78c5ab96c9 feat(coderd/x/chatd): serve workspace MCP tools and read_skill from pinned context (#26581)
This wires the last two consumer-side gaps of the agent-pushed workspace
context refactor. coderd already hydrates each chat's pinned context
(`chat_context_resources`), and `resolveTurnWorkspaceContext` already
prefers the pin for instruction files and skill metadata. This change
extends that preference to workspace MCP tools and the `read_skill`
body.

Workspace MCP tools are now built from the chat's pinned `mcp_server`
resources instead of a live `ListMCPTools` pull.
`resolveWorkspaceMCPTools` prefers the pin and falls back to live
discovery for chats whose agent has not reported context yet, gated the
same way as the instruction/skills pin: the pin wins whenever the chat
has any pinned rows, so a workspace with no MCP servers contributes no
tools rather than resurrecting stale ones. Because the agent reports
tool names unprefixed, each tool is re-prefixed to the
`{server}__{tool}` form and the pushed JSON Schema is split into
`properties` and `required` so the result matches what live discovery
produced. Calls still proxy through the workspace agent connection; the
snapshot carries tool definitions, not a way to execute them.

`read_skill` now serves a workspace skill's `SKILL.md` body from the
pinned snapshot (`SkillMeta.Meta`) instead of dialing the agent, so a
pinned chat keeps returning the same instructions even when the
workspace is unreachable. The supporting-file list stays a best-effort
live lookup, since the snapshot carries only the meta file per the agent
push contract.

The legacy live paths remain as the fallback for agents that have not
pushed context; RFC Release-5 cleanup of those paths is out of scope
here.

<details>
<summary>Implementation plan and decisions</summary>

### Background

The agentcontext refactor is mostly shipped across earlier PRs (#25983,
#26526, #26533, #26577, #26570, #26573): the agent resolves instruction
files, skills, and MCP servers into a snapshot, pushes it via
`PushContextState`, and coderd hydrates each chat's pinned context
(`chat_context_resources`). This PR closes the two remaining
consumer-side gaps.

### Key facts established from the code

- Pushed MCP tool names are **unprefixed** (`mcprunner` stores
`tool.Name`); the agent MCP proxy and `CallMCPTool` expect the
`{server}__{tool}` form (`agentmcp.ToolNameSep`). The pinned path
reconstructs the prefix for execution, matching the model-facing names
the legacy path produced.
- Legacy `agentmcp` sets `MCPToolInfo.Schema = InputSchema.Properties`
and `Required = InputSchema.Required` separately. The pushed
`input_schema` is the full JSON Schema object, so the pinned builder
extracts `properties` and `required` to match that shape.
- `SkillMetaBody.meta` is the verbatim SKILL.md. The supporting-file
list is **not** in the snapshot, so it is fetched live on demand
(best-effort).
- Gating mirrors `resolveTurnWorkspaceContext`: the pin wins when the
chat has any pinned rows; otherwise the live path is used.

### Changes

1. `chattool/skill.go`: add `SkillMeta.Meta []byte`; extract
`listSkillFiles` from `LoadSkillBody`; in `readWorkspaceSkillBody`, when
`Meta` is present, parse the body from it without dialing and list files
best-effort, else use the legacy live read.
2. `context_prompt.go`: populate `SkillMeta.Meta` in
`contextResourcesToPrompt`; add `workspaceMCPToolInfosFromResources`
(pinned `mcp_server` rows to `[]workspacesdk.MCPToolInfo` with prefixed
names and split properties/required) and `splitMCPInputSchema`.
3. `chatd.go`: add `pinnedWorkspaceMCPTools` (build tools from the pin,
ok-gated) and `resolveWorkspaceMCPTools` (pin-first, fall back to
`discoverWorkspaceMCPTools`).
4. `generation_preparer.go`: call `resolveWorkspaceMCPTools` instead of
`discoverWorkspaceMCPTools`.

### Tests

- `chattool/skill_test.go`: read_skill serves the pinned body without
dialing, lists files via LS, and still returns the body when the
workspace is unreachable.
- `context_prompt_internal_test.go`: `SkillMeta.Meta` is populated;
`workspaceMCPToolInfosFromResources` prefixing/properties/required/skip
behavior; `pinnedWorkspaceMCPTools` ok-gating and fallback dispatch.

</details>

---

*Opened by Coder Agents on behalf of @kylecarbs.*
2026-06-22 13:13:27 -06:00
dependabot[bot] 3fb402cd82 chore: bump github.com/gohugoio/hugo from 0.163.0 to 0.163.3 (#26579)
Bumps [github.com/gohugoio/hugo](https://github.com/gohugoio/hugo) from
0.163.0 to 0.163.3.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/gohugoio/hugo/releases">github.com/gohugoio/hugo's
releases</a>.</em></p>
<blockquote>
<h2>v0.163.3</h2>
<h2>What's Changed</h2>
<ul>
<li>markup/highlight: Escape lang in default code block rendering
ce1a7e0b <a href="https://github.com/bep"><code>@​bep</code></a> thanks
to <a href="https://github.com/k0ngj1"><code>@​k0ngj1</code></a> for
reporting this issue.</li>
<li>parser/pageparser: Preserve non-ASCII whitespace after e.g. summary
divider 70a9068a <a
href="https://github.com/bep"><code>@​bep</code></a></li>
<li>resources: Support babel/postcss config variants 9d66d513 <a
href="https://github.com/jmooring"><code>@​jmooring</code></a> <a
href="https://redirect.github.com/gohugoio/hugo/issues/15039">#15039</a>
<a
href="https://redirect.github.com/gohugoio/hugo/issues/15040">#15040</a>
<a
href="https://redirect.github.com/gohugoio/hugo/issues/15043">#15043</a></li>
<li>hugolib: Fix page/section name collision regression f0133466 <a
href="https://github.com/jmooring"><code>@​jmooring</code></a> <a
href="https://redirect.github.com/gohugoio/hugo/issues/15046">#15046</a></li>
</ul>
<h2>v0.163.2</h2>
<h2>What's Changed</h2>
<ul>
<li>Continue resolving on ERR_ACCESS_DENIED in Node's resolver 134674f0
<a href="https://github.com/bep"><code>@​bep</code></a> <a
href="https://redirect.github.com/gohugoio/hugo/issues/15041">#15041</a></li>
<li>markup: Standardize behavior when external converters are missing
147f605f <a
href="https://github.com/jmooring"><code>@​jmooring</code></a> <a
href="https://redirect.github.com/gohugoio/hugo/issues/14222">#14222</a></li>
</ul>
<h2>v0.163.1</h2>
<p>The majority of the fixes in this release are security related
(including the upstream fix in 93c8c7d3 (golang.org/x/image)). Thanks to
<a href="https://github.com/vnth4nhnt"><code>@​vnth4nhnt</code></a> for
finding the issues fixed in a00b5c72 and cf9c8f93 (I will do the CVE
work on this later). There has been a uptick in security reports lately,
which doesn't mean that Hugo has gotten less secure, this is mostly the
work of the new and powerful AI tools using Hugo's restrictive <a
href="https://gohugo.io/about/security/">security model</a> as their
baseline. Just take a look at Go's recent <a
href="https://github.com/golang/go/issues?q=is%3Aissue%20label%3ASecurity">security
issue list</a> to see a demonstration of this.</p>
<h2>What's Changed</h2>
<ul>
<li>build(deps): bump golang.org/x/image from 0.41.0 to 0.42.0 93c8c7d3
<a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]</li>
<li>Fix multi --renderSegments merge behavior 95e5e9f4 <a
href="https://github.com/bep"><code>@​bep</code></a> <a
href="https://redirect.github.com/gohugoio/hugo/issues/15024">#15024</a></li>
<li>security: Normalize integer IPv4 host encodings in http.urls check
a00b5c72 <a href="https://github.com/bep"><code>@​bep</code></a></li>
<li>Drop symlinks in os.ReadDir, os.ReadFile, os.Stat and os.FileExists
cf9c8f93 <a href="https://github.com/bep"><code>@​bep</code></a> <a
href="https://redirect.github.com/gohugoio/hugo/issues/15019">#15019</a></li>
<li>commands: Fix convert command 2602796c <a
href="https://github.com/jmooring"><code>@​jmooring</code></a> <a
href="https://redirect.github.com/gohugoio/hugo/issues/15012">#15012</a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/gohugoio/hugo/commit/4d22555aebf458d5d150500c9ac4bee5b24cf0d3"><code>4d22555</code></a>
releaser: Bump versions for release of 0.163.3</li>
<li><a
href="https://github.com/gohugoio/hugo/commit/ce1a7e0bce3713af40496ded3c2c0ceeed49231d"><code>ce1a7e0</code></a>
markup/highlight: Escape lang in default code block rendering</li>
<li><a
href="https://github.com/gohugoio/hugo/commit/e8988c31412249897a2e6805b61c37ed5c82a10c"><code>e8988c3</code></a>
Merge commit 'c86d9f4aa8a58931f52df6516f10b67c807505fb'</li>
<li><a
href="https://github.com/gohugoio/hugo/commit/c86d9f4aa8a58931f52df6516f10b67c807505fb"><code>c86d9f4</code></a>
Squashed 'docs/' changes from 1f8ddb8a52..e17426e2b6</li>
<li><a
href="https://github.com/gohugoio/hugo/commit/70a9068aa67c67a9eb2ab5fe062faf5a99e6b650"><code>70a9068</code></a>
parser/pageparser: Preserve non-ASCII whitespace after e.g. summary
divider</li>
<li><a
href="https://github.com/gohugoio/hugo/commit/9d66d513cee02e77c96c78059d5c3ae6b1c5dde9"><code>9d66d51</code></a>
resources: Support babel/postcss config variants</li>
<li><a
href="https://github.com/gohugoio/hugo/commit/f01334666790f328e222f67c278396ee77003021"><code>f013346</code></a>
hugolib: Fix page/section name collision regression</li>
<li><a
href="https://github.com/gohugoio/hugo/commit/96e06e1ab8484327dcbb521b8445a52d0c022cf3"><code>96e06e1</code></a>
releaser: Prepare repository for 0.164.0-DEV</li>
<li><a
href="https://github.com/gohugoio/hugo/commit/19a5cec0b9618163bb519487382e861d29edf383"><code>19a5cec</code></a>
releaser: Bump versions for release of 0.163.2</li>
<li><a
href="https://github.com/gohugoio/hugo/commit/134674f00df2c2c0db24f0674de2263298d33eb7"><code>134674f</code></a>
Continue resolving on ERR_ACCESS_DENIED in Node's resolver</li>
<li>Additional commits viewable in <a
href="https://github.com/gohugoio/hugo/compare/v0.163.0...v0.163.3">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/gohugoio/hugo&package-manager=go_modules&previous-version=0.163.0&new-version=0.163.3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts page](https://github.com/coder/coder/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-22 18:28:02 +00:00
McKayla はな 116a220ff6 fix(coderd): let admins change their own workspace sharing role (#26559) 2026-06-22 12:27:16 -06:00
Kyle Carberry 966dd89537 feat: add chat context source CLI and agent-token refresh (#26577)
Adds the `coder exp chat context` CLI for managing workspace context
sources, plus the agent-token refresh endpoint the in-workspace refresh
relies on. Part of breaking the "Workspace Context Sources for Coder
Agents" RFC (#26466) into small, reviewable PRs.

## What this adds

**CLI (`coder exp chat context`)**, talking to the agent's local IPC
socket from inside the workspace:

- `list` lists the registered scan roots (built-in defaults are not
shown).
- `show <path>` shows a source and the resources the agent resolves from
it, including failures.
- `add <path>` registers a path as an additional context source. With
`--chat`, it keeps the legacy one-shot behavior (read context from the
path once and inject it into a single chat).
- `remove <path>` unregisters a source.
- `refresh [<chat>]` re-pins chat context to the agent's latest
snapshot.

**Agent-token refresh path** for the no-argument `refresh`:

- `refresh <chat>` uses the existing user-facing
`ExperimentalClient.RefreshChatContext` (already on main) and works from
anywhere.
- `refresh` with no argument runs inside the workspace: it re-resolves
the agent's sources over the context socket (catching freshly-cloned
repos and startup-script writes), then asks the agent, authenticating
with its own token, to re-pin every drifted chat. No `coder login`
required.
- This adds `agentsdk.RefreshChatContext` and `POST
/api/v2/workspaceagents/me/experimental/chat-context/refresh`
(`workspaceAgentRefreshChatContext`), mirroring the existing clear
endpoint's agent-token auth model.

## Testing

- `go test ./cli` (`TestExpChatContextAdd`, `TestParseChatID`,
`TestResolveContextSourcePath`)
- `go test ./coderd/x/chatd -run TestChatContextRefreshFromAgentToken`
(end-to-end: echo-provisioned agent pushes a snapshot, drifts a bound
chat, the agent-token refresh re-pins it, and an agent-less chat stays
untouched)
- `go build ./...`, `go vet`, `golangci-lint`, `make gen` (no generated
changes; experimental commands are excluded from CLI golden/doc
generation)

<details>
<summary>Design notes</summary>

This is **Split 4** of #26466. Split sequence:

1. #26558 - prompt pin consumption (merged)
2. #26570 - `codersdk` context resource types (merged)
3. #26573 - the context indicator UI (merged)
4. **This PR** - the CLI + agent-token refresh.
5. The context diff (`changes`, `ChatContextResourceChange`, the changes
dialog, `buildContentPatch`) - last.

Key points:

- The agent-local context subsystem (`agent/agentsocket` IPC for source
CRUD, snapshot, resync), the user-facing
`ExperimentalClient.RefreshChatContext`, and the per-chat
`chatd.RefreshChatContext` all already exist on main, so this split is
the CLI surface plus the small agent-token refresh endpoint that fans
out per-chat refresh across an agent's drifted chats.
- `add <path>` resolves relative paths to absolute before handing them
to the agent (which requires canonical paths) but preserves a leading
`~` for the agent to expand against its own home.
`TestResolveContextSourcePath` covers this.
- The agent endpoint is annotated `@x-apidocgen {"skip": true}`,
matching the other agent-token chat-context endpoints.
- No diff/changes rendering is involved; that lands in the final split.

</details>

*This PR was created by Coder Agents on behalf of @kylecarbs.*
2026-06-22 12:15:27 -06:00
Nick Vigilante ee3572ab9a feat: wire Vale prose linter into docs CI (#25467)
Wires Vale into docs CI as an advisory (non-blocking) prose-lint step.
Closes [DOCS-40](https://linear.app/codercom/issue/DOCS-40).

> **Integration update (rebased onto `main`).** Since this branch was
opened, `main` consolidated docs linting into the **required**
`lint-docs` job in `ci.yaml` and removed the standalone `docs-ci.yaml`
([#25608](https://github.com/coder/coder/pull/25608)). This PR adds Vale
to that `lint-docs` job instead of resurrecting `docs-ci.yaml`, and the
`docs/.style/` scaffold defers to the merged
[#25466](https://github.com/coder/coder/pull/25466) (DOCS-180). Vale
stays advisory.

> **Post-review refactor.** Following the Coder Agents review, Vale is
now invoked through `mise exec "aqua:errata-ai/vale"` (the same pattern
as `actionlint`/`zizmor`) instead of a bespoke `curl`/`tar` download.
This removed the GNU-only `grep -oP` version extraction and `uname`/arch
mapping that broke on macOS BSD grep, and the prose step now skips paths
a PR deletes. See the resolved review threads for CRF-17/19/20/21/22.

A sample of what this check does is as follows:

<img width="1443" height="1293" alt="image"
src="https://github.com/user-attachments/assets/cf68dbf9-d9df-49ba-8dbf-200875bc289e"
/>

## What changes

- `.vale.ini` at the repo root: Google base + Coder (custom, empty in
v1) + curated write-good. `alex` rules are pulled in a la carte. Inline
comments justify every enable/disable.
- `mise.toml`: pin Vale `3.7.1` via aqua. `mise.lock`: lock that pin
across all platforms so `mise install --locked` (used by `build_image`)
resolves it.
- `Makefile`: a `docs/.style/.vale-synced` sentinel that gates `vale
sync`, and a `lint/prose` target that runs `vale --no-exit`. Both invoke
Vale via `mise exec "aqua:errata-ai/vale" -- vale ...`, so mise owns the
version and the OS/arch download (no hand-rolled install path).
- `.github/workflows/ci.yaml`: append Vale steps to the existing
required `lint-docs` job: `Detect changed Markdown`, `Restore Vale
styles`, `Prepare Vale styles` (`make docs/.style/.vale-synced`), `Vale
prose lint`, and a default-branch-only `Save Vale styles`. They lint
only changed Markdown under `docs/` that still exists on disk, with a
problem matcher for inline PR annotations.
- `.github/vale-problem-matcher.json`: parses `vale --output=line` so
alerts surface as annotations on the Files Changed tab.
- `.gitignore` and the workflow cache `path:`: use
`docs/.style/styles/*` plus a `!docs/.style/styles/Coder` negation so
adding a package does not require parallel edits.
- `.markdownlint-cli2.jsonc`: ignore the synced styles so `make
lint/markdown` does not lint upstream READMEs.

Scaffold prose under `docs/.style/` and
`.claude/docs/DOCS_STYLE_GUIDE.md` / `AGENTS.md` come from the merged
DOCS-180; this PR no longer touches them. Net diff against `main` is the
8 Vale-wiring files only.

## Severity policy (v1)

Rule severity reflects two things together: the rule's false-positive
rate against real Coder docs and the gravity of the rule. Low FPs plus
high gravity argues for `error`; lower gravity or more judgment calls
argue for `warning` or `suggestion`. v1 lands most rules at `warning`
and the wordiness rules at `suggestion`.

A rule promotes to `error` only when (a) its false-positive rate against
real content is effectively zero and (b) the existing-content violation
count for that rule is also zero. Vale exits non-zero only on
error-level alerts regardless of `MinAlertLevel`; the Makefile and CI
invoke Vale with `--no-exit` so the baseline error count from
un-overridden Google rules does not fail the build while real failures
(bad config, missing files) still propagate.

## CI integration

Vale runs as steps appended to the required `lint-docs` job in
`ci.yaml`, gated on changed Markdown:

1. **`Detect changed Markdown`** (`tj-actions/changed-files`) scopes to
changed `**.md`; the prose step re-filters to `docs/` (the `docs/**.md`
glob silently skips dot-prefixed dirs and would miss
`docs/.style/style-guide.md`).
2. **`Restore Vale styles`** (`actions/cache/restore`), keyed off
`hashFiles('.vale.ini', 'mise.toml', 'docs/.style/styles/Coder/**')`.
mise manages the Vale binary, so only the synced styles are cached.
3. **`Prepare Vale styles`** runs `make docs/.style/.vale-synced` (`mise
exec ... vale sync`).
4. **`Vale prose lint`** filters the changed set to `docs/` paths still
present on disk, then runs `mise exec ... vale --no-exit --output=line`,
emitting inline annotations via the problem matcher.
5. **`Save Vale styles`** writes the cache, gated to `refs/heads/main`
only so PR runs cannot poison the cache other branches restore from (the
zizmor `cache-poisoning` concern).

**Every Vale step is `continue-on-error: true`.** This is a deliberate
change from the original standalone-workflow design: now that Vale lives
inside the *required* `lint-docs` job, a transient `vale sync` network
failure (or first-use `mise` install blip) would otherwise block merges.
`continue-on-error` keeps Vale advisory, so only the markdownlint /
table-formatter checks above (`pnpm check-docs`) remain merge-blocking.
`vale --no-exit` additionally keeps the baseline error count from
un-overridden Google rules from failing the step.

## Verification

- `actionlint` clean on `ci.yaml` (local + `make
lint/actions/actionlint`); `zizmor --persona regular` reports no
findings.
- `make lint/prose` on the full `docs/` corpus: ~406 errors, ~5,346
warnings, ~7,928 suggestions across 461 files, exit 0 (`--no-exit`),
Vale `3.7.1` installed by mise.
- Net diff vs `main` is the 8 Vale-wiring files only; the `docs/.style/`
scaffold already matches `main`.

<details>
<summary>Implementation plan and decision log</summary>

### Why this rule set

The Vale evaluation against the full docs corpus (measured 2026-05-18)
produced ~43,940 raw violations across six candidate base styles. The
selection here drops Microsoft and RedHat (overlap with Google, and
RedHat's Spacing rule hammers technical IDs), and proselint (Annotations
rule treats `> [!NOTE]` admonitions as TODO markers).

Within the kept styles:

- **Google** is the base. Disables: `EmDash` (conflicts with `make
lint/emdash`), `Latin` (i.e./e.g. are fine for our audience), `Spacing`
(4,500 errors on `codersdk.SomeType` patterns in the auto-generated API
reference). Softened: `Parens` to `suggestion`, `WordList` to `warning`.
- **write-good** is the base, with `Passive` and `E-Prime` off.
`TooWordy` and `ThereIs` are suggestions; `Weasel` is a warning.
- **alex** is cherry-picked (not in `BasedOnStyles`): `Ablist`,
`Condescending`, `LGBTQ`, `ProfanityLikely`, `Race`, `Suicide` at
warning. The `ProfanityMaybe`/`ProfanityUnlikely` rules trip on
`execute`, `kill`, `failed`, and `attack`, which read as technical
vocabulary in our context.
- **Coder** is in `BasedOnStyles` but the directory is empty in v1.
Rules land through the per-rule tickets in the [Docs style
guide](https://linear.app/codercom/project/docs-style-guide-7828445b9afc)
project.

### Why `mise exec` instead of a download block

Vale is pinned in `mise.toml` like `actionlint` and `zizmor`, so
invoking it via `mise exec "aqua:errata-ai/vale" -- vale ...` makes the
pin the single source of truth and lets mise handle the OS/arch-specific
download. This replaced an earlier ~30-line `curl`/`tar` block whose
GNU-only `grep -oP ...\K` version extraction returned empty on macOS BSD
grep. Note: the bare `vale` short name in `mise exec` ignores the pin
and resolves to the latest release, so the full aqua key is required.

### Why `vale sync` instead of vendoring

The three style packages weigh ~272 KB combined, so vendoring is cheap.
But Vale's ecosystem treats `Packages = ` + `vale sync` as canonical,
the upstream LICENSE files are not in the package tarballs (would need
to be added manually), and the CI cache makes the sync nearly free after
the first run. Sticking with the canonical pattern keeps the repo lean
and the upgrade path obvious.

### Why `lint/prose` is not in `lint:` or `lint-light:`

Vale on the full docs corpus takes ~20s on cold caches. Forcing every
pre-commit through that would be aggressive for a feature that ships as
warnings. `make lint/typos` follows the same pattern (it is in
`lint-light` but not `lint`; CI invokes it directly). v1 keeps Vale
opt-in locally and CI-only by default; promote to `lint:` once the rule
set stabilizes.

### Exit-code handling

Two mechanisms combine, and the choice changed when the step moved into
the required `lint-docs` job:

- `vale --no-exit` suppresses Vale's non-zero exit on alerts, so the
baseline error-level violations from un-overridden Google rules do not
fail the step while the cleanup PRs land. Real failures (config invalid,
file missing) still exit non-zero.
- `continue-on-error: true` on every Vale step. Because the steps now
run inside the *required* `lint-docs` job, a `vale sync`
download/network blip must not block merges. The original (standalone,
non-required) design rejected `continue-on-error` for showing a
misleading yellow badge; in a required job that tradeoff flips, and
advisory-yellow is strictly preferable to merge-blocking-red on an
infrastructure flake. `|| true` in the Makefile was also rejected: it
swallows missing-config failures indiscriminately.

### Pre-mortem

- **Generated docs noise**: `docs/reference/` is dominated by
auto-generated content (clidocgen, apidocgen, auditdocgen,
metricsdocgen). The architectural decision is to fix the generators, not
exclude paths in Vale. Google.Spacing is the only rule silenced
specifically to defer the generator fix; everything else surfaces as
warnings.
- **First-run cost**: `mise` installs the pinned Vale (a single small
binary) and `vale sync` pulls the style packages on a cold run. The
Actions cache keyed off `hashFiles('.vale.ini', 'mise.toml',
'docs/.style/styles/Coder/**')` makes subsequent runs near-instant; the
`Coder/**` hash is defense-in-depth against
[actions/toolkit#713](https://github.com/actions/toolkit/issues/713) so
a future cache release that regresses path-negation cannot serve a stale
`Coder/` from cache.
- **Required-job blast radius**: moving Vale into the required
`lint-docs` job means any Vale step failure would gate merges. Mitigated
by `continue-on-error` on all Vale steps plus a clean skip when no
changed `docs/` Markdown remains on disk, so only `pnpm check-docs`
stays blocking.
- **Cross-platform install**: handled by mise (aqua backend) rather than
a hand-rolled `uname`/arch map, which removes the macOS BSD-grep break
the review flagged.
- **Deleted files**: `all_changed_files` is ACMRD and lists paths a PR
removes; the prose step filters to files still present on disk so Vale
does not error on a missing file.
- **Local-vs-CI parity**: CI lints changed files only; local `make
lint/prose` lints the full tree. This mirrors `make lint/markdown` (full
tree) vs the changed-files CI step. Acceptable for v1.

</details>

---

*Filed via [Coder Agents](https://coder.com/docs/ai-coder/agents) on
Nick's behalf.*
2026-06-22 14:14:31 -04:00
Zach 917dbde439 fix: regen feature stage docs from HEAD & enforce generation (#26528)
Generate the experimental and beta tables in
docs/install/releases/feature-stages.md from the current source tree
instead of release tags + GitHub API because we found the table of beta
features was stale in recent release(s). This approach works now that
Coder publishes per-release docs.

This change was assisted by Coder Agents.
2026-06-22 11:12:26 -06:00
Kyle Carberry e6a9b59abe feat(site/src): surface pinned chat workspace context in the UI (#26573)
Adds the workspace-context indicator UI for agent chats, part of
breaking the "Workspace Context Sources for Coder Agents" RFC (#26466)
into small, reviewable PRs.

## What this adds

- **Pinned context popover**: the context-usage indicator now lists the
chat's pinned resources, instruction files, skills, and MCP servers with
their tools. Unusable resources (invalid skill, unreadable/oversize
file) are surfaced in an "Issues" section with their error rather than
dropped silently.
- **Drift and error states**: when the pinned context differs from the
agent's latest snapshot, the ring shows a warning marker and the popover
explains the drift. A snapshot-level error gets a distinct treatment.
- **Refresh context**: a button re-pins the chat to the agent's latest
snapshot via `PUT /api/experimental/chats/{id}/context`.
- **Live updates**: `context_dirty` watch events apply the lightweight
dirty flags across the cached chat lists and refetch the open chat so
the full pinned detail loads.

## Not included

The context **changes/diff** view (the "View changes" affordance and its
dialog) is intentionally deferred to a later split, so this PR contains
no diff rendering.

## Testing

- `pnpm lint` (types, biome, circular deps, React Compiler, knip)
- `pnpm test src/api/queries/chats.test.ts` (cache-merge unit tests,
including the new `context_dirty` cases)
- `pnpm test:storybook
src/pages/AgentsPage/components/ContextUsageIndicator.stories.tsx` (4
stories)
- `pnpm format`

<details>
<summary>Design notes</summary>

This is **Split 3** of #26466. Split sequence:

1. #26558 - prompt pin consumption (merged)
2. #26570 - `codersdk` context resource types (merged). During review
the type was renamed `ChatContextMCPTool` -> `ChatContextTool` and the
field `mcp_tools` -> `tools`; this PR consumes those merged names.
3. **This PR** - the UI.
4. CLI `coder exp chat context` source CRUD + `refresh` (next).
5. The context diff (`changes`, `ChatContextResourceChange`, the changes
dialog, and `buildContentPatch`) - last.

Key decisions:

- The backend already publishes `ChatWatchEventKindContextDirty` and
exposes the refresh endpoint (#26389). Watch/pubsub payloads stay
lightweight: they carry only the `dirty`/`dirty_since`/`error` flags and
omit `resources`. So `mergeWatchedChatSummary` merges (not replaces) the
cached context to preserve the pinned `resources` a single-chat GET
populated, and the `AgentsPage` watch handler refetches only the open
chat to pull the full pinned detail.
- The indicator prefers the chat's pinned `resources`; while they have
not loaded it falls back to the agent's `last_injected_context`,
skipping the empty context-file placeholder so it never renders a
nameless row.
- All diff/changes rendering is excluded here and lands in the final
split to keep this PR focused on the read-only pinned view and the
refresh action.

</details>

*This PR was created by Coder Agents on behalf of @kylecarbs.*
2026-06-22 11:09:39 -06:00
Kyle Carberry c0f854c289 feat: report pinned chat context resources on chat API (#26570)
Surfaces a chat's pinned workspace-context resources on the single-chat
GET and refresh responses, so clients can show *what* context the prompt
was built from, not just whether it drifted.

## What's included
- **codersdk**: `ChatContextResource` (plus `ChatContextResourceKind`
and `ChatContextResourceStatus`) and `ChatContextMCPTool`, and a new
`Chat.Context.Resources` field (metadata only, no bodies). It is
populated only on the single-chat GET/refresh response; list and watch
payloads stay nil to remain lightweight.
- **coderd/x/chatd**: `Server.ContextResources`, which builds the
metadata-only list from the chat's pinned `chat_context_resources` rows.
Non-OK resources (invalid / unreadable / oversize / excluded) are
reported with their status and error so the UI can explain why a
resource was dropped from the prompt instead of silently omitting it.
The shared protojson body decoders are extracted so the prompt and
detail paths reuse them.
- **coderd**: `getChat` and `refreshChatContext` enrich the response
with the resource list. Failures are non-fatal (the chat stays usable
without the detail).

## Scope / what's deferred
This is an incremental split from #26466. This PR reports only the
**resource inventory**. The pinned-context drift *diff* (the per-source
`changes` set and the "View changes" dialog) is intentionally deferred
to a later split; the existing `dirty` bit already signals that context
changed. MCP resources are reported for display only; they are not
injected into the prompt (a future RFC item).

<details>
<summary>Design notes</summary>

- The resource list is the chat's full pinned inventory (instruction
files, skills, and MCP configs/servers), preserving the query's `source
ASC` order. OK-but-empty instruction files, OK skills with no name, and
untracked kinds (reserved plugin/hook/subagent/command) are skipped.
- MCP tool names are reported with the agent's `"<server>__"` prefix
stripped so they read as the server exposes them.
- The detail is computed on read and attached only on the single-chat
GET and refresh responses; list and watch payloads omit it to stay
lightweight.
- `refreshChatContext` enriches its own response (mirroring `getChat`)
so the client reflects a refresh immediately, without a full reload.
</details>

<details>
<summary>Testing</summary>

- `go test ./coderd/x/chatd/ -run
'TestPinnedContextResources|TestContextResources|TestChatContextDirtyFromAgentPush'`
(unit + integration on embedded Postgres) passes. The integration test
exercises the GET and refresh enrichment end-to-end.
- `go build`, `go vet`, `golangci-lint`, and `gofmt` are clean.
- `make gen` regenerated `apidoc`, `swagger.json`,
`docs/reference/api/*`, and `typesGenerated.ts`.
</details>

---
*This PR was created by Coder Agents on behalf of @kylecarbs.*
2026-06-22 10:00:15 -06:00
Jon Ayers 401aa58eeb feat: add schema changes for autostop notification (#26417) 2026-06-22 10:59:43 -05:00
Danny KoppingandNick Vigilante bd893a4504 docs: restore Bedrock static credentials walkthrough (#26563)
## Summary

Restores the step-by-step "Obtaining static Bedrock credentials"
walkthrough that was present on the v2.32.6 `ai-bridge/setup` page but
missing from the current `ai-gateway/providers` page.

The current page mentions static credentials in a single line but no
longer explains how to create the IAM user and access key in the AWS
console. This PR brings back that walkthrough, adapted to the current
database/dashboard-managed provider flow.

## Changes

- Add an `#### Obtaining static Bedrock credentials` subsection under
the Amazon Bedrock provider section in
`docs/ai-coder/ai-gateway/providers.md`.
- Keep the AWS console steps (choose region, generate API keys, create
access key) from v2.32.6.
- Replace the deprecated `CODER_AIBRIDGE_BEDROCK_*` environment-variable
configuration step with guidance to enter the credentials when
adding/editing the provider via the dashboard or AI Providers API,
matching the post-v2.34 database-managed model.

<details>
<summary>Context and decisions</summary>

- Source: [`docs/ai-coder/ai-bridge/setup.md` at
v2.32.6](https://coder.com/docs/@v2.32.6/ai-coder/ai-bridge/setup)
"Obtaining Bedrock credentials" section.
- The old flow set provider config via environment variables, which are
deprecated since v2.34 (providers are now stored in the database and
managed via dashboard/API). The restored content keeps the AWS-side
credential-creation steps but routes the final configuration step
through the current provider management flow rather than env vars.
- Open questions from
[AIGOV-432](https://linear.app/codercom/issue/AIGOV-432/restore-bedrock-static-credentials-docs-from-v2326)
(whether other pages also need this, and whether the content needs
further accuracy updates) are left for review.

</details>

Closes
[AIGOV-432](https://linear.app/codercom/issue/AIGOV-432/restore-bedrock-static-credentials-docs-from-v2326).

> [!NOTE]
> This PR was generated by Coder Agents on behalf of @dannykopping.

---------

Co-authored-by: Nick Vigilante <nickvigilante@users.noreply.github.com>
2026-06-22 15:51:18 +00:00
Nick Vigilante e458692cb8 refactor(docs): convert absolute coder/coder blob/tree/main links to relative (DOCS-351) (#26341)
Closes [DOCS-351](https://linear.app/codercom/issue/DOCS-351).

> [!WARNING]
> **DO NOT MERGE** until
[DOCS-349](https://linear.app/codercom/issue/DOCS-349)
([coder.com#877](https://github.com/coder/coder.com/pull/877)) has
shipped to production and baked for at least one Vercel cycle.
>
> Without DOCS-349, the relative links in this PR resolve to broken
docs-route URLs (`/docs/helm/coder/values.yaml` -> 404) instead of
GitHub URLs tagged with the displayed docs version. DOCS-349 fixes the
rewriter to classify these as GitHub blob/tree URLs with the page's
resolved ref.

## TL;DR

Converts 121 absolute
`https://github.com/coder/coder/(blob|tree)/main/<path>` links across 39
docs markdown files to relative paths. After this lands AND DOCS-349
deploys, every one of these links will follow the displayed docs version
(mainline tag on bare URLs, explicit tag on `/@vX.Y.Z/`, `main` on
`/@main/`) instead of always pointing to `main`.

## Why

Today a reader on `/docs/@v2.30.0/install/docker` follows a
`compose.yaml` link and arrives at `main`'s `compose.yaml`, which
doesn't necessarily match what the docs page describes. Helm values,
Terraform templates, and source-code references in particular drift
across versions. The fix is to let the coder.com rewriter substitute the
page's resolved ref into the URL; that only works on relative links.

## Example payoff (post-DOCS-349)

| URL | Today (absolute, always `main`) | After (relative + rewriter) |
|---|---|---|
| `/docs/install/docker` |
`https://github.com/coder/coder/blob/main/compose.yaml` |
`https://github.com/coder/coder/blob/v2.34.1/compose.yaml` (today's
mainline) |
| `/docs/@v2.30.0/install/docker` | same as above |
`https://github.com/coder/coder/blob/v2.30.0/compose.yaml` |
| `/docs/@main/install/docker` | same as above |
`https://github.com/coder/coder/blob/main/compose.yaml` |

## Scope

- **121 conversions** across **39 files**.
- Verb breakdown: `tree/main` (directories) and `blob/main` (files),
both flipped to relative paths.
- Line anchors (`#L23-L24`) and query strings preserved verbatim.
- Conversion is mechanical: relative path computed from the doc file's
directory to the target via `os.path.relpath`. Any path starting at the
same directory or below gets a `./` prefix; otherwise `../` chains.

## Rebased on main

The branch was rebased onto `main` after the DOCS-350 hotfix
([#26339](https://github.com/coder/coder/pull/26339)) merged. The hotfix
repointed 3 `docs-backend-contrib-guide` refs in `backend.md` to `main`,
which then needed the same `main` -> relative conversion this PR is
doing for the other 121 links. The conflict was resolved by reapplying
the mechanical conversion to `backend.md` after taking the hotfix's
content. Net result: those 3 links land here as relative, same as
everything else. New HEAD `3f501cb622`.

## Inline fix folded in: dead `nix` link

- `docs/about/contributing/CONTRIBUTING.md:7` -> `../../../nix`

The original absolute URL `https://github.com/coder/coder/tree/main/nix`
already returned 404 today. Repointed to `flake.nix` (modern Nix
entrypoint, what the prose "Nix environment" semantically refers to).
Closes [DOCS-357](https://linear.app/codercom/issue/DOCS-357) here since
the `check-docs` Linkspector job surfaced it during rebase; cheaper to
fix inline than in a separate single-line PR.

## Out of scope (filed separately)

- [DOCS-350](https://linear.app/codercom/issue/DOCS-350): 3 dead
`docs-backend-contrib-guide` branch refs in `backend.md`
([#26339](https://github.com/coder/coder/pull/26339), merged).
- [DOCS-352](https://linear.app/codercom/issue/DOCS-352): 10 SHA-pinned
`(blob|tree)/<sha>` links pending intent review.
- [DOCS-355](https://linear.app/codercom/issue/DOCS-355): code-server
analog (4 absolute `(blob|tree)/main` links in `coder/code-server`).
- [DOCS-356](https://linear.app/codercom/issue/DOCS-356): 2 upstream
content bugs in `coder/code-server/docs/CONTRIBUTING.md` (independent of
this PR).


## Not triggering `/coder-agents-review`

Docs-only edit; per `AGENTS.md` the bot review is reserved for
product/CI changes.

## Pre-mortem

| Concern | Mitigation |
|---|---|
| Merging before DOCS-349 deploys regresses ~120 currently-working links
into 404s on coder.com | Clear DO-NOT-MERGE banner; tracked as blocker
in Linear. |
| Relative path computed incorrectly (off-by-one `..`) | Verified all
114 newly-relative non-md/non-image paths resolve to existing files in
the repo (only exception is the pre-existing dead `nix` link above). |
| Line anchors stripped during conversion | Preserved by the
substitution regex; verified `#L<n>-L<m>` cases in `airgap.md` and
`speed-up-templates.md`. |
| Future code reorgs change file locations | Relative links will start
pointing to nothing. Same failure mode as absolute links pointing to
renamed files; can be caught with a future link-checker job. |

## Validation

```
$ grep -rE 'github\.com/coder/coder/(blob|tree)/main' docs --include="*.md" | wc -l
0
$ git diff --stat origin/main | tail -1
39 files changed, 118 insertions(+), 118 deletions(-)
```

114 newly-relative paths verified to resolve to existing repo files
(Python `os.path.exists` check on each computed target).

<details>
<summary>Decision log + planning context</summary>

**Why relative over `(blob|tree)/{{currentDocsVersion}}/...`
templating**: relative paths require zero markdown-system support and
zero upstream churn beyond this one PR. Templating would require a
preprocessor on `coder.com` side AND a convention upstream authors have
to remember; relative paths just work in a plain editor and
`github.com`'s own renderer too.

**Why `./` prefix on same-directory targets**: makes the conversion
grep-able later (`grep -E '\((\.\./|\./)'`).

**Why preserve `#L<n>-L<m>` anchors verbatim**: the anchor is meaningful
to the linked file's content, not to the URL form; keeping it as-is
preserves authorial intent. If the file later changes such that the line
range drifts, that's a different problem the SHA-pin audit
([DOCS-352](https://linear.app/codercom/issue/DOCS-352)) will surface.

</details>

---

*Generated by Coder Agents on @nickvigilante's behalf.*





## Drive-by external link fix folded in

`docs/about/contributing/CONTRIBUTING.md:296` cited
`https://reflectoring.io/meaningful-commit-messages/` which is returning
HTTP 503 (the host appears to be down site-wide right now). `check-docs`
Linkspector flagged it after the rebase. Replaced with
`https://cbea.ms/git-commit/` (Chris Beams' canonical "If applied, this
commit will..." article, confirmed 200), which is the original source of
the rule the prose recites anyway.
2026-06-22 11:39:12 -04:00
Kyle Carberry 46916bf899 feat(coderd/x/chatd): consume the pinned chat context in prompt generation (#26558)
## What

`prepareGeneration` now builds the system-prompt instruction block and
workspace skills from a chat's **pinned context copy**
(`chat_context_resources`, populated in #26438) instead of re-scanning
per-turn history, when the chat has a pinned copy. This is the first
production reader of the pin.

Selection is **presence-based, no experiment**: a chat with pinned rows
builds its prompt from the pin; a chat without them falls back to the
existing per-turn history path. The two paths are mutually exclusive, so
older agents that never report context keep their current behavior and
the per-turn pull stays as the fallback.

## How

- `contextResourcesToPrompt` maps the protojson resource bodies
(instruction files and skills) into the instruction block and skill
metadata, skipping non-OK statuses, non-prompt body kinds, and malformed
bodies (the malformed count is logged so a proto/encoding regression
cannot silently drop context).
- `pinnedWorkspaceContext` reads the pin and reports `ok=false` (history
fallback) when there are no pinned rows; read errors propagate. The
bound agent only decorates the instruction header with OS and directory,
so the pin still resolves when the workspace is unreachable.
- `resolveTurnWorkspaceContext` dispatches between the pinned and
history paths; `prepareGeneration` calls it.

## Testing

- `go test ./coderd/x/chatd/` for `TestContextResourcesToPrompt`,
`TestPinnedWorkspaceContext` (incl. `...FromHydratedPin` against real
Postgres), and `TestResolveTurnWorkspaceContext`: pass.
- `make gen` (no drift), `golangci-lint`, `gofmt`, emdash scan, and `go
build`/`go vet` on `./coderd/x/chatd/...`: all clean.

## Scope

This is the foundational backend slice split from #26466 (the full-stack
staging PR). It changes no API surface, schema, proto, or generated
files. The remaining pieces land as follow-ups in dependency order:

1. `ChatContext` drift/diff API (`resources` + `changes`,
`ContextDetail`). This also extracts the body decoders inlined here so
they are shared with the diff path.
2. Context-ring drift indicator, changes dialog, and refresh (UI).
3. In-workspace `coder exp chat context` source CRUD and `refresh`
(CLI).

<details>
<summary>Why this is the first split</summary>

The coderd hydration, the `PUT /chats/{id}/context` refresh endpoint
(#26389), the `chat_context_resources` table (#26430), and the
copy-into-pin logic (#26438) are already merged, as is the agent-side
push (#26526, #26533). Consuming the pin in prompt building is the step
#26438 explicitly deferred, and it is the bottom of the remaining
dependency stack: the drift/diff API, the UI indicator, and the CLI are
only meaningful once the chat actually builds its prompt from the pin.
Keeping it presence-based means it is independently revertable and
leaves the per-turn pull intact as a fallback, matching the RFC's
Release 3 rollout.

The files are taken verbatim from the reviewed #26466 boundary commit
(before the diff-API work began), so the deep-review feedback already
applied there (CRF-1 through CRF-10) is preserved.

</details>

---

*This PR was created by Coder Agents on behalf of @kylecarbs.* Split
from #26466.
2026-06-22 08:51:57 -06:00
Ehab Younes f5cb2e547e feat: include rotated agent logs in support bundles (#26055)
Support bundles previously captured only the active coder-agent.log, losing
history across agent restarts. Add an optional `after` filter to the agent's
/debug/logs endpoint: without it the endpoint is unchanged (active log only,
10 MiB cap); with it the response includes the active log plus rotated
coder-agent-*.log files modified after the cutoff, newest first. Support
bundles request the last 24h.

Closes #25395
2026-06-22 16:38:18 +03:00
Sas Swart adad5bdd49 feat: surface agent firewall correlation in AI Bridge sessions API (#26416)
Add `agent_firewall_session_id` and `agent_firewall_sequence_number`
fields to `AIBridgeThread` in the `GET
/api/v2/aibridge/sessions/{session_id}` response. These fields link each
thread to its agent firewall confinement session so the frontend can
discover the boundary session and compute sequence ranges for
interleaving firewall events within the thread timeline.

The database columns already exist on `aibridge_interceptions`
(migration 000520) and are already selected by
`ListAIBridgeSessionThreads`. This PR surfaces them through the SDK type
and the `db2sdk` conversion.

Depends on #24814

**Naming note:** The RFC uses `boundary_session_id` /
`boundary_sequence_number`, but the codebase standardized on
`agent_firewall_*` naming in the DB migration. The API fields follow the
existing convention.

</details>

> [!NOTE]
> This PR was authored by Coder Agents.
2026-06-22 15:17:37 +02:00
Sas Swart 335d6bda1b feat: add GET /api/v2/agent-firewall/sessions/{id}/logs endpoint (#24816)
Add a `GET /api/v2/agent-firewall/sessions/{id}/logs` endpoint that
returns agent firewall audit logs for a given session, sorted by
sequence number ascending.

The endpoint supports `seq_after` and `seq_before` (exclusive bounds)
and `limit` query parameters. This enables the frontend to fetch exactly
the firewall events that fall between two AI Bridge interceptions within
a thread, as described in FR 4 of the Boundary/Bridge correlation RFC.

Authorization reuses the `boundary_log` RBAC resource (owner and auditor
can read; members cannot). Returns 404 for unauthorized users to avoid
leaking existence information.

The endpoint is enterprise-only, gated behind `FeatureBoundary`
entitlement, matching the session endpoint from #24814.

Depends on #24814

> [!NOTE]
> This PR was authored by Coder Agents.
2026-06-22 13:56:29 +02:00
Ehab Younes c0b8fa9418 feat(site): add user AI budget override UI (#26402)
Add a per-user AI budget override dialog in the group members menu, scoped to the group's organization.

Refs AIGOV-295
2026-06-22 13:58:53 +03:00
Danny Kopping 4f8acfaeff docs: add Codex WebSocket fallback troubleshooting (#26565)
## Summary

Adds a Troubleshooting section to the Codex CLI AI Gateway client docs
covering the WebSocket-to-HTTPS transport fallback.

Recent Codex CLI versions default to the WebSocket runtime for the
Responses API. AI Gateway does not support WebSocket transport, so each
request attempts a WebSocket connection, fails, and falls back to HTTPS,
surfacing:

```text
Falling back from WebSockets to HTTPS transport.
```

The doc explains the cause and the fix: set `supports_websockets =
false` in the `[model_providers.ai_gateway]` block in
`~/.codex/config.toml` to force HTTPS directly and remove the fallback
delay.

Closes
[AIGOV-453](https://linear.app/codercom/issue/AIGOV-453/document-codex-cli-websocket-fallback-workaround).

<details>
<summary>Note on the config value</summary>

The original request and the Linear issue referenced enabling websocket
support / `support_websockets = false`. The authoritative Codex CLI
[config reference](https://developers.openai.com/codex/config-reference)
confirms:

- The key is `supports_websockets` (trailing "s").
- It declares whether a provider supports the Responses API WebSocket
transport.
- Setting it to `false` is the documented workaround to force HTTPS and
stop the fallback attempts.

Since AI Gateway does not support WebSockets, `supports_websockets =
false` is the correct value. `= true` would assert support that does not
exist and keep the fallback happening.

</details>

---

This PR was generated by Coder Agents on behalf of @dannykopping.
2026-06-22 11:56:58 +02:00
8b970e7ff3 docs: clarify Agents vs Chats API reference pages (#26021)
## Problem

The REST API reference page at
[`/docs/reference/api/agents`](https://coder.com/docs/reference/api/agents)
is confusing: by the name alone, a reader looking for the *AI Coder
Agents* programmatic API would assume this is the right page. In fact,
those endpoints are for the *workspace agent daemon* (the `coder_agent`
Terraform resource / `workspaceagent` daemon). The actual AI Coder
Agents API is documented at
[`/docs/reference/api/chats`](https://coder.com/docs/reference/api/chats).

Both pages compound the confusion by being rendered with a bare `#
Agents` / `# Chats` heading and no descriptive intro. The sidebar
entries are similarly ambiguous (`Agents` and `Chats` with no
descriptions).

## Root cause

The reference pages are generated by `scripts/apidocgen/generate.sh`
(swag → widdershins → postprocess). The widdershins template
(`scripts/apidocgen/markdown-template/main.dot`) already renders
`data.resource.description` directly under each section heading:

```
<!-- APIDOCGEN: BEGIN SECTION -->
{{= data.tags.section }}# {{= r}}

{{? data.resource.description }}{{= data.resource.description}}{{?}}
```

…but the swag annotations in `coderd/coderd.go` never declared
`@tag.name` / `@tag.description` for any tag, so the descriptions were
always empty.

## Changes

- `coderd/coderd.go`: add `@tag.name Agents` / `@tag.description …` and
`@tag.name Chats` / `@tag.description …` annotations next to the
existing `@title` / `@version` block.
- `docs/manifest.json`: rename the sidebar entry `Agents` → `Workspace
Agents` and add `description` fields to both API sidebar entries (every
other top-level section in the manifest has descriptions; the API
children did not).
- Regenerate `coderd/apidoc/swagger.json`, `coderd/apidoc/docs.go`,
`docs/reference/api/agents.md`, and `docs/reference/api/chats.md` via
`scripts/apidocgen/generate.sh` + `pnpm exec markdownlint-cli2 --fix` +
`pnpm exec markdown-table-formatter` + `scripts/biome_format.sh`
(matching the Makefile's `coderd/apidoc/.gen` pipeline).

Resulting diff is intentionally minimal — 6 files, 35 insertions / 3
deletions.

## After this PR

The Agents page will render:

> # Agents
>
> Workspace agent endpoints. These power the workspace agent daemon
defined by the `coder_agent` Terraform resource (sometimes called the
workspace daemon). This API is NOT the AI Coder Agents API. For
programmatic access to AI Coder Agents (formerly Tasks), see the Chats
API.

The Chats page will render:

> # Chats
>
> Programmatic API for Coder AI Agents (the user-facing "Coder Agents" /
"Chats" product). Experimental. Use these endpoints to create, list, and
manage AI coding agent sessions. For background and migration from the
Tasks API, see the AI Coder docs.

And the sidebar entry for the workspace-agent endpoints becomes
`Workspace Agents` instead of `Agents`.

## Out of scope (potential follow-ups)

- `docs/reference/api/chat.md` is a 7-byte stub — likely dead. Could be
deleted in a follow-up.
- Larger rename of the `Agents` Swagger tag (and/or the `coder_agent`
Terraform resource) to something like `Workspace Agents` /
`workspace_daemon` would more thoroughly fix the naming collision, but
that's a much bigger change.

Created on behalf of @mattvollmer.

---------

Co-authored-by: blink-so[bot] <211532188+blink-so[bot]@users.noreply.github.com>
Co-authored-by: Matt Vollmer <matthewjvollmer@outlook.com>
Co-authored-by: Atif Ali <atif@coder.com>
2026-06-22 07:43:13 +00:00
Ethan bc9cc8cb08 fix(enterprise/coderd): allow deleting external-agent workspaces (#26501)
I stumbled on this while manually testing another workspace change: if a
license expires after an external-agent workspace exists, deleting that
workspace is rejected because `CheckBuildUsage` enforces the
external-agent entitlement for every workspace transition. I assume this
is unintentional from a product POV.

This PR narrows the external-agent entitlement check to start builds.
Creating or rebuilding external-agent workspaces still requires the
feature entitlement, but stop and delete transitions stay available as
cleanup paths.

That matches the managed-agent entitlement precedent (i.e. the usage
billing check) in the same `CheckBuildUsage` path, where license
enforcement is scoped to start transitions instead of trapping users
with resources they can no longer remove.
2026-06-22 12:02:01 +10:00
Kyle Carberry 2f8bba792a feat: push MCP server context and tools from agentcontext (#26533)
## What

Live MCP servers and their tools now flow into the `agentcontext`
snapshot and are pushed to coderd via `PushContextState`, stored
alongside instruction files and skills. Previously the resolver's MCP
seam was unimplemented, so live MCP tool lists never reached the pushed
snapshot.

`agentcontext` is now **fully self-contained** for MCP: it connects to
the MCP servers declared in the `.mcp.json` files its own watcher
already discovers, lists their tools, and emits `KindMCPServer`
resources. It does **not** depend on or modify `agent/x/agentmcp` — that
package is left pristine and keeps serving the agent's MCP HTTP API. The
two MCP paths run independently, which means the legacy package can be
deleted later without touching this code.

## How

- **Self-contained runner** (`agentcontext/mcprunner.go`): a one-shot
MCP client (connect → initialize → list tools → close) with its own
`.mcp.json` parser. A Manager goroutine (`runMCPSync`) reloads it
whenever the discovered `KindMCPConfig` `path:contenthash` set changes,
then re-resolves so the new tools are published. Per-server connects run
in parallel (bounded) with a per-server timeout; a server that fails to
connect is recorded as a failure rather than aborting the batch. Each
connect also force-kills its subprocess on close, because mcp-go's stdio
`Close()` closes stdin and then blocks on `cmd.Wait()` with no kill — a
server that ignores stdin-close would otherwise stall the whole reload
loop.
- **Resource production** (`agentcontext/mcp.go`):
`buildMCPServerResources` turns the runner's non-blocking per-server
snapshot into `KindMCPServer` resources. Connected servers carry their
sorted tools (`StatusOK`); failed servers surface as `StatusUnreadable`
issues instead of vanishing; connected-but-no-tools-yet are skipped
until a later reload. The content hash is tool-set sensitive. The
resolver consumes this through a plain `MCPResources func() []Resource`
field (no `MCPProvider` interface).
- **Tool names**: emitted exactly as the server reports them. Flattening
into a single namespace (e.g. `server__tool`) is left to the control
plane in the next step, since each resource already carries the server
name.
- **Drift**: MCP resources are excluded from the snapshot
aggregate/drift hash (`driftResources`). MCP servers connect
asynchronously after boot; without this, a server finishing its connect
would dirty every hydrated chat even though nothing the user pinned
changed.
- **Wiring** (`agent.go`): the manager is given
`ManagerOptions.MCPExecer`/`MCPUpdateEnv`; `agent/x/agentmcp` is
untouched.
- **Config validation**: a structurally broken `.mcp.json` surfaces as
`StatusInvalid` rather than silently dropping all its servers.

coderd already persists `mcp_server`/`mcp_config` resource bodies
(including tools), so no coderd or proto changes were required.

## Testing

- **Unit**: `buildMCPServerResources` (grouping/sort/skip/failed/hash
sensitivity), MCP resources applied via the resolver seam, MCP exclusion
from the aggregate hash, `.mcp.json` parsing (transport inference, env
expansion), `toolInputSchema`, and `mcpConfigSet` change detection.
- **Proto serialization**
(`TestDRPCPusher_HappyPathSerializesAllFields`): a `KindMCPServer`
resource (tools + input schema) round-trips through `PushContextState`
into the `MCPServerBody` wire form, asserting the server name, tool
name/description, and the decoded `input_schema`.
- **Manager-level, real subprocess**
(`TestManager_MCPServerToolsInSnapshot`): a `.mcp.json` points at a
re-exec'd fake stdio MCP server; the runner connects it and its `echo`
tool surfaces as a `KindMCPServer` resource in the Manager snapshot —
the same snapshot pushed to coderd — exercising `runMCPSync` and the
resolver wiring end to end.
- **Regression** (`TestManager_MCPServerHangingCloseDoesNotStall`): the
fake server ignores stdin-close; the test asserts its tool still
surfaces, proving the runner force-kills the subprocess instead of
stalling the reload. Verified to fail without the fix.
- All pass under `-race`; `go build ./...`, `go vet`, and
`golangci-lint` are clean on the touched packages.

## Scope / follow-ups

This is the agent-side production+push half. The chatd consumer (reading
the pinned MCP resources for prompt/tool injection, including any
server-prefix flattening of tool names) and removing the legacy
`workspaceMCPToolsCache` pull path remain follow-ups, per the RFC
rollout. While both `agent/x/agentmcp` and `agentcontext` exist, stdio
MCP servers are spawned by both; this is intentional and temporary until
`agentmcp` is removed.

<details>
<summary>Implementation plan and decisions</summary>

**Goal:** produce live MCP server resources (with tools) from
`agentcontext` and push them to coderd.

**Starting state (main):** proto (`PushContextState`, `MCPServerBody`,
`MCPTool`), the drpc adapter, coderd storage
(`workspace_agent_context_resources`, body kind `mcp_server`), and the
resolver's MCP seam already existed; nothing implemented the seam or fed
live tools into the snapshot.

**Decision (agentcontext fully separate from agentmcp):** `agentcontext`
starts and lists its own MCP servers using only the connect-and-list
half of an mcp-go client, driven by the `.mcp.json` files its existing
watcher discovers. It shares no state with `agent/x/agentmcp` and does
not import it. Two earlier revisions of this branch were discarded: (1)
relocating `agentmcp` into `agentcontext` (rejected — it duplicates
config parsing and file watching `agentcontext` already does); (2)
reading `agentmcp`'s cached server snapshot via new accessors (rejected
— unnecessary coupling between two packages that should simply run
independently while one is being retired). The temporary double-spawn of
stdio servers is the accepted cost of keeping the two paths cleanly
separated until `agentmcp` is removed.

**Decision (no tool-name prefixing, no MCPProvider interface):** the
agent pushes raw, unflattened data — server name plus verbatim tool
names — and lets the control plane own any `server__tool` flattening.
With a single self-contained producer, the `MCPProvider` interface was
collapsed into a `func() []Resource` field on the resolver.

**Invariants held:** no secrets (env/headers) in pushed resources, only
server/tool metadata; MCP excluded from the drift hash; the seam is
non-blocking so the resolver never stalls on MCP I/O.

</details>

---

*This PR was created by Coder Agents on behalf of @kylecarbs.*
2026-06-21 16:31:05 -06:00
Cian Johnston d5ec26beac chore: replace testing.Testing with flag lookup (#26552)
In our codebase we have an existing convention of using
`flag.Lookup("test.v")` instead of `testing.Testing()`. This avoids
pulling in the entire `testing` package. Another consequence: some of
our custom linters trigger upon import of the `testing` package which
can lead to unexpected linter errors.
2026-06-19 19:59:54 +01:00
Cian Johnston befa1176b9 chore(dogfood): tighten template dormancy settings (#26421)
Tightens dormancy settings for all templates managed in this repo to 30
days / 30 days for time till dormancy and dormancy autodeletion,
respectively.

This was apparently done manually previously but ended up being
automatically reverted.
2026-06-19 15:07:53 +01:00
Danny Kopping 6186532cec fix: negative metric counter increment from token arithmetic (#26547)
_Disclosure: produced using Claude Opus 4.8_

Closes
[AIGOV-452](https://linear.app/codercom/issue/AIGOV-452/prevent-control-plane-panic-on-negative-cached-tokens)

Also addresses a similar shortcoming in
`aibridge/intercept/responses/base.go` and aligns token recording
approach for chatcompletions with other implementations

---------

Signed-off-by: Danny Kopping <danny@coder.com>
2026-06-19 15:44:04 +02:00
Cian Johnston a12b051834 chore: skip failing azureidentity test while under investigation (#26545)
Skips the failing azureidentity test while under investigation.
Ref: https://github.com/coder/internal/issues/1602
2026-06-19 12:49:49 +01:00
Cian Johnston fc83d77189 ci: correct location of redirects.json in weekly-docs workflow (#26542) 2026-06-19 12:03:55 +01:00
Marcin Tojek a2b680ab29 fix: sanitize MCP tool names to satisfy LLM provider constraints (#26539)
Fixes #26325
2026-06-19 11:48:30 +02:00
Susana Ferreira 4aa2482e93 fix: alias coder testutil to resolve import collision (#26540)
PRs https://github.com/coder/coder/pull/26092 and
https://github.com/coder/coder/pull/26519 both landed on main and the
circuit breaker test file ended up importing both
`aibridge/internal/testutil` and `coder/v2/testutil` under the same
name, causing a redeclaration error and breaking `make lint`.

Alias the latter as `codertestutil`, matching the convention already
used in `passthrough_internal_test.go` and `keyfailover_test.go`.
2026-06-19 09:23:18 +00:00
Susana Ferreira b0b698c643 fix(aibridge): increase circuit breaker test timeout to prevent flake (#26519)
`TestCircuitBreaker_FullRecoveryCycle/OpenAI` flaked once on macOS CI.
The most likely cause is that the circuit breaker `Timeout`
(open-to-half-open transition) was too short relative to the time
between test phases. On a slow runner, the breaker could transition to
half-open before the test verified it was still open, so the request
went through as a half-open probe instead of being rejected.

Increases `Timeout` to `testutil.IntervalMedium` (250ms) across all
circuit breaker integration tests.

**Note:** Ideally, these tests would use a mock clock for deterministic
timing, but https://github.com/sony/gobreaker (the library used for
circuit breaker logic) uses real time internally and doesn't expose a
clock interface.

Closes https://linear.app/codercom/issue/AIGOV-438

> Generated with [Coder Agents](https://coder.com/agents) on behalf of
@ssncferreira
2026-06-19 09:23:57 +01:00
Susana Ferreira fec21e1a28 refactor(aibridge): apply key pool failover follow-ups (#26130)
Applies follow-ups from the key pool failover work:

- Add a test verifying key pool state is shared across bridged and passthrough routes.
- Refactor the key failover and passthrough tests to use the shared `MockUpstream` helper.
- Simplify how the request body option is passed through the Anthropic messages interceptor.
- Make `ResponseErrorFromKeyPool` nil-safe and cover it with a test.

Closes: https://linear.app/codercom/issue/AIGOV-398/small-follow-up-cleanups-for-key-failover

> [!NOTE]
> Initially generated by Claude Opus 4.7, modified and reviewed by @ssncferreira
2026-06-19 09:01:39 +01:00
Susana Ferreira 19aa9f5616 refactor: separate aibridge provider and interceptor configs (#26092)
## Description

Separates the aibridge provider configuration from the per-request configuration an interceptor actually needs, and introduces a single `Credential` type that each provider resolves per request. Previously a provider handed its full config to the interceptor (including fields the interceptor didn't use) while other request data was passed as loose arguments, and authentication was spread across config fields and arguments.

## Changes

- Add `intercept.Config`: the per-request, provider-agnostic configuration an interceptor needs (`ProviderName`, `BaseURL`, `APIDumpDir`, `SendActorHeaders`).
- Introduce a single `Credential` interface (`BYOK` and `Centralized`) that each provider resolves per request in `resolveCredential`, and have interceptors route on the credential kind.
- Fail fast with `ErrNoCredential` when a request is neither BYOK nor backed by a centralized key pool.
- Remove unused provider config fields (`Key`, `BYOKBearerToken`, `ExtraHeaders`).

Closes: coder/aibridge#266
Closes: https://linear.app/codercom/issue/AIGOV-221/refactor-separate-provider-and-interceptor-configs

> [!NOTE]
> Initially generated by Claude Opus 4.7, modified and reviewed by @ssncferreira
2026-06-19 08:48:03 +01:00
Andrew Aquino 0d573587e8 feat(site): align ModuleSelection icon with top line when text wraps (#26531)
<img width="1840" height="1191" alt="image"
src="https://github.com/user-attachments/assets/3095d1a7-0f34-4a9a-9dbd-7b9cc524af84"
/>

Do you think we should also top-align the deselect button that displays
on hover?
2026-06-19 00:50:06 +00:00
Hugo Dutka ba860271b0 chore(coderd/x/chatd): clean up the message part buffer comments and implementation (#26508)
Address deferred review feedback for `messagepartbuffer` by documenting
its episode lifecycle, extracting the repeated episode lookup and
finalization helpers, and documenting subscriber channel buffering
decisions.

Addresses these PR #26109 comments:

- https://github.com/coder/coder/pull/26109#discussion_r3379915812
- https://github.com/coder/coder/pull/26109#discussion_r3379988015
- https://github.com/coder/coder/pull/26109#discussion_r3380010948
- https://github.com/coder/coder/pull/26109#discussion_r3380029684
2026-06-18 22:45:01 +02:00
Hugo Dutka 56eb705b8c fix(coderd/x/chatd): reset auto archive ticker after runs (#26512)
Prevents slow chat auto-archive runs from causing a constant archival
loop by resetting the ticker only after each run completes.

Also documents the UTC midnight cutoff used for archive eligibility so
chats with activity on the same UTC calendar date stay eligible or
ineligible for the full day.

Addresses deferred review comments:
- https://github.com/coder/coder/pull/26109#discussion_r3380197310
- https://github.com/coder/coder/pull/26109#discussion_r3380219922

Generated by Coder Agents and closely reviewed by Hugo.
2026-06-18 22:44:02 +02:00
Zach 76fa1d4577 fix(coderd): show correct deletion time in dormancy notification (#26488)
The dormancy notification's "will be automatically deleted in X"
sentence rendered the dormancy threshold instead of the auto-delete
duration. A 30-day threshold rendered as "4 weeks" even when auto-delete
was 90 days; a 60-day threshold rendered as "1 month" with a 7-day
auto-delete. Render the countdown from the auto-delete setting, and skip
the deletion sentence entirely when auto-delete is disabled so the
notification no longer promises a deletion that will never happen.
2026-06-18 20:17:37 +00:00
Kyle Carberry 992b1ffed1 feat(agent): serve context sources over the agent socket (#26526)
## Overview

Split from #26466, scoped to **agent-only** changes. This PR exposes the
agent's context sources and snapshots over the existing agent socket.
There
are no changes outside `agent/`.

## What's included

- **agentsocket**: context source CRUD (`ContextSources`,
`GetContextSource`,
`AddContextSource`, `RemoveContextSource`) plus `GetContextSnapshot` and
`ResyncContext` RPCs, with matching client methods and proto. The server
receives the context `Manager` via `WithContextManager` and returns a
clean
  error when it is absent.
- **agentcontext**: the resync JSON response now carries the
per-resource
  `Name`, keeping the HTTP resync payload in sync with the drpc
  `PushContextState` path in `agentsocket`.
- **agent**: passes the context `Manager` to the socket server via
  `WithContextManager`.

## What's intentionally NOT here

- No MCP wiring. There are no MCP additions in `agent.go` or
`agentcontext`.
MCP ownership will land later in `agentcontext`; this PR does not build
on
  `agent/x/agentmcp`.
- No changes to `agent/x/agentmcp` or the `agentcontext` resolver. The
socket
  serves whatever context resources the `Manager` already resolves.

<details>
<summary>Context for reviewers</summary>

This is one of several PRs split out of #26466. Earlier revisions also
wired
live MCP servers through the socket; that scope was removed so this PR
stays
purely socket + context plumbing inside `agent/`. The agentcontext
resolver,
`agent/x/agentmcp`, and `agent.go` MCP startup behavior are unchanged
from
`main`.
</details>

---
_Created by Coder Agents on behalf of @kylecarbs._
2026-06-18 13:00:28 -07:00
Andrew Aquino 61fa2ab878 fix(site): set external auth provider polling status individually (#26313)
fixes #22420
ref DEVEX-369
ref DEVEX-269

The bug on `CreateWorkspacePage`, where clicking one external auth
provider login button disabled all providers' login buttons, was caused
by providers all sharing a single polling status (`"idle" | "polling" |
"abandoned"`) in the `useExternalAuth` hook.

## changes

- Instead of setting one status across all providers, the polling status
in `useExternalAuth` is now tracked for each provider in a record whose
keys are the providers' IDs.
- The biggest diff is a new Storybook file
CreateWorkspacePage.stories.tsx which reproduces the bug behavior from
the issue.
- Until now we've only had CreateWorkspacePageView.stories.tsx, which
isn't able to model the user interactions / API responses needed to
verify the bugfix. This file is unchanged.
- Also deletes `CreateWorkspacePage`'s `useExternalAuth` hook in favor
of the global `useExternalAuth` hook (see #26310)

(co-written with Coder Agents)
2026-06-18 11:58:38 -07:00
Sas Swart 491a75294e feat: GET /api/v2/agent-firewall/sessions/{id} (#24814)
Add a GET endpoint at `/api/v2/agent-firewall/sessions/{id}` that
returns agent firewall session metadata (`id`, `workspace_id`,
`owner_id`, `confined_process`, `started_at`). The handler authorizes
against the `boundary_log` resource with `ActionRead` via dbauthz.

The endpoint is enterprise-only, gated behind the `FeatureBoundary`
entitlement.

The `GetBoundarySessionByID` SQL query JOINs through `workspace_agents`
→ `workspace_resources` → `workspace_builds` → `workspaces` to return
`workspace_id` and `workspace_owner_id` directly, avoiding a separate
query.

Also adds an `owner_id` column to the `boundary_logs` table (migration
000526) with a FK to `users(id)` and a backfill from
`boundary_sessions`. This enables user-scoped RBAC authorization for
`InsertBoundaryLogs` via `.WithOwner()`, ensuring workspace agents can
only insert logs for their own owner.

Depends on #24810

**RBAC behaviour:**

| Role    | Result |
|---------|--------|
| Owner   | read   |
| Auditor | read   |
| Member  | 404    |

> [!NOTE]
> This PR was authored by Coder Agents.
2026-06-18 20:50:17 +02:00
Jeremy RuppelandAndrew Aquino 20ed45cac7 feat(site/src/pages/TemplateBuilder): add ModuleSettingsStep (#26428)
Implement the module settings wizard step, which renders a
`ModuleConfiguration` card per selected module with variable
configuration fields.

- Map non-sensitive variables to `ConfigurationFieldDefinition` (switch
for bool, text input for string/number)
- Show info notice with `code` tags for sensitive variables that will be
collected from developers at workspace creation
- Disable Continue button until all required non-sensitive variables
across all selected modules have values (`moduleSettingsComplete`
helper)
- Step is automatically skipped when no selected modules have
configurable variables

Relates to [DEVEX-286](https://linear.app/codercom/issue/DEVEX-286).

> [!NOTE]
> This PR was authored with Coder Agents.

---------

Co-authored-by: Andrew Aquino <dawneraq@gmail.com>
2026-06-18 13:47:08 -04:00