Commit Graph
100 Commits
Author SHA1 Message Date
Cian Johnston d7439a9de0 feat: add Prometheus metrics for chatd subsystem (#24371)
Adds 7 Prometheus metrics to the chatd subsystem and introduces typed
`ActivityBumpReason` for deadline bump attribution.

| Metric | Type | Labels |
|--------|------|--------|
| `coderd_chatd_chats` | Gauge | `state` (streaming, waiting) |
| `coderd_chatd_message_count` | Histogram | `provider` |
| `coderd_chatd_prompt_size_bytes` | Histogram | `provider` |
| `coderd_chatd_tool_result_size_bytes` | Histogram | `provider`,
`tool_name` |
| `coderd_chatd_ttft_seconds` | Histogram | `provider` |
| `coderd_chatd_compaction_total` | Counter | `provider`, `result` |
| `coderd_chatd_steps_total` | Counter | `provider` |

> 🤖
2026-04-15 19:53:10 +01:00
Cian JohnstonandCopilot 2b68a1f4bd chore: update our fork of fantasy/anthropic-sdk-go to fix MarshalJSON over-allocations (#24390)
Updates go.mod to reference our internal fork of anthropic-sdk-go.

See: https://github.com/coder/anthropic-sdk-go/pull/7

Relates to CODAGT-167

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-15 19:47:09 +01:00
Cian Johnston f8d521d527 fix(site): style navbar version badge as a notch (#24372)
Pins navbar version badge flush to viewport top and removes top border / border-radius for a "notch" appearance.

> 🤖
2026-04-15 13:03:35 +01:00
Cian Johnston 6194bd6f57 fix: address post-merge review findings for chat org scoping (#24297)
Addresses review findings from #23827 that were added post-merge:

- Persisted attachments now store `organizationId`; mismatched orgs
pruned on restore
- Workspace selection reconciliation: stale IDs from previous orgs
dropped via derived `effectiveWorkspaceId`
- Org picker uses `permittedOrganizations()` for RBAC-aware filtering
- Org picker hidden when user belongs to only one org
- Ref-sync `useEffect` replaced with `useEffectEvent`
- `CreateWorkspace()` and `ListTemplates()` take `organizationID` and
`db` as required function parameters instead of optional struct fields —
compiler enforces them, removes scattered nil guards
- Cross-org template check in `CreateWorkspace` is now unconditional
- `ListTemplates` org-scoping filter now has test coverage
- `setupChatInfra` comment fixed; test helpers use params structs
instead of positional UUIDs
- Enterprise test documents that org admin only sees own chats (handler
hardcodes `OwnerID` — future work needs sidebar UI before lifting that
restriction)

> 🤖
2026-04-15 11:39:05 +01:00
Cian Johnston c552f9f281 fix: stop group spend limits from leaking across org boundaries (#24294)
Three SQL queries (`GetUserGroupSpendLimit`,
`ResolveUserChatSpendLimit`, `GetUserChatSpendInPeriod`) aggregated chat
spend limits and usage globally across all organizations. A restrictive
group limit in org A would bleed into org B.

## Changes

- Add `organization_id` parameter to all three SQL queries in
`coderd/database/queries/chats.sql`
- When nil UUID is passed, queries fall back to global behavior
(backward compat for HTTP dashboard endpoints)
- When real org ID is passed, limits and spend are scoped to that
organization
- Thread `organizationID` through `ResolveUsageLimitStatus` →
`checkUsageLimit` → all chatd call sites
- Update dbauthz wrappers for new param structs
- HTTP endpoints (`chatCostSummary`, `getMyChatUsageLimitStatus`) pass
`uuid.Nil` with TODO for future org-scoped UI
- Add `TestResolveUsageLimitStatus_OrgScoped` with 5 test cases covering
org isolation, nil-UUID fallback, spend scoping, and user override
priority

Closes coder/internal#1466

> 🤖
2026-04-14 16:56:17 +01:00
Cian Johnston 116323d3cf feat: graduate web-push from experiment to always-on (#24310)
* Removes experiment `web-push`.
* Falls back to NoopWebpusher in case of error
* Checks browser capability in FE
* Adds note to agents getting-started docs regarding webpush without TLS

> 🤖
2026-04-14 09:07:06 +01:00
Cian Johnston 11fe4972b6 fix(site): use readonly Organization[] and explicit is_default lookups (#24288)
`OrganizationAutocomplete` declared `options: Organization[]` (mutable),
but `useDashboard().organizations` returns `readonly Organization[]`.
Callers forced to spread to satisfy the type even though the component
never mutates the array.

While fixing this, found two more issues with how organization context
is resolved:

- `AgentCreateForm`, `ChatPageContent`, and `OrganizationRedirect` used
`organizations[0]` assuming the default org is first — nothing
guarantees that ordering.
- `ChatPageInput` was guessing the org from `useDashboard()` for file
uploads on existing chats, even though the chat already has
`organization_id`. In multi-org deployments, uploads could land in the
wrong org.

Changes:
- Widen `OrganizationAutocomplete` `options` to `readonly
Organization[]`
- Remove unnecessary `[...organizations]` spread in `AgentCreateForm`
- Replace all `organizations[0]` with `.find(o => o.is_default) ??
organizations[0]` for users not in the default org
- Eliminate `[...organizations].sort()` allocation in
`OrganizationRedirect` with direct `.find()` lookups (equivalent
priority: editable default → any editable → viewable default → any
viewable)
- Thread `chat.organization_id` from `AgentChatPage` →
`AgentChatPageView` → `ChatPageInput` so file uploads use the chat's
actual org instead of guessing

Closes #24285

> 🤖 PR initially created by Claude Opus 4.6
2026-04-13 14:28:48 +00:00
Cian JohnstonandMathias Fredriksson 22062ec52e feat: add organization scoping to chats (#23827)
Fixes https://github.com/coder/internal/issues/1436

* Adds organization_id to chats with backfill (workspace org → user org membership → default org)
* No support yet for ACLs (follow-up issue)
- Cross-org workspace binding rejected (both in `CreateChatRequest` and in `create_workspace` tool
- Adds `OrganizationAutocomplete` to `AgentCreateForm`
- Docs updated with `organization_id` in chats-api.md

> 🤖 Written by a Coder Agent. Reviewed by many humans and many agents.

---------

Co-authored-by: Mathias Fredriksson <mafredri@gmail.com>
2026-04-13 12:31:25 +01:00
Cian Johnston 0a14bb529e refactor(site): convert OrganizationAutocomplete to fully controlled component (#24211)
Fixes https://github.com/coder/internal/issues/1440

- Convert `OrganizationAutocomplete` to a purely presentational, fully
controlled component
- Accept `value`, `onChange`, `options` from parent; remove internal
state, data fetching, and permission filtering
- Update `CreateTemplateForm` and `CreateUserForm` to own org fetching,
permission checks, auto-select, and invalid-value clearing inline
- Memoize `orgOptions` in callers for stable `useEffect` deps
- Rewrite Storybook stories for the new controlled API


> 🤖 Written by a Coder Agent. Reviewed by a human.
2026-04-10 13:56:43 +01:00
Cian Johnston 1e40cea199 feat: warn in CLI when server runs dev or RC builds (#24158)
Adds warning on stderr when the server version contains `-devel` or
`-rc.N`

> 🤖 Written by a Coder Agent. Will be reviewed by a human.
2026-04-09 12:48:35 -04:00
Cian Johnston 27e5ff0a8e chore: update to our fork of charm.land/fantasy with appendCompact perf improvement (#24142)
Fixes CODAGT-117

Updates go.mod to reference our forks of the following dependencies:
* charmbracelet/anthropic-sdk-go =>
https://github.com/coder/anthropic-sdk-go/tree/coder_2_33
* charm.land/fantasy => https://github.com/coder/fantasy/tree/coder_2_33
2026-04-09 13:08:19 +01:00
Cian Johnston 461a31e5d8 feat(site): add under-construction navbar stripes for pre-release builds (#24157)
Dev and RC builds now show diagonal warning stripes in the navbar plus a
centered version badge, making it impossible to miss which build you're
running.

**Devel build:** amber "warning" from theme

**RC build:** sky "pending" from theme

> 🤖 Written by a Coder Agent. Will be reviewed by a human.
2026-04-08 20:10:03 +00:00
Cian JohnstonandCopilot 7b0421d8c6 fix: revert auto-assign agents-access role enabled (#24170)
This reverts commit d4a9c63e91 (#23968).

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-08 20:56:17 +01:00
Cian Johnston 497f637f58 chore: revert force deploying main (#23290) (#24072)
⚠️ DO NOT MERGE UNTIL @f0ssel SAYS SO ⚠️ 

This reverts commit 8f78c5145f
(https://github.com/coder/coder/pull/23290).
2026-04-08 11:19:14 -04:00
Cian Johnston f820945d9f refactor: decompose AgentSettingsBehaviorPageView + remove kyleosophy (#24141)
- Remove Kyleosophy alternative completion chimes (keeps original chime
intact)
- Extract 5 sub-components from the 717-line god component:
  - `PersonalInstructionsSettings` — user prompt textarea form
- `SystemInstructionsSettings` — admin system prompt + TextPreviewDialog
  - `VirtualDesktopSettings` — admin desktop toggle
  - `WorkspaceAutostopSettings` — admin autostop toggle + duration form
  - `RetentionPeriodSettings` — admin retention toggle + number input
- Parent is now a ~160-line layout shell
- `isAnyPromptSaving` coupling preserved via prop
- Add `docs/plans/` to `.gitignore`

> 🤖 Written by a Coder Agent. Reviewed by a human.
2026-04-08 14:01:38 +01:00
Cian Johnston 233343c010 feat: add chat and chat_files cleanup to dbpurge (#23833)
Fixes https://github.com/coder/coder/issues/23910

Adds periodic cleanup of chats and chat files to the dbpurge background
goroutine, with a configurable retention period exposed in the Agent
settings UI.

> 🤖 Written by a Coder Agent. Reviewed by a human.
2026-04-08 11:08:09 +01:00
Cian Johnston 63924ac687 fix(site): use async findByLabelText in ProviderAccordionCards story (#24087)
- Use async `findByLabelText` instead of sync `getByLabelText` in
`ProviderAccordionCards` story
- Same bug fixed in #23999 for three other stories but missed for this
one

> 🤖 Written by a Coder Agent. Will be reviewed by a human.
2026-04-07 14:13:56 +02:00
Cian Johnston d5a1792f07 feat: track chat file associations with chat_file_links on chats (#23537)
Needed by #23833

Adds a `chat_file_links` association table to track which files are
associated with each chat.

- `AppendChatFileIDs` query links a file to a chat with deduplication
- `GetChatFileMetadataByIDs` query returns lightweight file metadata by
IDs
- Tool-created files (e.g. `propose_plan`) are linked to the chat after
insert
- User-uploaded files are linked to the chat when the referencing
message is sent
- Single-chat GET endpoint hydrates `files: ChatFileMetadata[]` on the
response

> 🤖 Created by Coder Agents and massaged into shape by a human.
2026-04-07 12:05:29 +01:00
Cian Johnston 5b28548d1c chore(docs): fix sample command to grant role (#23987)
Fixes the sample bash one-liner. `--status` does not exist (yet)
apparently.
2026-04-02 16:11:51 +00:00
Cian Johnston b5da77ff55 test: cover read_template and create_workspace allowlist enforcement (#23645)
- Extend `TestChatTemplateAllowlistEnforcement` to also exercise
`read_template` and `create_workspace` through the allowlist
- Mock LLM now chains 4 tool calls: list_templates, read_template
(blocked), read_template (allowed), create_workspace (blocked)
- Wire dummy `CreateWorkspace` config into test server so the tool
reaches the allowlist check
- Generalize tool result collection to support multiple calls per tool
name

> 🤖 Created by Coder Agents and reviewed by Kyle the human.
2026-04-02 15:39:40 +00:00
Cian Johnston d4a9c63e91 feat: auto-assign agents-access role to new users when experiment enabled (#23968)
When the `agents` experiment is enabled, new users are automatically
granted the `agents-access` role at creation time so they can use Coder
Agents without manual admin intervention.

- Auto-assigns in `CreateUser()` — covers admin API, OAuth, and OIDC
creation paths
- Skips auto-assign for OIDC users when enterprise site role sync is
enabled (sync overwrites roles on every login; those admins should use
`--oidc-user-role-default` instead)
- CLI `create-admin-user` bypasses `CreateUser()` but creates `owner`
users who already have all permissions

> 🤖 Written by a Coder Agent. Will be reviewed by a human.
2026-04-02 14:46:47 +01:00
Cian Johnston 2ebc076b9e fix: make 'chat has no workspace agent' error actually helpful (#23971)
- Change `errChatHasNoWorkspaceAgent` message from cryptic `"chat has no
workspace agent"` to actionable `"workspace has no running agent: the
workspace may be stopped. Use the start_workspace tool to start it, or
create_workspace to create a new one"`
- Update test assertions to match the new message substring

> 🤖 Written by a Coder Agent. Reviewed by a human.
2026-04-02 14:18:26 +01:00
Cian Johnston 16add93908 fix(coderd/x/chatd): stabilize subagent pubsub completion test (#23944)
- stabilize `TestAwaitSubagentCompletion/CompletesViaPubsub` by waiting
for durable completion state before sending the synthetic pubsub wake
- add coverage for successful subagent completion with an empty report

> 🤖 Written by a Coder Agent. Reviewed by a human.
2026-04-02 12:29:47 +01:00
Cian Johnston cd784c755a fix(agent): exorcise data race haunting contextConfigAPI on reconnect (#23946)
Fixes: coder/internal#1441

- Move `contextConfigAPI` init from `handleManifest` to `init()`,
matching all other API fields
- Change `agentcontextconfig.NewAPI` to accept `func() string` closure
(lazy directory evaluation)
- `Config()` and HTTP handler now compute on demand via
`a.manifest.Load().Directory`
- Widen `TestAgent_Reconnect` to loop 5 reconnections with a non-empty
manifest directory
- Add `TestContextConfigAPI_InitOnce` internal test verifying lazy eval
across manifest changes
- Add `TestNewAPI_LazyDirectory` unit test for the lazy contract

> 🤖 Written by a Coder Agent. Reviewed by a human.
2026-04-02 09:00:13 +01:00
Cian Johnston 515ba209fd ci: fix weekly-docs check failing on pnpm cache save (#23937) 2026-04-01 20:04:46 +01:00
Cian Johnston b1c42bb630 fix(site/src/pages/AgentsPage): use chat ID as terminal reconnection token (#23926)
The terminal panel in the agents sidebar generated a fresh
`reconnectionToken` via `crypto.randomUUID()` on every mount. Navigating
between chats or reloading the page orphaned the PTY session.

- Use the chat ID (`agentId`) as the reconnection token for
`TerminalPanel`
- Add optional `chatId` prop to `TerminalPanel`, falling back to a
random UUID when not provided
- Thread `agentId` from `AgentChatPageView` to `TerminalPanel`

This mirrors how the dedicated Terminal page persists sessions via a
URL-stored token.

> 🤖 Written by a Coder Agent. Reviewed by a human.
2026-04-01 16:22:10 +00:00
Cian Johnston 129e3509a3 fix(site): address post-merge review comments on kyleosophy chimes (#23896)
Fixes issues found in post-merge review of #23891 and #23892.

- **P2:** Export `_resetForTesting()` from `chime.ts` to break
cross-test cache dependency; call in `beforeEach`
- **P2:** Add `KylesophyToggle` and `TogglesKyleosophy` Storybook
stories
- **P3:** Fix JSDoc on `maybePlayChime` — terminal states are
`waiting|pending`, not `waiting|error`
- **P3:** Rename `setKylesophyLocal` back to `setLocalKyleosophy` to
match `setLocal*` convention
- Preserve original `location` property descriptor in
`isKylesophyForced` tests to avoid leaking mutated descriptors across
test suites (#23892 review)

> 🤖 Written by a Coder Agent. Reviewed by a human.
2026-04-01 15:40:10 +01:00
Cian Johnston 7198f9040d fix: rename user-facing 'chats' to 'Coder Agents' (#23905)
Refs #23897

- Rename user-facing "chats" to "Coder Agents" (feature name) or
"conversations" (individual instances)
- Covers UI strings, docs prose, Storybook stories, and aria labels
- API paths, internal code identifiers, and the "Chats API" docs page
name are intentionally left unchanged
- TaskPage / AI Tasks are out of scope

> 🤖 Written by a Coder Agent. Will be reviewed by a human.
2026-04-01 14:30:04 +01:00
Cian Johnston 2a51687ff3 fix: stop amputating RC suffixes from docs URLs (#23903)
Fixes #23897 (docs link only — naming rename is in #23905)

- Fix version stripping logic in both Go (`codersdk/deployment.go`) and
TypeScript (`site/src/utils/docs.ts`) to preserve `-rc.X` suffixes
instead of amputating them along with `-devel`
- Add `v0.0.0` fallback in the TS frontend to match Go backend behavior
for dev builds
- Add tests covering RC, devel, and plain release version strings

> 🤖 Written by a Coder Agent. Will be reviewed by a human.
2026-04-01 13:05:14 +00:00
Cian Johnston 7ddde0887b feat(site): force-enable kyleosophy on dev.coder.com (#23892)
- Force-enable Kyleosophy on `dev.coder.com` via hostname check
- Toggle shows as checked + disabled with "Kyleosophy is mandatory on
`dev.coder.com`"
- `isKylesophyForced()` exported for UI and testability
- Tests for forced/non-forced hostname behavior

> 🤖 Written by a Coder Agent. Reviewed by a human.
2026-04-01 11:39:04 +01:00
Cian Johnston bec426b24f feat(site): add Kyleosophy alternative completion chimes (#23891)
- Add "Enable Kyleosophy" toggle to Settings > Behavior
- When enabled, replaces standard completion chime with random Kyle
sound clips
- Ships 8 alternative `.mp3` files as static assets (~82KB total)
- localStorage preference (`agents.kyleosophy`), defaults to off
- Pauses orphaned Audio elements on sound URL change to prevent overlap

<details><summary>Review findings addressed</summary>

- **P2** Stale JSDoc on `playChimeAudio` — updated to reflect
parameterized behavior
- **P3** Overlapping audio on rapid completions — added
`chimeAudio?.pause()` before replacement
- **P3** Test ordering dependency — pinned `Math.random` for
determinism, documented cache behavior
- **Nit** Setter naming — `setLocalKyleosophy` → `setKylesophyLocal`
- Toggle moved to bottom of Behavior page per product request
- Description changed to "IYKYK" per product request

</details>

> 🤖 Written by a Coder Agent. Reviewed by a human.
2026-04-01 10:06:01 +00:00
Cian Johnston d6df78c9b9 chore: remove racy ChatStatusPending assertions after CreateChat (#23882)
Removes 6 fragile `require.Equal(t, codersdk.ChatStatusPending,
chat.Status)` assertions from chat relay and creation tests.

**Root cause**: In HA tests with two replicas sharing the same DB, the
worker can acquire a just-created chat (flipping `pending → running` via
`AcquireChats`) before the HTTP response reaches the test. All affected
tests already synchronize via `require.Eventually` waiting for `running`
status, making the initial assertion both redundant and racy.

- Remove 5 assertions in `enterprise/coderd/exp_chats_test.go` (all
`TestChatStreamRelay` subtests)
- Remove 1 assertion in `coderd/exp_chats_test.go` (`TestPostChats`)
- An existing comment in `TestPostChats/Success` already documents this
exact race

Fixes flake:
https://github.com/coder/coder/actions/runs/23807597632/job/69385425724

> 🤖 Written by a Coder Agent. Will be reviewed by a human.
2026-04-01 10:00:50 +01:00
Cian Johnston a164d508cf fix(coderd/x/chatd): gate control subscriber to ignore stale pubsub notifications (#23865)
Fixes flaky `TestOpenAIReasoningWithWebSearchRoundTripStoreFalse` and
`TestOpenAIReasoningWithWebSearchRoundTrip`.

## Changes

- Gate the `processChat` control subscriber's cancel callback behind a
`chan struct{}` that is closed after publishing `"running"` status
- Add `TestGatedControlCancel` with 4 subtests exercising the gate logic

<details>
<summary>Root cause analysis</summary>

`SendMessage` publishes a `"pending"` notification on
`chat:stream:<chatID>` via PostgreSQL `NOTIFY`. `processChat` subscribes
to the same channel for control signals. Due to async NOTIFY delivery,
the `"pending"` notification can arrive at the control subscriber
**after** it registers its queue — even though it was published
**before**. `shouldCancelChatFromControlNotification("pending")` returns
`true`, immediately self-interrupting the processor before it does any
work.

The fix gates the cancel callback behind a closed channel. The channel
is closed after `processChat` publishes `"running"` status, so stale
notifications from before initialization are harmlessly ignored.
`close()` provides a happens-before guarantee in the Go memory model.
</details>

> 🤖 Written by a Coder Agent. Reviewed by a human.
2026-03-31 22:55:20 +01:00
Cian Johnston 2a990ce758 feat: show friendly alert for missing agents-access role (#23831)
Replaces the generic red `ErrorAlert` ("Forbidden.") with a proactive
permission check and friendly info alert when a user lacks the
`agents-access` role.

- Add `createChat` permission check to `permissions.json` using
`owner_id: "me"`
- Handle `"me"` owner substitution in `renderPermissions` (SSR path)
- Pass `canCreateChat` from `useAuthenticated().permissions` into
`AgentCreateForm`
- Show `ChatAccessDeniedAlert` and disable input immediately (no need to
trigger a 403 first)
- Also catch 403 errors as a fallback in case permissions aren't yet
loaded
- Add `ForbiddenNoAgentsRole` Storybook story with `play` assertions
- Add `TestRenderPermissionsResolvesMe` Go test to pin the `"me"`
sentinel substitution

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

- Uses the existing `permissions.json` + `checkAuthorization` system
rather than a separate API call
- `owner_id: "me"` is resolved to the actor's ID by both the auth-check
API endpoint and the SSR `renderPermissions` function
- Go test uses a real `rbac.StrictCachingAuthorizer` (not a mock) so it
verifies both the sentinel substitution and the RBAC role evaluation
end-to-end
- Alert follows the exact same `Alert` pattern as the 409 usage-limit
block
- Uses `severity="info"` and links to the getting-started docs Step 3
- Textarea is disabled proactively so the user never sees the scary
generic error

</details>

> 🤖 Created by a Coder Agent and will be reviewed by a human.
2026-03-31 17:26:58 +01:00
Cian JohnstonandCopilot 3190406de3 fix(site): stop workspace deletes playing hide-and-seek (#23641)
- Fix workspaces list invalidation after kebab-menu delete and add
Storybook coverage for the immediate `Deleting` state.

> 🤖 This PR was made by Coder Agents and read by me.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-31 11:36:47 +01:00
Cian Johnston 3ce82bb885 feat: add chat-access site-wide role to gate chat creation (#23724)
- Add `chat-access` built-in role granting chat CRUD at User scope
- Exclude `ResourceChat` from member, org member, and org service
account `allPermsExcept` calls
- Allow system, owner, and user-admin to assign the new role
- Migration auto-assigns role to users who have ever created a chat
- Update RBAC test matrix: `memberMe` denied, `chatAccessUser` allowed

**Breaking change**: Members without `chat-access` lose chat creation
ability. Migration covers existing chat creators. Members who have never
created a chat do not get this role automatically applied.

> 🤖 This PR was created by a Coder Agent and reviewed by me.
2026-03-31 10:07:21 +01:00
Cian Johnston 0880a4685b ci: fix pnpm not found in check-docs job (#23807)
- Enable corepack before the linkspector step so `pnpm` shim is in PATH
- `action-linkspector@v1.4.1` internally calls `actions/setup-node@v5`,
which now defaults `package-manager-cache: true` — it detects
`pnpm-lock.yaml` and tries to resolve the `pnpm` binary, but it's not
installed on the runner
- Add TODO to remove the workaround when upstream is fixed

Upstream: https://github.com/UmbrellaDocs/action-linkspector/issues/54

> 🤖 Cian asked a Coder Agent to make this PR and then reviewed the
change.
2026-03-30 21:28:51 +05:00
Cian Johnston a5c72ba396 fix(coderd/agentapi): trim whitespace from workspace agent metadata values (#23709)
- Trim leading/trailing whitespace from metadata `value` and `error`
fields before storage
- Trimming happens before length validation so whitespace-padded values
are handled correctly
- Add `TrimWhitespace` test covering spaces, tabs, newlines, and
preserved inner whitespace
- No backfill needed (unlogged table, stores only latest value)

> 🤖 Created by a Coder Agent, reviewed by me.
2026-03-27 15:08:47 +00:00
Cian Johnston 3f55b35f68 refactor: replace AsSystemRestricted with narrower actors (#23712)
Replace overly-broad `AsSystemRestricted` with purpose-built actors:

- **OAuth2 provider paths** → `AsSystemOAuth2` (13 call sites across
`tokens.go`, `registration.go`, `apikey.go`)
- **Provisioner daemon health read** → `AsSystemReadProvisionerDaemons`
(1 site in `healthcheck/provisioner.go`)
- **Provisionerd file cache paths** → `AsProvisionerd` (2 sites in
`provisionerdserver.go`, matching existing usage nearby)

<details>
<summary>Implementation notes</summary>

Each replacement actor is a strict subset of `AsSystemRestricted`. Every
DB method
at each call site is already covered by the narrower actor's
permissions:

- `subjectSystemOAuth2`: OAuth2App/Secret/CodeToken (all), ApiKey (Read,
Delete), User (Read), Organization (Read)
- `subjectSystemReadProvisionerDaemons`: ProvisionerDaemon (Read)
- `subjectProvisionerd`: File (Create, Read) plus provisionerd-scoped
resources

No new permissions added. `nolint:gocritic` comments updated to reflect
the new actors.
</details>

> 🤖 Created by a Coder Agent, reviewed by me.
2026-03-27 15:08:30 +00:00
Cian Johnston bfee7e6245 fix: populate all chat fields in pubsub events (#23664)
*Problem:* `publishChatPubsubEvent` was constructing a partial
`codersdk.Chat` that omitted `LastModelConfigID` and other fields. Go's
zero-value UUID caused the sidebar to show "Default model" for chats
received via SSE.

*Solution:*
- Extracted `convertChat`/`convertChats` from `exp_chats.go` into
`db2sdk.Chat`/`db2sdk.Chats`, alongside existing `ChatMessage`,
`ChatQueuedMessage`, and `ChatDiffStatus` converters.
`publishChatPubsubEvent` now calls `db2sdk.Chat(chat, nil)` instead of
maintaining its own copy of the conversion logic
- Added backend integration test
`TestWatchChats/CreatedEventIncludesAllChatFields`
- Added frontend regression tests for nil-UUID and valid model config ID
cases

> 🤖 Created by Coder Agents, reviewed by this human.
2026-03-26 16:49:26 +00:00
Cian Johnston 81fe7543b4 chore: set tls.VersionTLS12 MinVersion in cli/server.go to address gosec warning (#23646)
I was investigating `//nolint` comments and this one popped up.
It raised my eyebrows enough to warrant its own PR.
2026-03-26 14:53:47 +00:00
Cian Johnston 847a88c6ca chore: clean up stale and dangerous //nolint comments (#23643)
## Changes

- **Commit 1**: Remove 17 unnecessary `//nolint` directives:
  - `//nolint:varnamelen` — linter not active
  - `//nolint:unused` on exported `SlimUnsupported`
  - `//nolint:govet` in `coderd/httpmw/csrf` — no longer fires
  - `//nolint:revive` on functions refactored since the nolint was added
- `//nolint:paralleltest` citing Go 1.22 loop variable capture
(obsolete)
- Bare `//nolint` narrowed to specific `//nolint:gocritic` with
justification

- **Commit 2**: Fix root causes behind 5 dangerous nolint suppressions:
- Add `MinVersion: tls.VersionTLS12` to TLS client config (removes
`gosec` G402)
- Delete trivial unexported wrappers `apiKey()`/`normalizeProvider()` in
chatprovider (removes `revive` confusing-naming)
- Add doc comments to `StartWithAssert` and `Router` (removes `revive`
exported)
  - Rename unused parameters to `_` in integration test helpers

> 🤖 This PR was created using Coder Agents and reviewed by me.
2026-03-26 14:13:53 +00:00
Cian Johnston f164463c6a fix(scripts/metricsdocgen): shush the prometheus scanner in CI (#23642)
- Suppress informational `log.Printf` messages from the metrics scanner
when stdout is not a TTY (i.e. piped via `atomic_write` in `make gen` or
CI)
- Genuine warnings (`warnf`) still print unconditionally so real
problems remain visible
- `log.Fatalf` for fatal errors is unchanged

> 🤖 Created by Coder Agents and reviewed by a human
2026-03-26 12:58:02 +00:00
Cian Johnston d175e799da feat: show agent badge on workspace list (#23453)
- Adds `GET /api/experimental/chats/by-workspace` endpoint that returns
workspace_id → latest chat_id mapping
- Modifies FE to fetch this alongside the workspace list, gated on
`agents` experiment and render an "Agent" badge similar to the existing
"Task" badge in `WorkspacesTable`
- Badge links to the "latest chat" linked to the given workspace.

Notes:
- Intentionally uses `fetchWithPostFilter` for RBAC to decouple from
workspaces API — will migrate to `workspaces_expanded` view later.
- If users have multiple chats linked to the same workspace, the badge
will link to the most recently updated one.

> 🤖 This PR was created with the help of Coder Agents, and has been
reviewed by my human. 🧑‍💻
2026-03-26 11:30:12 +00:00
Cian Johnston 7a9d57cd87 fix(coderd): actually wire the chat template allowlist into tools (#23626)
Problem: previously, the deployment-wide chat template allowlist was never actually wired in from `chatd.go`

- Extracts `parseChatTemplateAllowlist` into shared `coderd/util/xjson.ParseUUIDList`
- Adds `Server.chatTemplateAllowlist()` method that reads the allowlist from DB
- Passes `AllowedTemplateIDs` callback to `ListTemplates`, `ReadTemplate`, and `CreateWorkspace` tool constructors

> 🤖 Created by Coder Agents and reviewed by a human.
2026-03-25 22:15:27 +00:00
Cian Johnston c753a622ad refactor(agent): move agentdesktop under x/ subpackage (#23610)
- Move `agent/agentdesktop/` to `agent/x/agentdesktop/` to signal
experimental/unstable status
- Update import paths in `agent/agent.go` and `api_test.go`

> 🤖 This mechanical refactor was performed by an agent. I made sure it
didn't change anything it wasn't supposed to.
2026-03-25 18:23:52 +00:00
Cian Johnston 5c9b0226c1 fix(coderd/x/chatd): make clarification rules coherent (#23625)
- Clarify the system prompt to prefer tools before asking the user for
clarification.
- Limit clarification to cases where ambiguity or user preferences
materially affect the outcome.
- Remove the contradictory instruction to always start by asking
clarifying questions.

> 🤖 This PR has been reviewed by the author.
2026-03-25 18:21:36 +00:00
Cian Johnston fb5c3b5800 ci: restore depot runners (#23611)
This commit reverts the previous changes to CI jobs affected by disk
space issues on depot runners.
2026-03-25 16:08:11 +00:00
Cian Johnston 796872f4de feat: add deployment-wide template allowlist for chats (#23262)
- Stores a deployment-wide agents template allowlist in `site_configs`
(`agents_template_allowlist`)
- Adds `GET/PUT /api/experimental/chats/config/template-allowlist`
endpoints
- Filters `list_templates`, `read_template`, and `create_workspace` chat
tools by allowlist, if defined (empty=all allowed)
- Add "Templates" admin settings tab in Agents UI ([what it looks
like](https://624de63c6aacee003aa84340-sitjilsyrr.chromatic.com/?path=/story/pages-agentspage-agentsettingspageview--template-allowlist))

> 🤖 This PR was created with the help of Coder Agents, and has been
reviewed by my human. 🧑‍💻
2026-03-25 15:19:17 +00:00
Cian Johnston ef2eb9f8d2 fix: strip invisible Unicode from prompt content (#23525)
- Add `SanitizePromptText` stripping ~24 invisible Unicode codepoints
and collapsing excessive newlines
- Apply at write and read paths for defense-in-depth
- Frontend: warn in both prompt textareas when invisible characters
detected
- Explicit codepoint list (not blanket `unicode.Cf`) to avoid breaking
flag emoji
- 34 Go tests + idempotency meta-test, 11 TS unit tests, 4 Storybook
stories

> This PR was created with the help of Coder Agents, and was reviewed by my human.
2026-03-25 14:09:24 +00:00
Cian Johnston f14f58a58e feat(coderd/x/chatd): send Coder identity headers to upstream LLM providers (#23578)
- Add `X-Coder-Owner-Id`, `X-Coder-Chat-Id`, `X-Coder-Subchat-Id`,
`X-Coder-Workspace-Id` headers to all outgoing LLM API requests from
chatd
- Extend `ModelFromConfig` with `extraHeaders` param, forwarded via
Fantasy `WithHeaders` on all 8 providers
- Add `CoderHeaders(database.Chat)` helper to build the header map from
chat state
- Update all 4 `ModelFromConfig` call sites (resolveChatModel,
computer-use override, title gen, push summary)
- Thread `database.Chat` into `generatePushSummary` (was `chatTitle
string`)
- Tests: `TestCoderHeaders` (4 subtests),
`TestModelFromConfig_ExtraHeaders` (OpenAI + Anthropic),
`TestModelFromConfig_NilExtraHeaders`
- Refactor existing `TestModelFromConfig_UserAgent` to use channel-based
signaling

> 🤖 This PR was generated by Coder Agents and self-reviewed by a human.
2026-03-25 13:34:29 +00:00
Cian Johnston 46edaf2112 test: reduce number of coderdtest instances (#23463)
Consolidates coderdtest invocations in 7 tests to reduce 23 instances to 7 across:
- `TestGetUser` (3 → 1) — read-only user lookups
- `TestUserTerminalFont` (3 → 1) — each creates own user via
CreateAnotherUser
- `TestUserTaskNotificationAlertDismissed` (3 → 1) — each creates own
user
- `TestUserLogin` (3 → 1) — each creates/deletes own user
- `TestExpMcpConfigureClaudeCode` (5 → 1) — writes to isolated temp dirs
- `TestOAuth2RegistrationTokenSecurity` (3 → 1) — independent
registrations
- `TestOAuth2SpecificErrorScenarios` (3 → 1) — independent error
scenarios

> 🤖 This PR was created with the help of Coder Agents, and has been
reviewed by my human. 🧑‍💻
2026-03-25 09:53:06 +00:00
Cian Johnston 32acc73047 ci: bump runner sizes (#23514)
Bumps the runners changed in 5544a60b6e to larger sizes.
2026-03-24 18:38:03 +00:00
Cian Johnston 5544a60b6e ci: yeet depot runners in favour of GitHub runners (#23508)
Depot runners are running out of disk space and blocking builds.
Temporarily switch the build and release jobs from depot runners to
GitHub-hosted runners:

- `ci.yaml` build job: `depot-ubuntu-22.04-8` → `ubuntu-latest`
- `release.yaml` check-perms + release jobs: `depot-ubuntu-22.04-8` →
`ubuntu-latest`

**This is intended to be reverted once depot resolves their disk space
issues.**

> 🤖 This PR was created with the help of Coder Agents, and will be
reviewed by my human. 🧑‍💻
2026-03-24 17:19:38 +00:00
Cian Johnston fd1e2f0dd9 fix(coderd/database/dbauthz): skip Accounting check when sub-test filtering (#23281)
- Detect `-testify.m` sub-test filtering in `SetupSuite` and skip the `Accounting` check.

> 🤖 This PR was created with the help of Coder Agents, and was reviewed by my human. 🧑‍💻
2026-03-24 14:58:04 +00:00
Cian Johnston cbd56d33d4 ci: disable go cache for build jobs to prevent disk space exhaustion (#23484)
Disables Go cache for the setup-go step to workaround depot runner disk space issues.
2026-03-24 11:17:39 +00:00
Cian Johnston 9e4c283370 test: share coderdtest instances in OAuth2 validation tests (#23455)
Consolidates invocations of `coderdtest.New` to a single shared instance per
parent for the following tests:

- `TestOAuth2ClientMetadataValidation`
- `TestOAuth2ClientNameValidation`
- `TestOAuth2ClientScopeValidation`
- `TestOAuth2ClientMetadataEdgeCases`

> 🤖 This PR was created with the help of Coder Agents, and was
reviewed by my human. 🧑‍💻
2026-03-23 21:03:34 +00:00
Cian Johnston 956f6b2473 test: share coderdtest instances to stop paying the startup tax 22 times (#23454)
Consolidates 6 tests that spun up separate coderdtest instances per sub-test into a single shared instance per parent. 

> 🤖 This PR was created with the help of Coder Agents, and has been
reviewed by my human. 🧑‍💻
2026-03-23 19:54:43 +00:00
Cian Johnston 3b268c95d3 chore(dogfood): evict 22 freeloading tools from the Dockerfile (#23378)
Removes unused tools from dogfood Dockerfile:
- Go tools `moq`, `go-swagger`, `goreleaser`, `goveralls`, `kind`,
`helm-docs`, `gcr-cleaner-cli`
- curl-installed `cloud_sql_proxy`, `dive`, `docker-credential-gcr`, `grype`,
`kube-linter`, `stripe` CLI, `terragrunt`, `yq` v3, GoLand 2021.2 , ANTLR v4 jar
- apt packages `cmake`, `google-cloud-sdk-datastore-emulator`, `graphviz`, `packer`

> 🤖 This PR was created with the help of Coder Agents, and was reviewed by my human. 🧑‍💻
2026-03-23 18:25:58 +00:00
Cian Johnston 80a172f932 chore: move chatd and related packages to /x/ subpackage (#23445)
- Moves `coderd/chatd/`, `coderd/gitsync/`, `enterprise/coderd/chatd/`
under `x/` parent directories to signal instability
- Adds `Experimental:` glue code comments in `coderd/coderd.go`

> 🤖 This PR was created with the help of Coder Agents, and was
reviewed by my human. 🧑‍💻
2026-03-23 17:34:43 +00:00
Cian Johnston ef14654078 chore: move chat methods to ExperimentalClient (#23441)
- Changes all 41 chat method receivers in `codersdk/chats.go` from
`*Client` to `*ExperimentalClient` to ensure that callers are aware that
these reference potentially unstable `/api/experimental` endpoints.


> 🤖 This PR was created with the help of Coder Agents, and has been
reviewed by my human. 🧑‍💻
2026-03-23 14:32:11 +00:00
Cian Johnston ab86ed0df8 fix(site): stop hijacking navigation after archive-and-delete settles (#23372)
- Guard both `onSettled` callbacks in
`archiveAndDeleteMutation.mutate()` with `shouldNavigateAfterArchive()`,
which checks whether the user is still viewing the archived chat (or a
sub-agent of it) before calling `navigate("/agents")`
- Extract `shouldNavigateAfterArchive` into `agentWorkspaceUtils.ts`
with 6 unit test cases covering: direct match, different chat, no active
chat, sub-agent of archived parent, sub-agent of different parent, and
cache-cleared fallback
- Look up the active chat's `root_chat_id` from the per-chat query cache
(stable across WebSocket eviction of sub-agents) to handle the sub-agent
case

> 🤖 This PR was created with the help of Coder Agents, and has been
reviewed by my human. 🧑‍💻
2026-03-23 11:28:06 +00:00
Cian Johnston ff8dcca2c7 feat: add global chat workspace TTL setting (#23265)
- Add `agents_workspace_ttl` site config (default: whatever the template
says a.k.a. `0s`)
- Expose via GET/PUT `/api/experimental/chats/config/workspace-ttl`
- Chat tool reads setting and passes `TTLMillis` on workspace creation
- Existing autostop infrastructure handles the rest (zero changes to
LifecycleExecutor, CalculateAutostop, or activity bumping)
- ⚠️ Template-level `UserAutostopEnabled=false` overrides this global
default. Not touching this.
- Frontend: "Workspace Lifetime" control in /agents/settings Behavior
tab (admin-only)

> This PR was created with the help of Coder Agents, and has been
reviewed by several humans and robots. 🤖🤝🧑‍💻
2026-03-20 17:38:39 +00:00
Cian Johnston f1d333f0e6 refactor: deduplicate utility helpers across the codebase (#23338)
Audited exported helpers in `coderd/util/*`, `testutil`, `cryptorand`,
and friends, then replaced duplicated implementations with canonical
versions.

- **fix: `maps.SortedKeys` generic signature** — value type was
hardcoded to `any`, making it impossible to actually call. Added second
type parameter `V any`. Added table-driven tests with `cmp.Diff`.
- **refactor: replace ad-hoc ptr helpers with `ptr.Ref`** — removed
`int64Ptr`, `stringPtr`, `boolPtr`, `i64ptr`, `strPtr`, `PtrInt32`
across 6 files.
- **refactor: replace local `sortedKeys`/`sortKeys` with
`maps.SortedKeys`** — now that the signature is fixed, scripts can use
it.
- **refactor: replace hand-rolled `capitalize` with
`strings.Capitalize`** — the typegen version was also not UTF-8 safe.

> 🤖 This PR was created with the help of Coder Agents, and was reviewed
by my human. 🧑‍💻
2026-03-20 15:12:41 +00:00
Cian Johnston 4c9041b270 chore: evict trivy from the dogfood Dockerfile (#23367)
- Remove `TRIVY_VERSION` ARG and trivy CLI install block from
`dogfood/coder/Dockerfile`
- The `trivy` job in `.github/workflows/security.yaml` is kept — it uses
`aquasecurity/trivy-action` pinned to a known-good commit

> 🤖 This PR was created with the help of Coder Agents, and was reviewed
by my human. 🧑‍💻
2026-03-20 14:52:11 +00:00
Cian JohnstonandCopilot c8e58575e0 chore: attempt to nudge agents away from dbauthz.AsSystemRestricted (#23326)
Adds a warning comment to dbauthz.AsSystemRestricted to hopefully nudge agents away from it.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-20 14:18:18 +02:00
Cian Johnston d8cad81ada fix(coderd/chatd): rate-limit stream drop WARN logs to avoid log spam (#23340)
- Rate-limit "chat stream buffer full" and "dropping chat stream event"
  WARN logs to at most once per 10s per chat.
- Intermediate drops not logged; WARN includes `dropped_count`.
- Per-chat tracking on `chatStreamState` using timestamp comparison
  against `quartz.Clock` — no global tickers, no new `Server` fields.
- Subscriber and buffer drop counters reset at all lifecycle boundaries.

> 🤖 This PR was created with the help of Coder Agents, and was reviewed
by my human. 🧑‍💻
2026-03-20 12:16:39 +00:00
Cian Johnston 06c50d13ad fix(cli): exorcise the DERP healthcheck demon from TestSupportBundle (#23337)
- Replace real healthcheck with mock `HealthcheckFunc` that returns a
canned report instantly
- Remove healthcheck cache-seeding goroutine/channel workaround
- Remove `HealthcheckTimeout: testutil.WaitSuperLong` (no longer needed)
- Reduce `setupCtx` from `WaitSuperLong` (60s) to `WaitLong` (25s)

The DERP healthcheck performs real network operations (portmapper
gateway probing, STUN) that hang for 60s+ on macOS CI runners. Since
`TestSupportBundle` validates bundle generation, not healthcheck
correctness, a canned report eliminates this entire class of flake.

Fixes coder/internal#272

> 🤖 This PR was created with the help of Coder Agents, and was reviewed
by my human. 🧑‍💻
2026-03-20 09:46:13 +00:00
Cian Johnston 2f50e89afd fix(coderd): bump workspace autostop deadline on chat heartbeat (#23314)
- Wire `workspacestats.ActivityBumpWorkspace` into `trackWorkspaceUsage`
so the workspace build deadline is extended each time the chat heartbeat
fires
- Prevents mid-conversation autostop for chat workspaces
- Updates `TestHeartbeatBumpsWorkspaceUsage` verifying the deadline bump

> This PR was created with the help of Coder Agents, and was reviewed by two humans and their pet robots 🧑‍💻🤝🤖
2026-03-19 22:07:20 +00:00
Cian Johnston 7c3c7bb5e6 refactor: extract test helpers in coderd/jobreaper to reduce duplication (#23001) 2026-03-19 21:51:26 +00:00
Cian Johnston a908d51097 fix(site): prevent scroll overshoot in diff viewer end spacer (#23305)
- Replace fixed `100vh` spacer at bottom of diff list with a dynamically
sized one
- Adds "X files changed" message to the bottom of the diff

> This PR was created with the help of Coder Agents, and was reviewed by several humans and robots. 🧑‍💻🤝🤖
2026-03-19 18:39:39 +00:00
Cian Johnston c107d2bf5d feat: add confirmation dialog to archive & delete workspace action (#23150)
* Adds a "molly-guard" to require users to type the workspace name
before the 'Archive & delete workspace' action fires. This prevents
accidental deletion of 'pet' workspaces.
* This is only shown for workspaces created *before* the chat was
created. The logic here is that any workspace that existed previous to
the chat *cannot* have been created by the chat.
2026-03-19 15:22:55 +00:00
Cian Johnston be1c06dec9 feat: add endpoint and CLI for users to view their own OIDC claims (#23053)
- Adds a new API endpoint `GET /api/v2/users/oidc-claims` that returns
only the **merged claims** (not the separate id_token/userinfo
breakdown). Scoped exclusively to the authenticated user's own identity
— no user parameter, so users cannot view each other's claims.
- Adds a new CLI command:** `coder users oidc-claims` that hits the
above endpoint.
- The existing owner-only debug endpoint is preserved unchanged for
admins who need the full claim breakdown.


> 🤖 This PR was created with the help of Coder Agents, and will be
reviewed by my human. 🧑‍💻
2026-03-18 22:10:04 +00:00
Cian Johnston 14ed3e3644 feat: bump workspace last_used_at on chat heartbeat (#23205)
- coderd: Wires `options.WorkspaceUsageTracker` into the chatd config.
- chatd: Adds `UsageTracker` and calls `UsageTracker.Add(workspaceID)`
on each heartbeat tick
- chatd: adds tests to verify `last_used_at` bump behaviour

> 🤖 This PR was created with the help of Coder Agents, and will be
reviewed by my human. 🧑‍💻
2026-03-18 19:07:21 +00:00
Cian Johnston 0b13ba978a fix: rename chat logger from coderd.chats.chat-processor to coderd.chatd.processor (#23246)
- Rename logger `coderd.chats` to `coderd.chatd` in `coderd.go`
- Rename sub-logger `chat-processor` to `processor` in `chatd/chatd.go`
2026-03-18 17:48:47 +00:00
Cian JohnstonandCopilot Autofix powered by AI 65b7658568 chore: extract testutil.FakeSink for slog test assertions (#23208)
Follow-up to [review comment on
#23025](https://github.com/coder/coder/pull/23025#discussion_r2930309487)
from @mafredri.

Extracts the repeated `logSink` / `fakeSink` test pattern into a shared
`testutil.FakeSink` and migrates all existing call sites.

> 🤖 This PR was created with the help of Coder Agents, and will be
reviewed by my human. 🧑‍💻

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-03-18 17:02:38 +00:00
Cian Johnston fe82d0aeb9 fix: allow member users to generate support bundles (#23040)
Fixes AIGOV-141

The `coder support bundle` command previously required admin permissions
(`Read DeploymentConfig`) and would abort entirely for non-admin
`member` users with:

```
failed authorization check: cannot Read DeploymentValues
```

This change makes the command **degrade gracefully** instead of failing
outright.

<details>
<summary>
Changes
</summary>

### `support/support.go`
- **`Run()`**: The authorization check for `Read DeploymentValues` is
now a soft warning instead of a hard gate. Unauthenticated users (401)
still fail, but authenticated users with insufficient permissions
proceed with reduced data.
- **`DeploymentInfo()`**: `DeploymentConfig` and `DebugHealth` fetches
now handle 403/401 responses gracefully, matching the existing pattern
used by `DeploymentStats`, `Entitlements`, and `HealthSettings`.
- **`NetworkInfo()`**: Coordinator debug and tailnet debug fetches now
check response status codes for 403/401 before reading the body.

### `cli/support.go`
- **`summarizeBundle()`**: No longer returns early when `Config` or
`HealthReport` is nil. Instead prints warnings and continues summarizing
available data (e.g., netcheck).

### Tests
- `MissingPrivilege` → `MemberNoWorkspace`: Asserts member users can
generate a bundle successfully with degraded admin-only data.
- `NoPrivilege` → `MemberCanGenerateBundle`: Asserts the CLI produces a
valid zip bundle for member users.
- All existing tests continue to pass (`NoAuth`, `OK`, `OK_NoWorkspace`,
`DontPanic`, etc.).

## Behavior matrix

| User type | Before | After |
|---|---|---|
| **Admin** | Full bundle | Full bundle (no change) |
| **Member** | Hard error | Bundle with degraded admin-only data |
| **Unauthenticated** | Hard error | Hard error (no change) |

Related to PRODUCT-182
2026-03-18 13:43:10 +00:00
Cian Johnston f8dff3f758 fix: improve push notification message shown on subscribe (#23052)
Updates push notification message for test notification.
2026-03-16 14:52:31 +00:00
Cian Johnston ae8bed4d8e feat(site): improve DERP health page readability (#22984)
## Why

The DERP health page displayed raw field names like
`MappingVariesByDestIP`, `PMP`, `PCP`, `HairPinning` with no context.
Users without deep networking knowledge had no way to understand what
these flags meant or why they mattered. This change makes the page
self-documenting.

## What

- DERPPage (`/health/derp`)
- Replace flat pill row with four logically grouped tables:
**Connectivity**, **IPv6 Support**, **NAT Traversal**, **Port Mapping**.
  - Rename section from "Flags" to "Network Checks".
- Surface `CaptivePortal` flag (previously missing from the UI
entirely).
- Invert display of `MappingVariesByDestIP` and `CaptivePortal` so green
always means good.
- Handle `null` boolean fields (e.g. UPnP, PMP, PCP) with a distinct
"not checked" neutral icon.

- DERPRegionPage (`/health/derp/regions/:regionId`)
- Replace per-node `BooleanPill` row with a table showing **Exchange
Messages**, **Direct HTTP Upgrade**, **STUN Enabled**, and **STUN
Reachable** per node.
- Invert `uses_websocket` display as "Direct HTTP Upgrade" (green when
websocket is not needed).
- Surface **STUN Enabled** and **STUN Reachable** per node (data was
returned by the API but never rendered).
- Add null guards for `region` and `node` (remove `!` non-null
assertions).
- Convert all emotion/MUI styles to Tailwind classes; remove
`reportStyles` object and `useTheme` import.

- Content.tsx (shared)
- Adds an exported `StatusIcon` component with three states: `true`
(green check), `false` (red minus), `null` (neutral help icon).
2026-03-16 09:14:24 +00:00
Cian Johnston 03d0fc4f4c fix(coderd): strip markdown code fences from Anthropic task name responses (#23024)
- Adds `extractJSON()` to strip markdown code fences before JSON parsing and wire into the `json.Unmarshal` call in `generateFromAnthropic`.
- Accepts variadic `RequestOption` in `generateFromAnthropic` so tests can inject a mock Anthropic server via `WithBaseURL`.
- Adds table-driven cases covering bare JSON, fenced with/without language tag, surrounding whitespace, and multiline JSON.
- Adds end-to-end cases using `httptest.NewServer` to serve fake Anthropic SSE streams with bare and fenced responses.
2026-03-13 17:35:26 +00:00
Cian JohnstonandClaude Opus 4.6 6104a000d1 refactor(healthcheck): reduce test boilerplate with healthyChecker helper (#23028)
## Summary

Extract a `healthyChecker()` test helper that returns an all-healthy
baseline `testChecker` in `coderd/healthcheck`. Each `TestHealthcheck`
table-driven test case now only overrides the single report field being
tested, instead of repeating all 6 healthy report structs.

- Reduces `healthcheck_test.go` from 603 to 341 lines (~260 lines, 43%
reduction)
- Test coverage unchanged at 77.2%
- All test cases and assertions preserved exactly

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

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 10:40:57 +00:00
Cian Johnston 8714aa4637 fix(coderd): downgrade heartbeat ping errors for closed connections to debug (#23025)
- `coderd/httpapi/websocket.go`: add `net.ErrClosed` +
`websocket.CloseStatus` checks; extract `heartbeatCloseWith` with
`quartz.Clock` parameter for testability
- `coderd/httpapi/websocket_internal_test.go`: new test file
2026-03-13 10:38:39 +00:00
Cian Johnston e9025f91e8 chore(db): remove 23 unused database methods (#22999)
Removes 22 database query methods with no callers outside generated code
and the dbauthz wrapper layer (~1,600 lines).

**Security keys (6)** — superseded by `cryptokeys` package:
`GetAppSecurityKey`, `UpsertAppSecurityKey`, `GetOAuthSigningKey`,
`UpsertOAuthSigningKey`, `GetCoordinatorResumeTokenSigningKey`,
`UpsertCoordinatorResumeTokenSigningKey`

**Superseded queries (4):**
- `GetProvisionerJobsByIDs` → `GetProvisionerJobsByIDsWithQueuePosition`
- `GetDeploymentDAUs` / `GetTemplateDAUs` →
`GetTemplateInsightsByInterval`
- `GetWorkspaceBuildParametersByBuildIDs` + its `GetAuthorized...`
variant → unused

**OAuth2 (2):**
`GetOAuth2ProviderAppByRegistrationToken`,
`UpdateOAuth2ProviderAppSecretByID`

**Chat (4)** — pre-wired with no callers:
`GetChatModelConfigByProviderAndModel`, `DeleteChatMessagesByChatID`,
`ListChatsByRootID`, `ListChildChatsByParentID`

**Other (6):**
`DeleteGitSSHKey`, `UpdateUserLinkedID`, `GetFileIDByTemplateVersionID`,
`GetTemplateVersionHasAITask`, `InsertUserGroupsByName`,
`RemoveUserFromAllGroups`
2026-03-12 21:32:57 +00:00
Cian Johnston 2d7dd73106 chore(httpapi): do not log context.Canceled as error (#22933)
A cursory glance at Grafana for error-level logs showed that the
following log line was appearing regularly:

```
2026-03-11 05:17:59.169 [erro]  coderd: failed to heartbeat ping  trace=xxx  span=xxx  request_id=xxx ...
    error= failed to ping:
               github.com/coder/coder/v2/coderd/httpapi.pingWithTimeout
                   /home/runner/work/coder/coder/coderd/httpapi/websocket.go:46
             - failed to ping: failed to wait for pong: context canceled
```

This seems to be an "expected" error when the parent context is canceled
so doesn't make sense to log at level ERROR.


NOTE: I also saw this a bit and wonder if it also deserves similar
treatment:

```
2026-03-11 05:10:53.229 [erro]  coderd.inbox_notifications_watcher: failed to heartbeat ping  trace=xxx  span=xxx  request_id=xxx ...
    error= failed to ping:
               github.com/coder/coder/v2/coderd/httpapi.pingWithTimeout
                   /home/runner/work/coder/coder/coderd/httpapi/websocket.go:46
             - failed to ping: failed to write control frame opPing: use of closed network connection
```
2026-03-11 09:48:07 +00:00
Cian Johnston bc27274aba feat(coderd): refactors github pr sync functionality (#22715)
- Adds `_API_BASE_URL` to `CODER_EXTERNAL_AUTH_CONFIG_`
- Extracts and refactors existing GitHub PR sync logic to new packages
`coderd/gitsync` and `coderd/externalauth/gitprovider`
- Associated wiring and tests

Created using Opus 4.6
2026-03-10 18:46:01 +00:00
Cian Johnston 4c63ed7602 fix(workspaceapps): use fresh context in LastUsedAt assertions (#22863)
## Summary

The `assertWorkspaceLastUsedAtUpdated` and
`assertWorkspaceLastUsedAtNotUpdated` test helpers previously accepted a
`context.Context`, which callers shared with preceding HTTP requests. In
`ProxyError` tests the request targets a fake unreachable app
(`http://127.1.0.1:396`), and the reverse-proxy connection timeout can
consume most of the context budget — especially on Windows — leaving too
little time for the `testutil.Eventually` polling loop and causing
flakes.

## Changes

Replace the `context.Context` parameter with a `time.Duration` so each
assertion creates its own fresh context internally. This:

- Makes the timeout budget explicit at every call site
- Structurally prevents shared-context starvation
- Fixes the class of flake, not just the two known-failing subtests

All 34 active call sites updated to pass `testutil.WaitLong`.

Fixes coder/internal#1385
2026-03-10 16:53:28 +00:00
Cian Johnston 12bdbc693f docs: remove experimental chat API from generated docs (#22897)
The chat API is experimental (behind `ExperimentAgents`) and not ready
for public documentation yet. This removes swagger annotations from the
chat handlers so they no longer appear in the generated API reference at
https://coder.com/docs/reference/api/chats.

## Changes
- Remove `@swagger` annotations from 5 chat handlers in
`coderd/chats.go`
- Regenerate `coderd/apidoc/swagger.json` and `docs.go`
- Delete `docs/reference/api/chats.md`
- Remove Chats entry from `docs/manifest.json`
2026-03-10 15:04:08 +00:00
Cian Johnston c933ddcffd fix(agents): persist system prompt server-side instead of localStorage (#22857)
## Problem

The Admin → Agents → System Prompt textarea saved only to the browser's
`localStorage`. The value was never sent to the backend, never stored in
the database, and never injected into chats. Entering text, clicking
Save, and refreshing the page showed no changes — the prompt was
effectively a no-op.

## Root Cause

Three disconnected layers:
1. **Frontend** wrote to `localStorage`, never called an API.
2. **`handleCreateChat`** never read `savedSystemPrompt`.
3. **Backend** hardcoded `chatd.DefaultSystemPrompt` on every chat
creation — no field in `CreateChatRequest` accepted a custom prompt.

## Changes

### Database
- Added `GetChatSystemPrompt` / `UpsertChatSystemPrompt` queries on the
existing `site_configs` table (no migration needed).

### API
- `GET /api/experimental/chats/system-prompt` — returns the configured
prompt (any authenticated user).
- `PUT /api/experimental/chats/system-prompt` — sets the prompt
(admin-only, `rbac: deployment_config update`).
- Input validation: max 32 KiB prompt length.

### Backend
- `resolvedChatSystemPrompt(ctx)` checks for a custom prompt in the DB,
falls back to `chatd.DefaultSystemPrompt` when empty/unset.
- Logs a warning on DB errors instead of silently swallowing them.
- Replaced the hardcoded `defaultChatSystemPrompt()` call in chat
creation.

### Frontend
- Replaced `localStorage` read/write with React Query
`useQuery`/`useMutation` backed by the new endpoints.
- Fixed `useEffect` draft sync to avoid clobbering in-progress user
edits on refetch.
- Added `try/catch` error handling on save (draft stays dirty for
retry).
- Save button disabled during mutation (`isSavingSystemPrompt`).
- Query key follows kebab-case convention (`chat-system-prompt`).

### UX
- Added hint: "When empty, the built-in default prompt is used."

### Tests
- `TestChatSystemPrompt`: GET returns empty when unset, admin can set,
non-admin gets 403.
- dbauthz `TestMethodTestSuite` coverage for both new querier methods.
2026-03-10 11:46:53 +00:00
Cian Johnston f07e266904 fix(coderd): use dbtime.Now() for tailnet telemetry timestamps (#22861)
Fixes a flaky test (`TestUserTailnetTelemetry/invalid_header`) caused by
sub-microsecond precision mismatch between `time.Now()` calls on
Windows.

The server used `time.Now()` (nanosecond precision) for `ConnectedAt`
and `DisconnectedAt`, while the test compared against its own
`time.Now()`. On Windows, wall-clock jitter can cause the server
timestamp to appear slightly before the test's `predialTime`.

Switch to `dbtime.Now()` which rounds to microsecond precision (matching
Postgres), consistent with all other timestamps in `workspaceagents.go`.

Relates to: https://github.com/coder/internal/issues/1390
2026-03-09 20:37:05 +00:00
Cian Johnston 5b7ba93cb2 fix(site): only use git watcher when workspace agent connected (#22714)
Adds a guard + some unit tests to ensure that we don't try to fetch git
changes if there's no workspace agent from which to do so.

Generated by Claude Opus 4.6 but read using Cian's eyeballs.
2026-03-09 08:53:00 +00:00
Cian Johnston 81468323e0 fix(coderd): use dbtime.Now() instead of time.Now() in test assertions against DB timestamps (#22685)
`time.Now()` has nanosecond precision while Postgres timestamps are
microsecond precision. When tests compare `time.Now()` against
DB-sourced timestamps using `Before`/`After`/`WithinRange`/etc., there
is a non-zero flake risk from the precision mismatch.

This replaces `time.Now()` with `dbtime.Now()` (which rounds to
microsecond precision) in all test assertions that compare against
database timestamps.

Follows from #22684.

## Changes (11 files)

| File | Changes |
|---|---|
| `coderd/apikey_test.go` | 11 comparisons with `ExpiresAt` |
| `coderd/users_test.go` | 2 comparisons with `ExpiresAt` |
| `coderd/oauth2_test.go` | 1 comparison with `token.Expiry` |
| `coderd/workspaces_test.go` | 2 comparisons with `DormantAt` |
| `coderd/workspaceagents_test.go` | 3 comparisons with
`ConnectedAt`/`DisconnectedAt` |
| `coderd/workspaceapps/db_test.go` | 1 comparison with `token.Expiry` |
| `coderd/provisionerdserver/provisionerdserver_test.go` | 1 comparison
with `key.ExpiresAt` |
| `enterprise/coderd/workspaces_test.go` | 1 comparison with `DormantAt`
|
| `enterprise/coderd/license/license_test.go` | 3 `NotBefore` values |
| `enterprise/coderd/licenses_test.go` | 2 `NotBefore` values |
| `enterprise/coderd/users_test.go` | 3 `Next()` comparisons |

## Not changed (intentionally)

- `scaletest/placebo/run_test.go` — compares wall-clock elapsed time,
not DB timestamps
- `cli/server_test.go`, `coderd/jwtutils/jwt_test.go`,
`enterprise/aibridgeproxyd/aibridgeproxyd_test.go` — TLS cert fields,
not DB-stored
- `coderd/azureidentity/azureidentity_test.go` — Azure cert expiry, not
DB


🤖 Generated by Claude Opus 4.6 but reviewed manually.
2026-03-06 09:14:11 +00:00
Cian Johnston 0b1e4880bd chore(cli): fix TestTokens harder (#22684)
`time.Now()` is greater than microsecond precision while timestamps we
store in Postgres are only microsecond precision. Flake potential is
non-zero.
2026-03-06 00:04:09 +00:00
Cian Johnston 89cee2dd81 chore(cli): fix flaky temporal assertion in TestTokens (#22654)
Fixes https://github.com/coder/internal/issues/1379
2026-03-05 10:18:51 +00:00
Cian JohnstonandAtif Ali 9c91f472b9 ci: remove temporary deploy override (#22389)
Merge after Tuesday March 3rd.

Co-authored-by: Atif Ali <atif@coder.com>
2026-03-05 09:47:15 +00:00
Cian Johnston d0a51e1752 fix: use testutil.Eventually in chatd interrupt test (#22653)
Follow-up to #22630. Addresses [review
feedback](https://github.com/coder/coder/pull/22630#pullrequestreview-2953419963)
that was missed due to auto-merge.

## Changes

Replaces three `require.Eventually` calls with `testutil.Eventually` in
`TestInterruptChatDoesNotSendWebPushNotification`, linking the condition
to the existing test context (`ctx`) created on line 1194. This ensures
the test respects context cancellation instead of using a standalone
timeout/tick pattern.
2026-03-05 09:42:34 +00:00
Cian Johnston 4d0d187806 fix(chatd): wait for startup scripts before returning from create_workspace (#22498)
The `create_workspace` tool waited for the workspace build to succeed
and the agent to become connectable, but did not wait for the agent's
startup scripts (e.g. git clone) to finish. This caused agents to
attempt file operations on repositories that hadn't been cloned yet.

Add a waitForStartupScripts step that polls the agent's lifecycle_state
via GetWorkspaceAgentLifecycleStateByID until it transitions out of
created/starting into a terminal state (ready, start_error, or
start_timeout). The tool now only returns success once the workspace is
fully initialized.

If the scripts fail or time out, the tool still returns (non-fatal) with
an appropriate agent_status so the model knows something went wrong.

Created using thingies (Opus 4.6 Max)
2026-03-05 09:42:12 +00:00
Cian Johnston 8a095ae722 fix(site): remove ugly webpush button (#22560) 2026-03-03 16:44:46 +00:00
Cian Johnston 88465ea48a chore: misc improvements to compose.dev.yaml (#22541)
- Unexposes port 5432 so it doesn't conflict with existing databases.
You can still hop into the DB if you need.
- Updates multiple CODER_ envs to sensible defaults, overrideable via env
2026-03-03 12:07:57 +00:00
Cian Johnston 3c4a416b55 feat(site): add Open Terminal and Copy SSH Command to agent chat TopBar (#22529)
Adds two new items to the agent chat TopBar dropdown menu:

- **Open Terminal**: opens the workspace web terminal in a new browser
window, reusing the existing `getTerminalHref`/`openAppInNewWindow`
infra.
- **Copy SSH Command**: copies the SSH command (e.g. `ssh
agent.workspace.owner.suffix`) to the clipboard with a toast
confirmation. Only shown when the deployment SSH hostname suffix is
configured.

Both items appear after a separator below the existing editor/workspace
actions.

## Changes

| File | What |
|---|---|
| `TopBar.tsx` | Added `Open Terminal` and `Copy SSH Command` dropdown
items with separator, `TerminalIcon`/`CopyIcon`, toast on copy |
| `AgentDetail.tsx` | Wired up `getTerminalHref`, `openAppInNewWindow`,
`deploymentSSHConfig` query, and passed new props to TopBar in all 3
render paths |
| `TopBar.stories.tsx` | Added new fields to default story props |
2026-03-03 11:44:39 +00:00
Cian Johnston 517cb0ce73 refactor(webpush): use RequireExperimentWithDevBypass middleware (#22525)
Replace manual experiment checks in web-push handlers with the
`RequireExperimentWithDevBypass` middleware on the route group, matching
the pattern used by OAuth2, Agents, and MCP experiments.

## Changes

- **`coderd/coderd.go`**: Add `RequireExperimentWithDevBypass`
middleware to `/webpush` route group
- **`coderd/webpush.go`**: Remove inline
`api.Experiments.Enabled(codersdk.ExperimentWebPush)` checks from all
three handlers
- **`cli/server.go`**: Gate webpush dispatcher initialization with
`buildinfo.IsDev()` fallback so dev builds always init the real
dispatcher
- **`coderd/webpush_test.go`**: Remove experiment enablement from tests
(dev bypass handles it)

Net effect: -26 lines removed, +5 added.

Created using whatchamacallits (Opus 4.6 Max)
2026-03-03 09:49:04 +00:00