mirror of
https://github.com/coder/coder.git
synced 2026-09-01 14:53:15 +08:00
20ca63d1bca8b3a0fbf9f2571556253ff7ab9e3b
16086 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
20ca63d1bc |
test(site/src/api): expect v2 MCP ACL path (#28657)
> 🤖 This PR was written by Coder Agents on behalf of Jake Howell.
The MCP server ACL client uses the promoted `/api/v2` route, but its
path assertion still expected the experimental compatibility route.
Update the assertion to match the v2 endpoint and restore the JavaScript
test suite.
|
||
|
|
2a8e390f0c |
refactor: replace most usage of ptr.Ref with new (#28282)
|
||
|
|
2eee703ec4 |
feat(site/src/pages/AgentsPage): add organization filter to compaction settings (#28559)
Adds an organization picker above the compaction thresholds table on `/agents/settings/compaction`, matching the organization dropdowns in the agents admin area (`OrganizationAutocomplete`, as used by the Models and MCP Servers pages). The picker only appears when enabled models span more than one organization, defaults to the default organization, and the table shows the selected organization's models. Save tracking still covers all models so an edited row hidden by the picker is not dropped. Also removes the organization name text under each model badge (the organization remains in the accessible labels to disambiguate duplicate model names) and changes the model badge size from `sm` to `md`. _This PR was generated by Coder Agents on behalf of @tracyjohnsonux._ |
||
|
|
35cbca080e |
feat: allow sharing MCP servers with users and groups (#28593)
## Summary Adds the missing user-facing half of MCP server config ACLs: admins can now share an MCP server with individual users and groups from the UI, backed by a new permission-safe candidate-discovery endpoint. ## Problem The MCP server config ACL backend already existed (share RBAC action, hydrated `GET .../acl`, sparse `PATCH .../acl`), but there was no frontend for it. There was also no way to populate a sharing autocomplete without the generic organization member/group APIs, which require `organization_member:read` and `group:read` and would wrongly couple MCP sharing to the workspace-sharing mode (the same bug fixed for chat models in #28542). ## Changes Backend: - New v2-only `GET /api/v2/organizations/{organization}/mcp-servers/{mcpserverconfig}/acl/available` returning `codersdk.ACLAvailable`; because this API is new, it is not mounted under the experimental compatibility prefix. Gated on `ActionShare` for the specific server config; performs bounded org-scoped member/group lookups via `dbauthz.AsSystemRestricted`; excludes system users; supports `q`, `limit`, `offset`, and `after_id` with template/chat-model autocomplete semantics. - Tests cover authorization (404 without share), search, pagination, system-user exclusion, org scoping, and a share-only custom role under all three workspace-sharing modes (`none`, `service_accounts`, `everyone`). Frontend: - `shareMCPServerConfig` permission plumbing; the MCP servers list, details page, and AI settings sidebar now admit share-only users (share does not require update; share-only users cannot edit the server form). - Share-only access follows the established permission contract: a bare-share role manages sharing through the list and ACL endpoints (the detail route still requires read, update, or delete, matching the existing enterprise permission-matrix tests), and the disabled-config gate now also admits the share permission so a sharer with read access can open a disabled server. Top-level navigation (Admin settings menu and the /ai/settings index redirect) discovers organization-level MCP sharers and surfaces permission lookup failures instead of silently falling back. - "Share server" action on the MCP server edit page opening a sharing dialog: hydrated ACL grants render from the `GET .../acl` response, adds/removals are saved as sparse `PATCH` deltas (`"read"` / `""`). - Principal autocomplete backed exclusively by the new `/acl/available` endpoint; the sharing flow never calls the generic org member/group APIs (Storybook enforces this by rejecting those API spies). A discovery error surfaces under the autocomplete without hiding existing grants. - Storybook interaction coverage for the dialog (hydrated rendering, add/remove, sparse deltas, error/cancel/reopen paths, autocomplete exclusion and failure) and share-only access to the page/form. Dogfood UAT ran remotely against a dev instance and passed, including verifying via the network log that candidate discovery only hits `/acl/available`. The share-only custom role UI flow could not be exercised there (no premium license on the dev instance); it is covered by the backend permission-matrix tests. > 🤖 This PR was authored by Xum (an AI coding agent) acting on Mike's behalf. |
||
|
|
f75854699d |
fix(site): keep at least 8 characters of the model name visible in the model selector (#28487)
## Problem When the composer's button area fills up (badges, workspace pill, MCP servers), the model selector trigger in the chat input collapses down to just the provider icon and chevron — the model name is truncated away entirely. The trigger `Button` had `min-w-0`, so the flex chain allowed the `truncate` label to shrink to zero width. ## Fix Add an explicit width floor on the trigger button: `min-w-[calc(8ch+3.125rem)]` — 8ch of label plus the fixed chrome around it (`px-2` padding, `size-3` provider icon, two `gap-1` gaps, `size-3.5` chevron). - The explicit `min-width` replaces the button's automatic minimum, so it still shrinks and the label still truncates with an ellipsis, but never below ~8 visible characters. - The inner structure (`min-w-0` wrapper + `truncate` label) is unchanged, so truncation behavior and the label–chevron `gap-1` are identical to before; the chevron always stays inside the pill. - The badge row's existing `+N` overflow pill absorbs the remaining width pressure. No behavior change for the other `ModelSelector` callsites other than gaining the same floor, which only applies under width pressure. <details> <summary>Iteration notes</summary> An earlier revision removed `min-w-0` from the button and label wrapper and put `min-w-[8ch]` on the label. That backfired: without `min-w-0`, the wrapper's automatic minimum became the full nowrap label width, so nothing could shrink — truncation stopped and the chevron was pushed out and clipped. The final approach floors the button width explicitly instead, leaving the shrink/truncate mechanics untouched. </details> --- *This PR was generated by Coder Agents on behalf of @tracyjohnsonux.* |
||
|
|
7394d2cab0 |
feat(site): use /api/v2 chat API paths (#28498)
## Stack context This is the final PR in the 3-PR chat API promotion stack: server compatibility mounts (#28496), codersdk promotion (#28497), and frontend path updates (this PR). ## Summary Switch promoted frontend chat and MCP REST and WebSocket calls to `/api/v2`. Debug runs, virtual desktop streaming, advisor, and computer-use provider routes remain on `/api/experimental` because those surfaces were not promoted. Update the matching tests, stories, helpers, and end-to-end route expectations. Remote dogfood UAT passed with chat traffic verified on the v2 routes. > [!NOTE] > Xum acted on Mike's behalf in this pull request. <!-- xum-attribution: model=claude-fable-5 thinking=high --> |
||
|
|
046a532a18 |
chore(site/src): move deployment docs links inline as View docs text links (#28612)
Moves documentation CTAs that were previously rendered as header buttons into inline `View docs` external text links under the relevant header or section copy. ## Pages updated Deployment settings: - `/deployment/overview` - `/deployment/appearance` - `/deployment/external-auth` - `/deployment/oauth2-provider/apps` - `/deployment/network` - `/deployment/workspace-proxies` - `/deployment/idp-org-sync` - `/deployment/notifications` - `/deployment/userauth` - `/deployment/security` - `/deployment/observability` - `/deployment/premium` - `/deployment/ai-governance` Organization and user settings: - `/organizations/:organization/groups` - `/organizations/:organization/roles` - `/organizations/:organization/idp-sync` - `/organizations/new` - `/organizations/:organization/provisioners` - `/organizations/:organization/provisioner-keys` - workspace sharing settings Other admin/tool pages: - `/ai/settings/gateway-keys` - `/audit` - `/connectionlog` - template permissions settings ## Changes - `SettingsHeaderDocsLink` now renders the shared inline `Link` component with the default label `View docs`, external icon, and screen-reader text for new-tab behavior. - Right-side header actions now remain reserved for real actions such as `Add application`, `Create key`, or export buttons. - `/deployment/security`: removed the duplicate Browser-Only Connections badge and vertically aligned the remaining badge with the section heading. - `/deployment/observability`: kept docs links scoped to Audit Logging and Monitoring, and removed the top-level observability docs link. - Notification misconfiguration alerts now use the same `View docs` text-link treatment. - Storybook stories updated to assert the new link labels and hrefs where covered. ## Validation - `pnpm check` - `pnpm lint:types` - Targeted Storybook interaction tests for the updated pages. _This PR was generated by Coder Agents on behalf of @tracyjohnsonux._ |
||
|
|
12d7bf29c8 |
fix(site): standardize AI provider badge tooltips (#28624)
The AI provider status badges used different tooltip implementations, and long warning messages could render as a single wide line. Use the shared tooltip component for both badges, constrain tooltip width for readable wrapping, and cover the portaled tooltip content and accessible triggers in Storybook. Related to #28549. <details> <summary>Screenshots</summary> **Before** <img width="1274" height="113" alt="before_proxy_warning" src="https://github.com/user-attachments/assets/3fb6f317-50d5-434c-8713-cbfab60f6550" /> <img width="1085" height="116" alt="before_agent_warning" src="https://github.com/user-attachments/assets/4d0f4a6a-81b6-49bc-8bc0-f4ea63eb9af0" /> **After** <img width="998" height="190" alt="after_proxy_warning" src="https://github.com/user-attachments/assets/57076e70-eaaf-43b7-b15a-5861ffe5054c" /> <img width="974" height="121" alt="after_agent_warning" src="https://github.com/user-attachments/assets/d27d1940-8ade-4ee2-b8a4-4f368450e6d6" /> </details> > [!NOTE] > Generated by Coder Agents for @ssncferreira. --------- Co-authored-by: Jeremy Ruppel <jeremy.ruppel@gmail.com> |
||
|
|
973d5a458b |
feat: promote codersdk chat API methods to Client (#28497)
## Stack context This is the second PR in the 3-PR chat API promotion stack: server compatibility mounts (#28496), codersdk promotion (this PR), and frontend path updates (#28498). ## Summary Move the promoted chat and MCP SDK methods from `ExperimentalClient` to `Client` and update them to use `/api/v2`. Methods for routes that remain experimental stay on `ExperimentalClient`. Update in-repository callers and generated types. The multi-replica chat stream relay dials `/api/v2` directly: mixed-version replica sets are not a supported upgrade path, so no experimental-path fallback is kept (per review). Remote dogfood UAT passed for the composed stack. > [!NOTE] > Xum acted on Mike's behalf in this pull request. <!-- xum-attribution: model=claude-fable-5 thinking=high --> |
||
|
|
7281027dd8 |
docs: remove internal Slack link from CONTRIBUTING.md [DOCS-684] (#28320)
The `codercom.slack.com` link in CONTRIBUTING.md returns a 403 for the docs link checker (Slack archive URLs require authentication, so it can never pass) and advertises an internal Slack channel in a public contributor doc. The `pr-deploy.yaml` workflow already treats the channel URL as a secret, so the doc now refers to the channel by name only. While in the file, this also fixes a few formatting issues: emdashes used as punctuation in the dev server list and the backport conflict note, sample `nix-shell` output where hard-wrapping had garbled the `cache.nixos.org` URLs, and a misindented closing code fence in the "Verify Your Session" step. Refs [DOCS-684](https://linear.app/codercom/issue/DOCS-684/remove-internal-slack-link-from-contributingmd-and-fix-minor) --- 🤖 Built with AI assistance. |
||
|
|
50f36315fd |
fix(coderd): accept RFC 9728 resource metadata with array-valued resource (#28592)
GitLab's official MCP server returns `"resource"` as a JSON array in its `/.well-known/oauth-protected-resource` document, while RFC 9728 §2 defines it as a string. Coder's OAuth2 auto-discovery failed to decode the response (`json: cannot unmarshal array into Go struct field protectedResourceMetadata.resource of type string`), blocking zero-config `oauth2` DCR against GitLab. This makes `protectedResourceMetadata.Resource` tolerate both a single string and an array of strings. Part of [CODAGT-570](https://linear.app/codercom/issue/CODAGT-570/coder-agents-mcp-client-cannot-connect-to-gitlabs-official-mcp-server). The other blocker in that issue (GitLab replying 204 to `notifications/initialized`) was already fixed by the migration from `mark3labs/mcp-go` to `modelcontextprotocol/go-sdk`, whose streamable-HTTP client accepts 204. <details> <summary>Investigation notes</summary> CODAGT-570 reported two independent incompatibilities with GitLab CE's `/api/v4/mcp` server: 1. **204 vs 202 on `notifications/initialized` (blocked all auth modes):** the bundled `mark3labs/mcp-go` client only accepted 200/202 for notification POSTs. That library has since been removed entirely; `coderd/x/chatd/mcpclient` now uses `github.com/modelcontextprotocol/go-sdk v1.7.0`, which accepts both 204 and 202 (and only warns on other codes in non-strict mode, which is the only mode reachable from Coder). No further change needed. 2. **RFC 9728 `resource` array (blocked zero-config oauth2 DCR):** Coder's own parser in `coderd/mcp.go` declared `Resource string`. This lives outside the MCP library, so the SDK migration did not fix it. Fixed here with a custom `UnmarshalJSON` accepting string or array of strings. The field is only decoded, never consumed downstream, so behavior is otherwise unchanged. </details> --- Generated by Coder Agents on behalf of @f0ssel. |
||
|
|
8afd0020ea |
docs: fix chat status names and auto-archive behavior claims (#28570)
Two Coder Agents pages describe chat statuses that no longer exist and attribute auto-archive to the wrong subsystem. The valid status set is `waiting`, `running`, `error`, `requires_action`, `interrupting`. - `architecture.md`: a submitted prompt marks the chat `running`, not `pending`; there is no queued state. - `chat-auto-archive.md`: the excluded status set is `running`, `interrupting`, `requires_action`. - `chat-auto-archive.md`: the job runs in the chatd chat worker on an hourly tick, not in `dbpurge`. - `chat-auto-archive.md`: documents the dashboard control at **AI Settings** > **Coder Agents** > **Lifecycle**, and the 25-title cap on the digest notification. API paths on the auto-archive page are intentionally untouched; a separate PR fixes the `/api/v2` to `/api/experimental` drift. Linear: DOCS-722 https://linear.app/codercom/issue/DOCS-722 <details><summary>Analysis evidence</summary> - `codersdk/chats.go`: `ChatStatus` constants are `waiting`, `running`, `error`, `requires_action`, `interrupting`. No `pending`, `paused`, or `completed`. - `coderd/x/chatd/chatstate/transitions.go`: `sendMessageQueueAndSetStatus` and the send paths apply `database.ChatStatusRunning`, so a submitted prompt is `running` immediately. `docs/ai-coder/agents/tasks-to-chats-migration.md` already states that chats have no separate queued state. - `coderd/database/queries/chats.sql`, `GetAutoArchiveInactiveChatCandidates`: `status NOT IN ('running', 'interrupting', 'requires_action')`, plus `archived = false`, `pin_order = 0`, `parent_chat_id IS NULL`. - `coderd/x/chatd/auto_archive.go`: `archiveLoop` runs from `coderd/x/chatd/worker.go` on `opts.ArchiveInterval`; `coderd/x/chatd/options.go` sets `defaultArchiveInterval = time.Hour`. Nothing in `dbpurge` performs chat auto-archive. - `coderd/x/chatd/auto_archive.go`: `chatAutoArchiveDigestMaxChats = 25`; `buildAutoArchiveDigestData` truncates to 25 titles and reports the remainder as `additional_archived_count`. - `coderd/x/chatd/options.go`: `defaultArchiveBatchSize = int32(1000)`, so the existing "up to 1,000 root chats" claim stands. - `site/src/pages/AISettingsPage/LifecyclePage/LifecyclePageView.tsx` mounts `AutoArchiveSettings`; `site/src/modules/management/AISettingsSidebarView.tsx` places the Lifecycle sub-nav item under Coder Agents. Validation: `pnpm run format-docs` (no changes) and `pnpm run lint-docs` (0 errors). </details> Generated by Coder Agents on behalf of @nickvigilante. |
||
|
|
6093705476 |
fix: use old_text/new_text vocabulary across edit_files (#28626)
The schema has advertised old_text/new_text since #25658, but agent error strings and hints still said "search string", directing the model toward field names it is not sending. Fixes CODAGT-523 |
||
|
|
c51ffc7c88 |
fix(coderd/x/chatd/chatprompt): keep NUL out of jsonb chat content (#28625)
PostgreSQL jsonb rejects \u0000, so persisting Gemini responses failed with "pq: unsupported Unicode escape sequence": binary thought signatures reach ProviderMetadata as strings containing NUL, and the sentinel codec only covered six fields. Real recorded Gemini signatures contain NUL in 22 of 34 cases. Classify every string-bearing part field in one table, partNulFields. Free-form and opaque fields encode NUL reversibly as PUA sentinel pairs; structured fields (identifiers, paths, URLs) reject it with an error naming the part, field, and offset, since their consumers cannot handle NUL anyway. Raw JSON is also normalized for lone surrogate escapes, which jsonb equally rejects. A reflection-driven test injects NUL into every field and fails when a future field is missing from the table. The Gemini regression asserts the sentinel-encoded row in the database and a byte-exact signature round-trip. Known limits: json.Marshal corrupts other invalid UTF-8 upstream of this codec (CODAGT-986), and chat debug steps have always dropped silently on NUL prompt text (CODAGT-987, pre-existing). Fixes coder/coder#25555 Fixes CODAGT-463 |
||
|
|
276d0a8586 |
fix: expose effective member AI budgets (#28410)
## Description The group members page could not distinguish an unlimited Everyone fallback from a configured budget on the Everyone group when viewing another group, so it incorrectly displayed `Unlimited` and `Everyone (not allocated)` despite the configured Everyone budget. The member spend response now includes `effective_budget` as the canonical budget that currently applies to the user. The existing `group_budget` field remains for backward compatibility and is deprecated in favor of `effective_budget`. ## Changes - Add `effective_budget` to group member AI spend responses. - Deprecate `group_budget` in favor of `effective_budget`. - Display budgeted Everyone as the governing group instead of `Unlimited` and `Everyone (not allocated)`. - Add database, API, conversion, and Storybook coverage. Closes https://linear.app/codercom/issue/AIGOV-588 > [!NOTE] > Generated by Coder Agents on behalf of @ssncferreira. |
||
|
|
351bb14032 |
feat: mount chat API routes under /api/v2 (#28496)
## Stack context This is the base of a 3-PR stack promoting the chat API from `/api/experimental` to `/api/v2`: server compatibility mounts (this PR), codersdk promotion (#28497), and frontend path updates (#28498). ## Summary Double-mount the stable chat and MCP handlers under `/api/v2` while retaining the existing experimental routes for the one-release compatibility window decided in CODAGT-921. CODAGT-922 tracks removing the compatibility mounts. The shared route builders preserve existing authentication and middleware behavior. Experiment-gated, debug, tombstone, and legacy default-organization model routes remain experimental-only. Signed file URLs, external OAuth callback URLs, and mixed-version replica relays also remain on the experimental prefix during the transition. Update Swagger and the generated API reference for the promoted routes, including the workspace lookup and a runnable raw-body chat file upload example. Retain internal endpoints outside the published reference, share chat-file rate limits across both prefixes, enable CORS for the v2 MCP routes, and cover dual mounts plus exclusions with compatibility tests. Remote dogfood UAT passed for the promoted chat, model, MCP, and file flows. > [!NOTE] > Xum acted on Mike's behalf in this pull request. <!-- xum-attribution: model=claude-fable-5 thinking=high --> |
||
|
|
0c0e605521 |
fix(site): show Manage agents link to organization model admins (#28637)
The Agents settings panel only showed the **Manage agents** link to users with `editDeploymentConfig`, even though the Coder Agents page it links to (`/ai/settings/coder-agents`) already admits organization model admins, as does the AI settings sidebar. Organization model admins had no way to discover the page from the Agents settings panel. Gate the link on a new `canAccessCoderAgentsSettings` helper (deployment edit access or any chat model config access), threaded as a separate `canManageAgentSettings` prop through `AgentsPageLayout`, `ChatsSidebar`, and `SettingsPanel`. `isAdmin` is left untouched because it also controls API-key item visibility and the provider configs query. Refs CODAGT-74. <details> <summary>Implementation plan (Coder Agents)</summary> ### Bug: hidden Manage agents link Current behavior: - `AgentsPageLayout.tsx` defines agents administration as `permissions.editDeploymentConfig` and threads it as `isAdmin` through `ChatsSidebar`. - `SettingsPanel.tsx` shows **Manage agents** only when `isAdmin`. - `CoderAgentsPage.tsx` already allows organization model administrators, and #28473 explicitly intended the page and sidebar entry to support deployment access or organization model access. Design: - Add `canAccessCoderAgentsSettings(permissions)` in `site/src/modules/permissions/index.ts`, defined as deployment edit access or `canAccessAnyChatModelConfig(permissions)`. - Add a separate `canManageAgentSettings` prop through `AgentsPageLayout`, `ChatsSidebar`, and `SettingsPanel`. - Do not widen `isAdmin`, because it also controls API-key visibility and provider queries. Tests: - `ChatsSidebar` story where `isAdmin` is false and organization model access is true: the link appears and the API-key item stays hidden. - `AgentsPageLayout` story with `MockNoPermissions` plus `editAnyChatModelConfig`: the link appears through the full permission-driven flow. - The plain-member denial stories stay green. </details> --- Generated by Coder Agents on behalf of @ethanndickson. |
||
|
|
45f2eb48cc |
refactor(coderd): restore agents-access cleanup migration (#28587)
## Summary - restore the `agents-access` data cleanup migration as `000587` (renumbered from `000585`; main advanced past that number) - revert the temporary RBAC tolerance for retired role names now that the migration strips stale grants - make the migration test discover its predecessor dynamically, since this branch does not carry main's `000585`/`000586` ## Stack context Stacked on #28186, which retires the `agents-access` role but intentionally ships without a migration so it can be backported across diverged migration numbers. This layer completes the retirement on main: it cleans up stale role grants in `users.rbac_roles`, `organization_members.roles`, and `organizations.default_org_member_roles`, then reverts the interim leniency (role expansion dropping legacy names, assignment validation ignoring them) that existed only to keep the migration-less base safe. `rbac.ReservedRoleName` still reserves the retired name so custom roles cannot shadow it on rollback. Do not backport this PR alongside the base PR. ## Rollback note The down migration restores effective access broadly because it cannot reconstruct which users previously held the removed role. ## Validation - `go build ./coderd/... ./enterprise/...` - full `coderd/database/migrations`, `coderd/rbac/...`, and `coderd/database/dbauthz` suites - targeted members tests in `coderd` - repository pre-commit checks (`lint/go`, `lint/ts`, etc.) > Mux created this pull request on Mike's behalf. |
||
|
|
31146642ba |
fix(codersdk/toolsdk): select default chat org based on recent usage (#28543)
When `coder_create_chat` omitted `organization_id`, it selected the first organization membership, which could choose an organization without an available chat model. Default to the organization used by the user's most recently updated chat. For users with multiple organizations and no prior chats, return a descriptive error requiring `organization_id`; single-organization behavior is unchanged. Closes https://linear.app/codercom/issue/CODAGT-968/fix-mcp-chat-creation-organization-selection <details> <summary>Coder Agents disclosure</summary> This pull request was generated by Coder Agents. </details> |
||
|
|
e3e7200cff |
fix(site): distinguish cross-org budgets (#28596)
## Description Group member budget actions were disabled whenever another group governed the member's budget, including groups in the same organization. Distinguish same-organization and cross-organization effective groups so administrators can manage overrides from any same-org group page. Keep the action disabled when the effective group belongs to another organization. ## Changes - Add an `otherOrganization` effective-group classification. - Enable budget management for same-org effective groups. - Keep budget management disabled for cross-org effective groups. - Render same-org lookup failures without the cross-org message. > [!NOTE] > Generated by Coder Agents on behalf of @ssncferreira. |
||
|
|
845790e652 |
feat: enable Coder Agents for organization members (#28186)
## Summary - make Coder Agents available to organization members without a separate built-in role - remove the obsolete role from backend and frontend role surfaces - update authorization, UI, documentation, and regression coverage ## Database note This PR intentionally ships no migration so it backports cleanly across diverged migration numbers. Stale `agents-access` grants may remain in user role arrays and org default member roles; the retired name is treated as a grant of nothing (role expansion drops it, assignment validation ignores it, and the name stays reserved). A follow-up PR will add the data cleanup migration. ## Validation - `make gen` - `make lint` - repository pre-commit checks - targeted RBAC, migration, chat API, enterprise, site, Storybook, and race tests - `git diff --check` ## Rollback note Rolling back the deployment restores the previous behavior directly: no data changed, and older binaries still resolve any lingering `agents-access` grants. > Mux created this pull request on Mike's behalf. |
||
|
|
9dda7e7a1b |
test(cli): wait for expected GitHub auth state (#28622)
The GitHub OAuth server test treated any successful `AuthMethods` response after restart as ready. In the failing macOS run in CI (described in the linked Linear issue), the response decoded successfully but reported GitHub authentication as disabled, even though startup logged the default GitHub provider configuration. Wait until the complete GitHub authentication method reaches the expected state before asserting it. This preserves retries for transport readiness while also covering successful but incorrect startup responses. Closes https://linear.app/codercom/issue/ENG-3285/flake-testserveroauth2githubdefaultprovidernewdeployment <sub>Generated by Coder Agents.</sub> |
||
|
|
56c614422e |
fix: clarify AI provider collision warnings (#28549)
AI provider collision warnings were only partially asserted, and the update coverage did not exercise a provider changing from a distinct hostname to a colliding hostname. Assert the complete warning consistently across create, get, list, and update responses, cover collisions introduced by base URL and enabled-state updates, and only return the proxy-specific warning when AI Gateway Proxy is enabled. Clarify that lifecycle status values are reported by the provider metrics. Follow-up to #28208. > [!NOTE] > Generated by Coder Agents for @ssncferreira. |
||
|
|
bd4af31f78 |
fix(coderd/provisionerdserver): clarify duplicate preset errors (#28530)
> 🤖 This PR was written by Coder Agents on behalf of Jake Howell. Template imports with duplicate preset names currently expose the underlying PostgreSQL unique constraint error. Translate that constraint violation into a clear validation message that identifies the duplicate preset name. <img width="1110" height="462" alt="image" src="https://github.com/user-attachments/assets/dc7b86a3-8f85-4694-a0be-aea3d0b53360" /> Closes https://github.com/coder/coder/issues/19270 |
||
|
|
5008603d08 |
fix(site): dispose Monaco diff models on unmount (#28503)
> 🤖 This PR was written by Coder Agents on behalf of Jake Howell. Fixes [DEVEX-736](https://linear.app/codercom/issue/DEVEX-736/template-editor-crashes-becomes-unusable-when-opening-versions). ## Problem Opening template versions in the template editor leaks Monaco text models. Each version renders one `DiffEditor` per changed file, and `SyntaxHighlighter` sets `keepCurrentOriginalModel` / `keepCurrentModifiedModel`, which tells `@monaco-editor/react` **not** to dispose the underlying text models when the editor unmounts. Because no stable model paths are supplied, every visit creates fresh anonymous models that are never freed. Navigating between versions accumulates models without bound until the tab lags, spins, and eventually OOMs — matching the customer reports on 2.34.5. The flags themselves are still required: removing them makes the library dispose the models mid-teardown (before the `DiffEditorWidget` is torn down), which throws. ## Fix Keep the `keepCurrent*Model` flags, but move the diff editor into its own `DiffFile` component that owns the model cleanup: - `DiffFile` captures the `original`/`modified` models in `onMount` and disposes them in its own unmount effect. Because the effect lives with the diff editor, it runs whenever the diff editor unmounts — **including when a file switches diff → plain across versions while `SyntaxHighlighter` stays mounted** (the `key={filename}` case raised in review), not only on full unmount. - Disposal is deferred with `queueMicrotask` so it runs after React's commit and after `@monaco-editor/react` disposes the widget. Freeing the models in the same synchronous teardown throws `TextModel got disposed before DiffEditorWidget model got reset`. Non-diff files use the plain `Editor`, which already disposes its own model, so they are unaffected. No editor options or UI behavior change. ## Evidence Reproduced locally on a template with 32 versions (9 `.tf` files each), driving 192 in-app version navigations (single SPA session, no page reloads) and reading `monaco.editor.getModels().length` via temporary instrumentation. | | before (pass 1 → pass 6) | after (pass 1 → pass 6) | |---|---|---| | retained Monaco models | 532 → **3,142** (unbounded) | ~10–19 (flat) | | per-navigation render latency | 206ms → **611ms** | ~120–270ms (flat) | Before: models climb ~520 per full pass and never release (a sample taken with zero editors on screen still showed 522 live models). After: model count and render latency stay flat across the same workload. ## Testing Added `SyntaxHighlighter.stories.tsx` with a `play` regression test (`DisposesModelsOnDiffToggle`) that reproduces the exact leak: it toggles a single surviving `SyntaxHighlighter` between diff and plain three times and asserts `monaco.editor.getModels().length` drops when the diff is removed and returns to the baseline each cycle (no accumulation). - The test **fails on the unfixed behavior** (disposal removed): `expected 5 to be less than 4` — models grow instead of being freed. - With the fix it passes with **zero Monaco teardown errors**: ``` $ pnpm test:storybook src/components/SyntaxHighlighter/SyntaxHighlighter.stories.tsx Test Files 1 passed (1) Tests 3 passed (3) Errors 0 ``` - `pnpm check` (biome) — pass - `pnpm lint:types` (`tsc --noEmit`) — pass - Manual stress test: model count and render latency stay flat across 192 navigations (see Evidence). <details> <summary>Investigation & decision log</summary> **Root cause trace** - `site/src/pages/TemplateVersionPage/TemplateVersionPageView.tsx` → `TemplateFiles` renders every template file at once. - Each changed file → `SyntaxHighlighter` → a full Monaco `DiffEditor`. - `@monaco-editor/react@4.7.0` unmount cleanup does, in effect: ```js const model = editor.getModel(); keepCurrentOriginalModel || model.original.dispose(); keepCurrentModifiedModel || model.modified.dispose(); editor.dispose(); ``` With both `keepCurrent*` flags set, neither model is disposed, so they leak. - The flags exist to stop a storybook hang caused by that same disposal-order error, so they cannot simply be removed. **Options considered** 1. Remove the flags — fixes the leak but re-introduces the mid-teardown throw. Rejected. 2. Supply stable `originalModelPath`/`modifiedModelPath` so models are reused — the library reuses by URI, but its value-sync effect is skipped on the first render, so a reused model renders **stale content** for the new version. Rejected. 3. Keep the flags and dispose the captured models when the diff editor unmounts — targeted and correct for the diff → plain → diff case. **Chosen.** **Why the cleanup is scoped to a child component** The first iteration kept the effect in `SyntaxHighlighter` with an empty dependency array, so it only ran on full unmount. Review correctly flagged that a surviving instance switching diff → plain would then orphan the previous model pair. Scoping the effect to the `DiffFile` child ties it to the diff editor's own lifecycle, closing that gap. **Why `queueMicrotask`** Disposing synchronously in the effect cleanup races Monaco's own widget teardown and throws `TextModel got disposed before DiffEditorWidget model got reset`. Deferring one microtask lets the widget finish tearing down first. **Test-only editor options** The stories pass `renderGutterMenu: false` / `occurrencesHighlight: "off"` through `editorProps`. Those Monaco features register delayed disposables whose teardown throws under jsdom on unmount; disabling them keeps the test runner clean. They do not affect model count, and production keeps Monaco's default options unchanged. </details> |
||
|
|
c9fc5b7ea1 |
fix: canonicalize API key scope aliases at ingress (#28322)
`rbac.IsExternalScope` accepts `all` and `application_connect`, neither
of which is an `api_key_scope` enum member. The plural `Scopes` field
stored the name as given, so `{"scopes":["all"]}` passed validation and
then 500'd inside `apikey.Generate` with `invalid API key scope: "all"`.
Reachable from `coder tokens create --scope all`, from the spellings
`docs/admin/users/sessions-tokens.md` taught, and from the `codersdk`
constants exported for exactly this purpose.
- Canonicalize inside `apikey.Generate`, in the loop that already
validates each name. One statement covers every caller and all three
input paths: plural `Scopes`, deprecated singular `Scope`, and the
default. Replaces two open-coded switches and the handler's own
per-element copy, so one place decides the stored spelling and the
handler only decides what may be requested.
- Deduplicate in the same pass. An alias and its canonical spelling are
two names going in and one name in the column.
- `ExternalScopeNames()` lists every name `IsExternalScope` accepts,
instead of dropping catalog entries that fail to parse. A curated entry
that cannot be stored used to pass every test and 500 at runtime; it now
fails two.
- Split the 400. A misspelled name and an internal `api_key_scope`
member need different words, since no re-spelling makes the second
requestable. Both rejection sites share one helper that names the case
and links the docs.
- Add reject-path tests for `not_a_real_scope` and `debug_info:read`.
The second is a valid enum member the rbac catalog treats as internal,
so before this both handler guards could be deleted with the suite still
green. A further case pins that plural `Scopes` wins when a caller sets
both fields.
- `TestExternalScopesAreStorable` pins the class rather than the two
known instances: every public rbac scope name must be storable.
`coderd/rbac` cannot assert this itself, since `database` imports `rbac`
and not the reverse.
- Docs use the canonical spellings, link the `codersdk.APIKeyScope`
schema, and say which scopes a token cannot request.
Egress is unchanged: `convertAPIKey` still derives a legacy singular
name on the way out. `ExternalScopeNames()` returns the same 57 names as
before, and `codersdk/apikey_scopes_gen.go` regenerates identically.
`rbac.CanonicalScopeName` merged with #28167, so this applies to `main`
and reviews on its own. The OAuth2 provider ignores scopes entirely
today (`authorize.go:237`, `tokens.go:377`, `tokens.go:520`), so it is
unaffected until those TODOs resolve.
Deferred from review: PLAT-528, PLAT-529, PLAT-530, and PLAT-532.
PLAT-532 covers `{"scopes":[]}` and `{"allow_list":[]}` defaulting open,
which predates this PR.
---------
Co-authored-by: McKayla はな <mckayla@hey.com>
|
||
|
|
895c5c77a1 | feat: add a template parameter settings page (#27559) | ||
|
|
2236710bad |
fix(coderd/notifications): HTML-escape the email template values (#28397)
Follows #28340, now merged. `smtp.go` renders the notification title through `PlaintextFromMarkdown`, which strips Markdown **and decodes HTML entities**, then stores the result in `Labels["_subject"]`. `html.gotmpl` interpolated that raw into `<title>` and `<h1>`, so an entity-encoded payload in a user-controlled label arrived as live markup: ``` template_display_name = <a href="https://attacker.example/login">Re-authenticate now</a> -> <title>Template "<a href="https://attacker.example/login">Re-authenticate now</a>" deleted</title> ``` Markdown escaping cannot reach this. `&` is not backslash-escapable in either renderer, and this path never enters gomarkdown, so neither `html.SkipHTML` nor the `Safelink` added in #28340 sees the string. `{{ .UserName }}` was interpolated raw at the same template, straight from the unescaped payload the dispatcher receives. This PR adds `| html` to seven values across eleven positions in `html.gotmpl`: | Value | Positions | Why | |---|---|---| | `.Labels._subject` | 2 | the injection above, in `<title>` and `<h1>` | | `.UserName` | 1 | reaches the template straight from the unescaped payload | | `$action.URL` | 1 | rendered from user data at `enqueuer.go:201`; `EscapedForMarkdown` does not touch `Actions` | | `$action.Label` | 1 | static today, escaped so it stays safe if that changes | | `base_url` | 4 | `--access-url` is scheme-checked only, so a `"` closes the `href` | | `current_year` | 1 | cannot carry markup, escaped so the rule has no exceptions | | `.NotificationTemplateID` | 1 | same | `logo_url` and `app_name` were already escaped in #28340. `{{ .Labels._body }}` stays unescaped: it is intentionally gomarkdown output, and it is the only value in the file that is not escaped. The action and `base_url` values are defense in depth rather than open holes. A `"` in an action URL fails closed at enqueue, because the rendered actions JSON is unmarshalled before use and the quote breaks that parse; `<`, `>`, `&` and `'` survive but are inert inside a double-quoted attribute. `base_url` requires an operator to set a hostile `--access-url`. Every value is guarded by a test. Removing `| html` from any of the nine escaped values now fails a named test, verified by removing each pipe in turn: - `TestSMTPHTMLTemplateEscapesUntrustedValues` covers `_subject`, `UserName` and both action values. - `TestSMTPHTMLTemplateEscapesTrustedValues` covers `base_url`, `current_year` and `.NotificationTemplateID`, none of which can carry markup in production, so no golden file would catch their regression. - `TestSMTPHTMLTemplateEscapesAppearanceHelpers` covers `logo_url` and `app_name`. That last point is why the trusted values needed tests rather than goldens: escaping them costs zero golden churn, so nothing already in the tree fails when it is removed. Before this PR the same was true of `$action.Label`, whose escaping could be deleted with no golden diff and no failing test at all. The 36 golden files change by entity encoding only, mostly `"` to `"` and `'` to `'` in subjects. Verified by quoted-printable decoding every file before and after and confirming the two are identical once HTML entities are decoded: 36/36 with no semantic difference. Escaping `base_url`, `current_year` and `.NotificationTemplateID` added no further churn. **NOTE**: `$action.URL | html` turns the `&` in the one-time passcode reset link into `&`. That is the correct encoding of a literal `&` in an attribute value, and every conformant client decodes it before navigating, so the request the server receives is unchanged. It is the only golden change with behavior attached. Migrating this template to `html/template` was considered and declined; the reasoning and the conditions that would reverse it are on the CRF-3 review thread. Refs https://linear.app/codercom/issue/PLAT-273/markdown-link-injection-into-admin-notification-emails-sec-93 |
||
|
|
c4d356ff71 | feat(cli): add --username-infix selection to scaletest notifications… (#28515) | ||
|
|
f4730aed12 | feat: add --username-infix to scaletest create-users (#28514) | ||
|
|
ce59a86a81 |
docs: fix Coder Agents tool tables, search filters, attachments, template ranking (#28575)
Corrects four independent factual errors in the Coder Agents documentation, each verified against the current implementation. - **Missing tools in the tool tables.** `stop_workspace` was absent from the built-in tool table in `docs/ai-coder/agents/index.md` and the platform tool table in `docs/ai-coder/agents/architecture.md`, and from the root-chat-only tool list. `find_tools` was undocumented entirely; both docs now list it with its `mcp-tool-search` experiment gating. - **Missing `source:` search filter.** `docs/ai-coder/agents/chat-search-syntax.md` documented every chat filter except `source:`, while also stating that unrecognized keys return HTTP 400. Adds the filter row and an example. - **Attachments described as images only.** The attachments section claimed PNG, JPEG, GIF, and WebP up to 10 MB. The upload allowlist also accepts plain text, Markdown, CSV, JSON, and PDF; the cap is 10 MiB per upload with at most 50 attachments per conversation, and Anthropic models cap inline images at 5 MiB. Also notes that providers differ in accepted file types and that rejected parts are downgraded to text. - **Template ranking described as popularity sort.** `docs/ai-coder/agents/platform-controls/template-optimization.md` said templates are sorted by active developer count. `list_templates` ranks by query relevance tiers first, then by an affinity score that weights the user's own recent usage far above organization popularity. Wording now matches `docs/ai-coder/agents/tools/index.md`, which already documents the formula, and links to it. Linear: DOCS-716 https://linear.app/codercom/issue/DOCS-716 <details><summary>Analysis evidence</summary> | Claim | Source | |-------|--------| | `stop_workspace` is a root-chat platform tool registered alongside `create_workspace` and `start_workspace` | `coderd/x/chatd/chattool/stopworkspace.go`; registration in `coderd/x/chatd/chatd.go` (`chattool.StopWorkspace`), root-only gate in `builtinPlanToolAllowed`, disabled for explore sub-agents in `allowedExploreToolNames` | | `find_tools` requires the `mcp-tool-search` experiment and deferrable MCP candidates | `coderd/x/chatd/chattool/findtools.go`; `decideMCPToolSearch` in `coderd/x/chatd/mcp_tool_search.go` returns false without `codersdk.ExperimentMCPToolSearch` or with no candidates; wired in `coderd/x/chatd/generation_preparer.go` | | `source:` accepts `created_by_me` and `shared_with_me`; passing both returns owned plus shared; default is owned only | `coderd/searchquery/search.go` (`Chats`): filter defaults to `OwnedOnly: true`; the `source` parser rejects any other value, and both values together set `OwnedOnly` and `SharedOnly`. The literal value `all` appears in the doc comment but is not accepted by the parser, so it is not documented. | | Attachment allowlist, size caps, and per-conversation limit | `codersdk/chats.go`: `AllChatAttachmentMediaTypes` covers `application/json`, `application/pdf`, `image/gif`, `image/jpeg`, `image/png`, `image/webp`, `text/csv`, `text/markdown`, `text/plain`; `MaxChatFileSizeBytes = 10 * 1024 * 1024`; `MaxChatFileIDs = 50`; `AnthropicInlineImageCapBytes = 5 * 1024 * 1024` | | Anthropic and Bedrock share the inline image cap; rejected file parts are downgraded to text | `coderd/x/chatd/chatprovider/chatprovider.go`: `InlineImageCapBytes` and `Model.AcceptsFilePartMediaType` | | Template ranking is relevance tiers then a composite affinity score | `coderd/x/chatd/chattool/listtemplates.go`: `rankTemplates` sorts by query score first; `computeAffinityScore` adds recency-decayed personal usage at `listTemplatesPersonalWeight = 10.0` to `ln(1 + active_developers)` at `listTemplatesOrgWeight = 1.0` | Note: `MaxChatFileIDs` is enforced per chat, not per message (`coderd/exp_chats.go`: "A chat can reference at most %d attachments"), so the docs say per conversation. Validation: `pnpm run format-docs` and `pnpm run lint-docs` both pass (0 errors). The formatter widened the touched tables, which accounts for the reformatted rows in the diff. </details> Generated by Coder Agents on behalf of @nickvigilante. |
||
|
|
06c0d3918a |
docs: correct plan mode MCP tool availability (#28569)
`docs/ai-coder/agents/index.md` claimed that MCP tools are blocked in plan mode. Root plan-mode chats actually receive external MCP tools for each server configuration an administrator approved for plan mode; only workspace MCP tools and plan-mode sub-agents get none. Corrects the plan-mode tool-availability wording in both places it appears in `index.md` so it matches `docs/ai-coder/agents/architecture.md` and the implementation. The tool table and attachment wording are out of scope here. Linear: DOCS-724 https://linear.app/codercom/issue/DOCS-724 <details><summary>Analysis evidence</summary> `coderd/x/chatd/chatd.go`, `filterExternalMCPConfigsForTurn`: in plan mode, a chat with a parent gets no external MCP configs ("Plan-mode subagents do not receive external MCP tools"); a root chat keeps every config whose `AllowInPlanMode` flag is set and returns those IDs as approved. `coderd/x/chatd/generation_preparer.go`: the returned `approvedPlanMCPConfigIDs` flows into `filterToolsForTurn` and `activeToolNamesForTurn`, so approved external MCP tools remain exposed for the turn. `coderd/x/chatd/chatd.go`, `toolAllowedForTurn`: in plan mode a tool passes only if it is on the built-in plan allowlist or is an MCP tool whose `MCPServerConfigID()` is in the approved set. Workspace MCP tools are not in the external config set, so they stay unavailable. `docs/ai-coder/agents/architecture.md` already documents this behavior; `index.md` now uses the same framing. </details> Generated by Coder Agents on behalf of @nickvigilante. |
||
|
|
60b03138ae |
fix(coderd/x/chatd): truncate overlong generated chat titles instead of rejecting them (#28589)
Chat title generation asks the model for a title in 2-8 words and then hard-rejects any response longer than 8 words (`validateGeneratedTitle`). Quickgen pins temperature for repeatable output, so a model that overshoots the budget for a given conversation overshoots on every retry: the rename dialog's Generate button returns a deterministic 500 (`generate manual title: generated title exceeded 8 words`) no matter how often it is clicked, and the automatic first-message path silently leaves the chat on its fallback title. On dev.coder.com this rejection fires 1-3 times a day; one chat took 13 consecutive manual failures on 2026-08-25 (14:22-14:27 UTC). Truncate the normalized title to the 8-word budget in `normalizeTitleOutput` instead of rejecting it, and keep the empty-title validation. Both the automatic and manual title paths share this normalization, so both are fixed. > Xum (AI agent) authored this change and PR on Mike's behalf. |
||
|
|
74e5a680bd |
fix(site/src/pages/AgentsPage): use outlined empty state and kebab menu for personal skills (#28560)
Polishes the `/agents/settings/personal-skills` page to match the table patterns used elsewhere (e.g. the AI Gateway keys page): - The table now renders in every state, so the content area always keeps its outline: `TableLoader` while loading, a `TableEmpty` with a Retry CTA on error (with the `ErrorAlert` above), and a `TableEmpty` with an Add skill CTA when there are no skills. - The per-row Download / Edit / Delete buttons are replaced with a kebab (`EllipsisVertical`) dropdown menu, following the pattern in `CustomRolesPageView`. The trigger shows a spinner and disables while a download is in flight, and the Actions column header is now screen-reader only. Storybook interaction coverage: `RowMenuActions` opens the kebab and exercises Download, Edit, and Delete; `DownloadingSkill` asserts the disabled trigger. <details> <summary>Review notes</summary> Self-audited the diff against the FE1-FE10 rules in `.claude/docs/FRONTEND_PATTERNS.md`: all rules pass. All 22 stories pass locally, along with Biome and `tsc`. </details> --- Generated by Coder Agents on behalf of @tracyjohnsonux. |
||
|
|
b146e6f262 |
docs: align Coder Agents docs with GA framing and licensing terms (#28571)
Removes early-access-program framing from the Coder Agents docs, drops a stale `early access` manifest tag, and standardizes the licensing wording on "Premium license with the Agent Hours entitlement". - `docs/ai-coder/agents/getting-started.md`: replaces the "collaborative evaluation between your team and Coder" and customer-specific Slack channel feedback path with GitHub issues plus normal Coder support channels, and rewrites the remaining evaluation-period wording to GA tone. Rollout guidance (pilot group, cost planning, concurrency) is kept. - `docs/manifest.json`: removes `"state": ["early access"]` from `ai-coder/agents/platform-controls/chat-debug-logging.md`, which is not experiment-gated. - Licensing terminology aligned across `getting-started.md`, `licensing-usage.md`, and `platform-controls/index.md`. No changes to `docs/reference/api/**` and no API path changes. <details><summary>Analysis evidence</summary> **Manifest state tag decisions (all `ai-coder/agents` pages)** | Page | Tag before | Tag after | Why | |------|-----------|-----------|-----| | `platform-controls/chat-debug-logging.md` | `["early access"]` | none | Routes at `coderd/coderd.go` (`GET`/`PUT /debug-logging`, `GET`/`PUT /user-debug-logging`) sit outside any `RequireExperimentWithDevBypass` group, there is no matching experiment in the `codersdk.Experiment` list, and the page body already states the feature is not experiment-gated. | | `platform-controls/advisor.md` | `["early access"]` | unchanged | Gated by `codersdk.ExperimentChatAdvisor` (`chat-advisor`); routes wrapped in `RequireExperimentWithDevBypass`. | | `platform-controls/virtual-desktop.md` | `["early access"]` | unchanged | Gated by `codersdk.ExperimentChatVirtualDesktop` (`chat-virtual-desktop`); routes wrapped in `RequireExperimentWithDevBypass`. | | `platform-controls/spend-management.md` | `["premium"]` | unchanged | `premium` is a licensing tag, not a maturity tag, and the page states budget endpoints require a license that includes AI Gateway. Flagging for awareness rather than changing it. | | All other `ai-coder/agents` pages | none | none | No maturity tags to review. | **Licensing mechanics** `enterprise/coderd/license/license.go` decodes `agent_runtime_hours_allocation`, `agent_runtime_hours_limit_soft`, and `agent_runtime_hours_limit_hard` into `codersdk.FeatureAgentRuntimeHours` on Premium licenses. `enterprise/coderd/x/chatd/agentadmission.go` lifts the concurrency cap only when that feature is enabled and usage is below the hard limit. So the entitlement, not the license tier alone, is what removes the cap. Wording is now "Premium license with the Agent Hours entitlement". `licensing-usage.md` keeps "Agent Time" as the name of the measured usage metric (matching `runtime_ms` reporting) and now says explicitly that Agent Time draws down the Agent Hours entitlement. Renaming the metric itself was left out of scope; flagging in case the product team wants one name for both. **Validation** `pnpm install --frozen-lockfile`, `pnpm run format-docs` (no table changes), `pnpm run lint-docs` (499 files, 0 errors). </details> Linear: DOCS-718 https://linear.app/codercom/issue/DOCS-718 Generated by Coder Agents on behalf of @nickvigilante. |
||
|
|
29995c9ef8 | feat: add a per-template workspace rename setting (#27558) | ||
|
|
827c11f43a |
docs: update Coder Agents admin navigation to AI Settings (#28572)
## Summary Fixes stale admin navigation on the three Coder Agents platform-controls pages not covered by #28482: the retention pages pointed at tabs that do not exist ("Behavior", the old Agents settings "Lifecycle" tab), and template optimization pointed at the removed **Agents** > **Settings** > **Manage Agents** navigation. Breadcrumbs follow the **Admin settings** > **AI** > **Coder Agents** convention established by #28482. ## Changes - `docs/ai-coder/agents/platform-controls/chat-retention.md`: replace the nonexistent **Behavior** tab with **Admin settings** > **AI** > **Coder Agents** > **Lifecycle**. - `docs/ai-coder/agents/platform-controls/chat-debug-retention.md`: same page, same convention, aligned with chat-retention.md. - `docs/ai-coder/agents/platform-controls/template-optimization.md`: replace **Agents** > **Settings** > **Manage Agents** > **Templates** with **Admin settings** > **AI** > **Coder Agents** > **Templates**. ## Scope note The first revision also fixed `models.md`, `advisor.md`, `chat-debug-logging.md`, and `platform-controls/index.md`. Those pages are covered by #28482 with the same corrections, so this PR reverted them to avoid conflicting edits and adopted that PR's breadcrumb convention instead. Linear: DOCS-715 https://linear.app/codercom/issue/DOCS-715 <details><summary>Analysis evidence</summary> - `site/src/router.tsx` (~735-780): `/ai/settings/{lifecycle,templates,coder-agents}` are the live routes; `/agents/settings/{admin pages}` (~833-890) are `Navigate`-only redirects. - No "Behavior" tab exists in any router revision; `RetentionPeriodSettings` and `DebugRetentionSettings` both render in `LifecyclePageView.tsx`. - Breadcrumb convention **Admin settings** > **AI** verified against `AdminSettings.tsx` / `AISettingsSidebarView.tsx` in #28482. </details> Generated by Coder Agents on behalf of @nickvigilante. |
||
|
|
31a976b288 |
chore(docs): update release docs for v2.35.6 (#28577)
Automated docs update for v2.35.6 release. Created by `releasetui`. |
||
|
|
409049d1d8 |
docs: document org-scoped admin configuration for agents (#28482)
## Summary Documents the organization scoping of Coder Agents admin configuration, and corrects the navigation paths, access wording, and default-model instructions that no longer matched the product. Documentation only, no product code changes. Follows #28473, which moved the organization model overrides into an **Organization settings** section on **Admin settings > AI > Coder Agents**. #28473 is merged, so this targets `main`. Closes CODAGT-715 ## Changes - **New page** `docs/ai-coder/agents/platform-controls/organizations.md`, registered in `docs/manifest.json`. Covers the deployment/organization scope split, what an upgrade changes, how an organization without models or MCP servers behaves, who configures each scope, and how model and MCP server sharing works. - **`models.md`**: models are organization-scoped while providers and their credentials stay deployment-wide. Each organization has one default model, and the first model added becomes it. Documents the **Default** badge and the **Set as Coder Agents default model** form option in place of the star action that no longer exists, and points model overrides at **Organization settings**. - **`getting-started.md`**: replaces the Owner-only claim with the actual access model, adds an organization selection step, and uses the **Add model** label. - **`platform-controls/index.md`**: separates deployment-wide controls from organization-scoped controls, documents personal model overrides alongside the model configuration section rather than as an enforcement caveat, and adds `--agents-allowed` plus the `agents-allowed:` search filter. - **`platform-controls/advisor.md`** and **`platform-controls/virtual-desktop.md`**: the Advisor and Virtual desktop settings are cards in the **Deployment settings** section of the **Coder Agents** page, not sidebar entries. **Providers** is a sidebar item, not a tab. - **`platform-controls/mcp-servers.md`**: slug uniqueness is per organization, the **Add server** label is correct, and the page is no longer described as admin-only. - **`architecture.md`**, **`index.md`**, **`tasks-to-chats-migration.md`**: organization scope corrections and organization-scoped model routes. - Breadcrumbs standardized to **Admin settings > AI > ...**, verified against `AdminSettings.tsx` and `AISettingsSidebarView.tsx`. <details> <summary>Review follow-ups</summary> The first revision leaked implementation detail into user-facing pages and contained several claims that did not match the UI. The follow-up commit removed and corrected the following. Removed as implementation detail: - The `chat_model_config` RBAC resource, its actions, and the token scopes. - The `available`, `unavailable_reason`, and `unsupported_providers` response fields, along with the `missing_api_key`, `fetch_failed`, and `user_api_key_required` values. - Role and permission tables, ACL storage mechanics, audit internals, and JSON error bodies on the new organizations page. - Migration mechanics with no effect on what a user does. Corrected against the product: - The star action does not exist. The list shows a **Default** badge and the add/edit form offers **Set as Coder Agents default model** (`ModelRow.tsx`, `ModelFormFields.tsx`). - The first model added to an organization becomes its default automatically (`coderd/exp_chats.go`). An organization without a stored default does not fail every chat, so that claim is gone. - Advisor and Virtual desktop are cards under **Deployment settings** (`CoderAgentsPageView.tsx`). - The MCP servers page does not require deployment configuration edit permission (`AISettingsSidebarView.tsx`). - Button labels: **Add model** and **Add server**. - The organization query parameter is `org`, confirmed by `modelOrganizationSearchParam`. Left alone deliberately: pre-existing defects outside the introduced text, including the `/api/v2/chats` route prefixes, create-chat examples missing `organization_id`, and the pre-existing MCP permissions table. </details> ## Testing - `pnpm run format-docs` and `pnpm run lint-docs` pass. - `make lint/emdash` passes. No emdash or endash added. - `grep -rn "Defaults & overrides" docs/` returns nothing. - Every navigation path, button label, and access claim checked against the corresponding file under `site/src/pages/AISettingsPage/` and `site/src/modules/management/`. --- This pull request was generated by Coder Agents on behalf of @ethanndickson. |
||
|
|
816f6fbeef |
fix(site): clarify chat model permission labels (#28579)
Renames chat model sharing controls to permissions terminology so the UI reflects that administrators are managing model access. Uses “Use” as the user-facing role while preserving the backend `read` ACL value, and updates the Storybook interactions for the new labels. |
||
|
|
c07bde80c4 |
fix: use medium badges and shorter shared key label on agent API keys page (#28557)
On `/agents/settings/api-keys`, bumps the provider key status badge and the enabled model badges from `sm`/`xs` to the medium (`md`) size, and shortens the status label "Using shared key" to "Shared key". Updates the matching Storybook assertion and the status indicator name in `docs/ai-coder/agents/models.md`. Verified with `tsc` and the page's Storybook tests (15 passing). --- *Created by Coder Agents on behalf of @tracyjohnsonux.* |
||
|
|
94f312dfbd |
refactor(site): use robot icon for subagent toggle in chat kebab menu (#28561)
Replaces the rotated `GitForkIcon` on the "Show/Hide subagents" item in the chat kebab menu with lucide's `BotIcon`, matching the robot icon already used for subagents in the chat stream (`SubagentTool`, `ToolIcon`). --- *This PR was generated by Coder Agents on behalf of @tracyjohnsonux.* |
||
|
|
9e8075db0f |
fix: prevent markdown injection in notifications (#28340)
First of two PRs. Notification title and body templates are Markdown authored by Coder, but the label values interpolated into them are user-controlled and were substituted through `text/template`, which does no escaping. Those values arrive from user profile fields and from OIDC/GitHub name claims. This PR: - Neutralizes Markdown structure in label, data, and `UserName` values before they reach the template. Applied in `notifier.prepare`, so already-queued messages are covered and the stored payload keeps its original values for webhook consumers. Nested `.Data` map keys are escaped too: one shipped template prints a key, and those keys are Terraform resource addresses. - Narrows the notification Markdown grammar to what templates actually use. `CommonExtensions` enabled Tables, DefinitionLists and MathJax, each openable from a value and used by no template. Autolink stays off so a URL in a value cannot become an anchor. - Enables `html.Safelink`, restricting generated hrefs to safe schemes, and guards the panic it exposes: `parser.IsSafeURL` slices a destination before bounds-checking it, so `[docs]()` crashed both renderers. - Folds line breaks out of the `Subject:` header and encodes it, fixing a pre-existing RFC 2047 violation for non-ASCII subjects, a forged encoded-word that let a value choose the displayed subject, and headers running past RFC 5322's 998-octet line limit. Escaping is narrow on purpose, split by where each character carries meaning: - `` \[]()!<` `` everywhere. Backtick is in this group because a fenced block's info string is an HTML sink: gomarkdown writes it into `class="language-..."` unescaped, and `SkipHTML` does not apply to a `CodeBlock` node, so a value that closes the attribute and the tag injects live markup. - `#-+.>|` only in leading position, so values like `bobby-workspace` and `1.5` are untouched. - `=`, `~` and `:` are not escapable by both renderers, so the preceding line break is folded instead. `:` opens a definition list and a GFM table delimiter row that escaping `|` cannot reach. A value's *first* line has no preceding break to fold, so a real tilde fence or `===` underline is escaped there instead, accepting a visible backslash: an unterminated `~~~` at the start of a title otherwise renders the Subject, `<title>` and heading empty. - Leading indentation is truncated to three spaces. Four open an indented code block and a space has no escape. - Emphasis characters are left alone. Escaping `_` corrupts label values such as `user_override` that body templates compare with `eq`, which silently drops content from the rendered email. One golden file changes: the resource replacements `body_markdown` now reads `docker_container\[0\]`, from the map-key escaping above. Every other golden is byte-identical. **One known residual**, pinned by a test that fails if it closes: CommonMark does not process escapes inside a code span, so where a template wraps a value in one, as the workspace out-of-disk body does, the escaper's own backslashes reach the reader. That depends on where the value lands rather than what it contains, which a pre-render escaper cannot see. This narrows the class rather than closing it. **#28397 completes the fix and is stacked on this branch. This PR should not merge without it.** Escaping here cannot reach the SMTP HTML template's sinks, by design rather than by oversight: the subject is produced by `PlaintextFromMarkdown`, which strips exactly the backslashes added here, and `html.gotmpl` then interpolated the result through `text/template`. On this branch alone, a label value still reaches the Subject, `<title>` and `<h1>` as live markup. #28397 escapes at those sinks with `| html`, which is the only place the information needed to escape correctly exists. |
||
|
|
be9f8592eb |
ci: use dedicated release App token to publish releases (#28553)
## Problem The `Publish release` step fails with `HTTP 403: Resource not accessible by integration` because the default `GITHUB_TOKEN` cannot create the release tag under the active tag-create protection ruleset. ## Change Use a dedicated GitHub App token for the `Publish release` step: - Add a `Generate release App token` step using `secrets.RELEASE_APP_ID` / `secrets.RELEASE_APP_PRIVATE_KEY`. - Switch only that step's `GITHUB_TOKEN` to the minted App token. ## Required before merge (admin) 1. Create the release GitHub App (least privilege) and install it on `coder/coder`. 2. Configure `RELEASE_APP_ID` / `RELEASE_APP_PRIVATE_KEY`. 3. Add the App to the tag-create protection ruleset bypass list. Refs coder/security-automation#297. |
||
|
|
7fc9a6e449 |
fix: revert update on chat messages table (#28552)
Reverts commit
|
||
|
|
79856ebd15 |
docs: reference Coder Agents instead of Coder Tasks in style guide (#28539)
## Summary Updates the Caitlin the Citizen Developer persona in the style guide to reference Coder Agents instead of the deprecated Coder Tasks product. ### Changes * `docs/.style/style-guide/audience-and-scope.md`: replaced both "Coder Tasks" mentions with "Coder Agents" in the Caitlin persona description and Coder surface list. <details><summary>Context</summary> Coder Tasks is deprecated as of v2.34 and replaced by Coder Agents. This change keeps the internal style guide's persona reference aligned with current product naming. </details> --- This PR was drafted by a Coder Agent on behalf of Matt. |
||
|
|
7cf24b1a37 |
docs(docs/ai-coder/agents): document the chat search: full-text filter (#28474)
Documents the `search:` full-text filter in `docs/ai-coder/agents/chat-search-syntax.md` and drafts the release notes copy for chat history search. Describes `main` plus #28319, which must merge first: stemming and stopword removal apply to message content only; titles and PR titles keep `simple` whole-word matching. All claims verified against the implementation. Two statements in CODAGT-727 were inaccurate and are documented as the code behaves: - A value that tokenizes to no searchable words returns an **empty list**, not HTTP 400 (`TestListChats_Search/NoSearchableWordsReturnsEmpty`). HTTP 400 covers only an empty `search:` value, unsupported keys, bare terms, or combining `search:` with `title:`, `pr_title:`, or `pr:` (`coderd/searchquery/search.go`). - Results are **paginated** with the standard chat list cursor (default 50), not a bounded non-paginated set (`GetChats`, `listChats`). Ordering is pinned first, then most recently updated. Also verified: message bodies are indexed by the dbpurge sweep (10-minute tick, up to 5 x 10,000 rows, newest first) while titles and PR titles are searchable immediately; numeric values match exact PR numbers; `title:`, `pr_title:`, and `repo:` retain `ILIKE` semantics. `pnpm run format-docs` and `pnpm run lint-docs` pass. Closes [CODAGT-727](https://linear.app/codercom/issue/CODAGT-727/chat-search-documentation-and-release-notes) <details> <summary>Release notes draft</summary> ### Chat history search You can now search your chat history by what was said, not just by title. The chat list `q` parameter accepts a new `search:` filter that performs full-text search across chat titles, pull request titles, PR numbers, and message content, for example `?q=search:"kubernetes restart"`. Matching is case-insensitive and supports quoted phrases, `OR`, and `-` negation. Message content additionally matches English word stems (`refactor` finds a message containing `refactoring`). `search:` cannot be combined with the `title:`, `pr_title:`, or `pr:` filters; the existing substring filters are unchanged. **Upgrading with existing chat history:** the message search index is populated by a background job (every 10 minutes, up to 50,000 messages per run, newest first). Upgrades are not blocked, and chat reads and writes are unaffected while the index fills. Older messages appear in search results progressively; messages indexed before the stemming upgrade match exact word forms until re-indexed. Titles and PR titles are searchable immediately. </details> --- 🤖 This PR was generated by Coder Agents on behalf of @f0ssel. |
||
|
|
378ac91b49 |
fix: stem chat search terms with the english text search config (#28319)
Chat message search used the `simple` text search config, which folds case but does no stemming, so `refactor` did not match messages containing `refactoring`. Message bodies now index and query with the `english` config, which stems both sides. Titles, PR titles, and all other filters are unchanged. Migration `000585` adds a `chat_messages.search_tsv_config` enum column recording which config produced each stored vector and stamps existing vectors `'simple'` (a column-only update; vectors and indexes are untouched, since rewriting `search_tsv` would block message writes on large tables). The dbpurge sweep gains a bounded `ReindexStaleChatMessagesSearchTsv` pass that rewrites stale vectors newest first and stops for the process lifetime once drained. Until a row is rewritten, `GetChats` matches it with the config that produced it, so pre-migration exact-form searches keep working during the drain. Vectors written by old binaries mid rolling upgrade cannot stamp the config, stay pending, and self-heal on the next upgraded sweep. Steady-state DB load is unchanged. The down migration resets `english` vectors to `NULL` so the parent version's existing sweep rewrites them with `simple`. Closes [CODAGT-867](https://linear.app/codercom/issue/CODAGT-867/backend-search-improvements-to-handle-partial-matches) <details> <summary>Decision log</summary> - **`english` stemming over prefix matching.** On a 300k-row corpus a stemmed lookup is a single GIN entry-tree probe, while `term:*` prefix matching unions posting lists of every lexeme sharing the prefix (up to a full scan for short prefixes), is asymmetric, and loses the `websearch_to_tsquery` phrase/OR/negation UX. `pg_trgm` rejected as a heavier index with different semantics. - **Scope to message bodies only** (review). Titles and PR titles keep their `simple` FTS expressions and indexes, identical to main. - **Config-versioned queue over migration-time reset** (review). An unqualified `UPDATE ... SET search_tsv = NULL` rewrites every indexed row in the migration transaction, and an old replica could backfill reset rows with `simple` vectors that permanently leave the `search_tsv IS NULL` queue. The config column makes staleness explicit and both problems disappear. - **Enum over `text`** (review) for the config column. - **Trade-offs.** English stopwords are dropped from message search (stopword-only queries match no messages); stemming is English-specific; titles do not stem; prefix matching remains out of scope. </details> --- 🤖 This PR was generated by Coder Agents on behalf of @f0ssel. |
||
|
|
6284964b5f |
test(coderd): remove flaky audit transaction case (#28468)
The `TransactionAfterNoChange` case in `TestChatRetentionDays_AuditInfrastructureFailureRejectsWrite` was flaky in [Flake Check run 32717900411](https://github.com/coder/coder/actions/runs/32717900411). Its generic `InTx` wrapper armed a shared fail-next flag, so an unrelated background transaction could consume the failure and let the target PUT return 204 instead of the expected 500. Remove the shared transaction wrapper and replace the endpoint-level case with deterministic unit coverage of `auditedChatOperationalSettingWrite`. The mock transaction executes the no-op callback successfully and then returns an error, preserving coverage that audit suppression happens only after the transaction succeeds without involving API background transactions. |
||
|
|
16e0c4dba7 |
fix(site/src/pages/AgentsPage): remove redundant model warning (#28484)
The empty chat form showed a generic no-model warning directly above the existing provider and model setup notice, repeating the same unavailable-state information. Remove the redundant warning and update the affected stories to assert the remaining actionable setup notice. |