Previously, the workspace "Download logs" dialog formatted the original byte
count with the promoted unit, so sizes above 1 KiB could be shown incorrectly,
for example `4472 KB` instead of `4.37 KB`. Exact 1024-byte files also stayed
in bytes.
When the control plane connection drops and reconnects, a new
`tunnelUpdater` is created with empty workspace state. This causes the
in-memory DNS resolver to lose all host records, breaking `.coder` name
resolution until the server sends a fresh workspace snapshot.
If the API is unreachable (e.g., the route goes through a VPN that is
also reconnecting), the snapshot never arrives and DNS stays broken
indefinitely — requiring a full Coder Desktop restart.
Fix: carry workspace state from the previous `tunnelUpdater` to the new
one on reconnect, and immediately re-apply DNS hosts so the resolver
stays populated during the reconnection window.
Fixes https://linear.app/codercom/issue/PLAT-110
<details><summary>Investigation & decision log</summary>
### Root cause analysis
Customer diagnostic data from Roblox (March 31) showed:
- NRPT rule present (`.coder` → `fd60:627a:a42b::53`) — routing is
correct
- DNS resolver returns NXDOMAIN for everything including the sentinel
`is.coder--connect--enabled--right--now.coder` — resolver is running but
has zero host records
- Coder Connect UI shows "connected" — the WireGuard data plane is up
The resolver is empty because
`TunnelAllWorkspaceUpdatesController.New()` creates a fresh
`tunnelUpdater` with `workspaces: make(map[uuid.UUID]*Workspace)`
(empty). The previous updater's workspace data is discarded. If the
server's workspace snapshot is delayed or the API is unreachable, the
resolver has no records to serve.
This is compounded by GlobalProtect VPN reconnects: the Coder API is
behind the VPN, so when GP reconnects, the API route is temporarily lost
and the snapshot can't arrive.
### What this PR changes
- `TunnelAllWorkspaceUpdatesController.New()` now clones workspace state
from the previous updater before creating the new one
- Immediately re-applies DNS hosts with the inherited state (log:
`re-applying DNS hosts from previous session`)
- When the server's snapshot arrives, it replaces the inherited data
normally
- If `SetDNSHosts` fails during re-apply, it's logged as a warning and
not fatal — the recvLoop will program DNS when the snapshot arrives
### What this PR does NOT fix (future work)
- **Tunnel binary restart**: when the tunnel process itself is killed
and relaunched, all in-memory state is lost. A DNS host cache on disk
would be needed for this case.
- **NRPT rule cleanup on startup**: the Tailscale fork's
`nrptRuleDatabase` constructor unconditionally deletes all NRPT rules on
engine creation. Deferring cleanup to the first successful `SetDNS` call
would reduce the DNS gap.
- **Hosts file retry**: the `setHosts()` retry in the Tailscale fork
(5×10ms) is too short for environments where endpoint security locks the
file.
These are tracked as follow-up items in the `coder/tailscale` fork.
</details>
> 🤖 Generated by Coder Agents
> AI tools where used when creating this PR
This PR removes environment variable parsing from `/aibridge` directory.
Added env variables/flags for dump dir as coder options.
Only added to new indexed provider options
(`CODER_AIBRIDGE_PROVIDER_<N>_*`) not to deprecated legacy env variables
(`CODER_AIBRIDGE_ANTHROPIC_*` and `CODER_AIBRIDGE_OPENAI_KEY_*`).
Reverted adding `MaxRetries` option as it will be removed soon due to
key failover work:
https://github.com/coder/coder/pull/24783#discussion_r3155544808
> AI tools where used when creating this PR
This PR:
* removes references to aibridge repository from coder docs
* updates aibdrige/README.md
* makes it clear aibridge (keeping old name) is a handler not a separate
process
* updates outdated sections about: metrics, recorded interface and
supported paths.
---------
Co-authored-by: Susana Ferreira <susana@coder.com>
Adds a role selector to the create user form so admins can assign
site-level roles at creation time rather than navigating to the user
afterward.
The `POST /api/v2/users` endpoint now accepts an optional `roles` field,
wiring it through to the existing `RBACRoles` field on the internal
`CreateUserRequest`. No database changes are needed since roles are
already stored inline on the user row.
On the frontend, a `RoleSelector` component renders the assignable roles
as a scrollable multiselect checklist with the non-assignable Member
role pinned as a non-interactive footer. The selector appears once a
login type is chosen.
Also adds a `condensed` size (690px) to `Margins` between the existing
`small` (460px) and `medium` (1080px), and exposes a `size` prop on
`FullPageForm`. The create user form uses `condensed` to give the role
selector more breathing room. Also fixes `MockUserAdminRole` and
`MockTemplateAdminRole` in test helpers to use hyphenated names
(`user-admin`, `template-admin`) matching the canonical names in the Go
RBAC layer.
Fixes `sortRolesByAccessLevel` in `UserRoleCell` to sort unranked roles
(e.g. `member`) after all known roles. Previously, `indexOf` returned -1
for unknown names, placing them first; now they receive
`POSITIVE_INFINITY` as their rank.
🤖 Generated with [Claude Code](<https://claude.ai/claude-code>)
---
https://github.com/user-attachments/assets/75e7c8c5-d0d2-481d-86e8-1fcfb574517c
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
> AI tools where used when creating this PR
Updates `AGENTS.md` to reflect that `/aibridge` is now a directory in
coder/coder repo not a separate repo.
This pull request updates the `CreateTemplateGalleryPageView` component
to enhance the page header actions by grouping them vertically and
adding a new external link button for users. The most important changes
are:
**UI improvements:**
* Groups the header action buttons into a vertical stack using a flex
container with column direction and spacing for better layout and
accessibility.
**New feature:**
* Adds a new button linking to the "template agent skill" documentation
on GitHub, allowing users to easily access guidance on using the
template agent skill.
**Screenshot**
<img width="1674" height="813" alt="image"
src="https://github.com/user-attachments/assets/8d12df13-2310-464b-936c-1b4e2215c7aa"
/>
Follow-up to #24742. Navigation paths were dropping `?archived` search
params, silently resetting the filter now that it's URL-derived.
Fixed all sibling navigation that used bare `/agents` paths or stored
only `location.pathname` without `location.search`:
- **ChatTopBar** mobile back button (`md:hidden`)
- **ChatTopBar** parent chat breadcrumb
- **AgentsSidebar** settings gear link (stored `state.from` without
search)
- **AgentPageHeader** mobile menu settings link (same `state.from` bug)
- **AgentAnalyticsPage** mobile back button (widened `mobileBack.to`
type to accept `To`)
- **SubagentTool** "View agent" external link
- **AgentsPage** `navigateAfterArchive` and `handleNewAgent` navigate
calls
Two Storybook play stories cover the ChatTopBar back button and the
sidebar settings round-trip.
_Generated by Coder Agents._
Document the fuzzy matching behavior, error-on-ambiguity semantics,
and atomic batch validation that were missing from the tool
description. These are the three behaviors most likely to surprise
callers who assume naive exact-match search/replace.
When tryAutoPromoteQueuedMessage's insert fails, return the error
instead of swallowing it so the transaction rolls back and the
queued message survives. Previously the POP DELETE committed while
the INSERT silently failed, permanently losing the message.
Remove clearStreamState() from the pending/waiting status handler
in the frontend. The durable message event clears stream state via
the existing needsStreamReset path, eliminating the visual gap
where content vanishes before the persisted message arrives.
Fixes CODAGT-61
chatd.New() no longer auto-starts the acquire/wake loop.
Callers that want chat processing call server.Start()
explicitly. Tests that want a passive server skip Start();
heartbeat, stream janitor, and stale recovery still run.
Closescoder/internal#1502
> Mux working on behalf of Mike.
Debug recording could consume request bodies when a provider SDK
returned the active body from `GetBody`, which left the upstream request
with an empty body after capture.
Reset the request body after debug capture and add coverage for shared
`GetBody` readers so debug logging does not alter the bytes sent
upstream.
Tighten visual rhythm and typography in the agent chat page so tool
calls, reasoning, and assistant text share the same baseline.
## Highlights
- Unify font size to **13px** across user messages, assistant
`Response`, reasoning, and every tool-call label.
- Reuse the `text-content-secondary → hover:text-content-primary`
transition on tool-call rows so labels, chevrons, and lucide icons
brighten together. Icons inside hover-aware headers switch to
`text-current` so they inherit the parent transition; static icons in
non-collapsible cards (`ExecuteTool`, `ProcessOutputTool`) keep the
constant secondary color.
- Collapse padding between adjacent tool/thinking blocks via a shared
`data-tool-call` attribute and adjacent-sibling selectors
(`[&:has(+[data-tool-call])]:pb-0` + `[[data-tool-call]+&]:pt-0`).
First/last items keep their padding against text and reasoning siblings.
- `read_file` now mirrors `write_file`: `Reading <name>…` while running,
`Read <name>` once complete.
- `ask_user_question` flips the inline label from `Asking:` to `Asked:`
once answered.
- Subagent row layout: status icon + label + chevron sit together at the
start of the row, while the `Worked for <duration>` text uses `ml-auto`
to anchor the right edge.
- New Storybook story `WithEveryTool` (under `Pages / AgentsPage /
AgentChatPage`) exercises every tool renderer plus subagent variants and
the generic MCP fallback in a single completed-then-streaming turn.
---
_Authored with help from a Coder Agent._
The MCP manager previously read .mcp.json exactly once at agent startup.
Editing the file had no effect until workspace rebuild or agent restart.
handleListTools now stats config file mtimes on every tool-list request
and triggers a differential reload when any file changed. Unchanged
servers keep their client pointer so in-flight tool calls survive.
Concurrent reload requests coalesce via singleflight.
MCP stdio subprocesses use the agent's execer for resource limits and
receive the same enriched environment as SSH sessions via updateEnv.
On the chatd side, WorkspaceMCPTool.Run detects 404 responses from
CallMCPTool (indicating the server was removed) and drops the chat's
cached tool list so the next turn refetches from the agent.
ValidateToken treated all 403 responses as "token invalid," including
GitHub rate limits. isFailedRefresh included 403 in the status code
fallthrough, destroying tokens on rate-limited refresh attempts.
Split the combined 401/403 check in ValidateToken into a switch on
status code. On 403, inspect X-RateLimit-Remaining and Retry-After
headers; if either indicates a rate limit, return optimistically valid.
Handle 429 the same way. Plain 403 without rate-limit headers preserves
the existing invalid-token behavior.
Add incorrect_client_credentials and invalid_client to isFailedRefresh
error code switch. Remove 403 from the status code fallthrough since no
known provider returns 403 from the token endpoint.
The CODER_AGENT_EXP_* env vars are agent-internal options. When set
in the workspace environment they leak to MCP subprocesses and user
shells.
ReadEnvConfig() captures the values and ClearEnvVars() strips them
before the reinit loop, so config survives agent restarts. NewAPI
and ReadEnvConfig both use applyDefaults() to fill zero fields.
The chatd test passes config via agenttest.WithContextConfigFromEnv().
When `StopAfterTool` fires (e.g., `propose_plan`), the LLM response
containing a `function_call` is stored at OpenAI via `store=true`, but
the tool result is only persisted locally. On the next user message,
`resolveChainMode` sees the tool result in the local DB and concludes
all calls are resolved. Chain mode activates with
`previous_response_id`, but OpenAI rejects because its stored chain has
an unresolved `function_call`.
This adds a `providerMissingToolResults` check to `resolveChainMode`
that detects the `assistant(tool-call) → tool(result) → user` pattern
with no follow-up assistant message. The absence of a follow-up
assistant proves the tool results were never round-tripped to the
provider. When detected, chain mode is blocked and the system falls back
to full history replay, which includes both the tool call and its
result.
Deploying this fix un-bricks existing affected chats with no DB
migration needed.
> Generated by Coder Agents.
> Mux is working on behalf of Mike.
## Summary
- Bump `github.com/coder/anthropic-sdk-go` to the corrected Bedrock
streaming header fix from coder/anthropic-sdk-go#14.
- Match botocore's `InvokeModelWithResponseStream` request shape by
using `X-Amzn-Bedrock-Accept` and omitting the HTTP `Accept` header.
- Update chatd regression coverage for the corrected header shape.
## Context
The previous fix set `Accept: application/vnd.amazon.eventstream`. Real
boto3/botocore streaming requests do not send that header. They send
`X-Amzn-Bedrock-Accept: application/json`, which is the modeled Bedrock
request header for the desired model response MIME type.
## Validation
- `go test ./coderd/x/chatd/chatprovider -run
'TestModelFromConfig_Bedrock(StreamingHeaders|StripsAnthropicHeaders)?$'
-count=1`
- `go mod tidy -diff`
- `git diff --check`
- pre-commit hook during `git commit`
> Mux is working on behalf of Mike.
## Summary
- Bump `github.com/coder/anthropic-sdk-go` to the clean Bedrock
streaming header fix from coder/anthropic-sdk-go#10.
- Add chatd regression coverage that verifies Bedrock streaming requests
use AWS event stream headers and include `X-Amzn-Bedrock-Accept` in the
SigV4 signed headers.
## SDK follow-up
- Reverted the bad coder/anthropic-sdk-go#8 merge with
coder/anthropic-sdk-go#9.
- Re-applied only the intended Bedrock streaming header change in
coder/anthropic-sdk-go#10.
## Validation
- `go test ./coderd/x/chatd/chatprovider -run
'TestModelFromConfig_Bedrock(StreamingHeaders|StripsAnthropicHeaders)?$'
-count=1`
- `go test ./coderd/x/chatd/chatprovider -count=1`
- `go mod tidy -diff`
- `make lint`
- pre-commit hook during `git commit`
## Problem
Anthropic returns HTTP 400 when an assistant message contains a
`web_search_tool_result` block whose `tool_use_id` has no matching
earlier `server_tool_use` block in the same assistant message. A
previous fix (#24706) sanitized provider-executed tool calls without
matching results, but the opposite direction, orphaned or misordered
provider-executed results, could still slip through both the prompt
sanitizer and the persistence path.
## Fix
Tighten Anthropic provider-executed tool history handling while
preserving the useful result payload as normal assistant text when the
provider-tool metadata is unsafe.
1. Extract Anthropic provider-tool sanitization into
`coderd/x/chatd/chatsanitize` so provider-specific repair logic is no
longer spread through `chatprompt` and `chatloop`.
2. `chatsanitize.SanitizeAnthropicProviderToolHistory` removes invalid
provider-executed tool structure for Anthropic prompts: orphans in
either direction, result-before-call, duplicate IDs, invalid JSON
inputs, empty IDs and tool names, unsupported tool names, mismatched
`ProviderExecuted` flags, provider-executed blocks outside assistant
messages, and web-search results without serializable Anthropic result
metadata. Provider-executed result payloads are textified instead of
being discarded when there is text to preserve.
3. `chatsanitize.SanitizeAnthropicProviderToolContent` mirrors the same
rule at the streamed step content level. Persisted history no longer
carries invalid provider-tool blocks forward, but it keeps the result
text for future turns.
4. `chatsanitize.ApplyAnthropicProviderToolGuard` only repairs
structurally invalid Anthropic provider-tool history. It no longer
strips otherwise-valid historical `web_search` blocks just because web
search is disabled for the current request. The fail-closed fallback
also textifies provider results before removing provider-tool metadata.
Tests cover prompt sanitization, validation reason strings, result
payload textification, content-level persistence sanitization, disabled
web-search history preservation, direct pre-request guard behavior, and
the fallback strip path.
> Mux is acting on Mike's behalf.
Persists the agents page archived filter in the URL via
`?archived=archived`,
so deep-linking to archived agents and restoring the filter from history
work
as expected. Unknown values fall back to active. Toggling back to active
removes the param from the URL so the default state has one canonical
form.
Also fixes a regression that surfaced once the filter became
URL-derived:
clicking a chat in the sidebar previously navigated to `/agents/:id`
with no
search params, silently resetting the filter. The sidebar's chat
`NavLink`
now preserves `location.search`.
Coverage:
- `useArchivedFilterParam` is unit-tested with `renderHook`, covering
URL
parsing variants and the `deleteValue` semantics for the default state.
- Cross-route preservation is covered by a Storybook play story
(`PreservesArchivedFilterOnChatNavigation`) that renders the real
`NavLink`
and asserts on a probe child route.
- The pre-existing sidebar callback tests in `AgentsSidebar.test.tsx`
cover
wiring that exists on `main`; they're kept here for completeness rather
than as new feature gates.
_Generated by Coder Agent._
Follow-up to #24650.
Canceling the terminal command confirmation dialog now calls
`window.close()` instead of stripping the `?command=` query parameter
and opening a plain terminal. The terminal always opens in a new tab,
so closing it is the expected UX when the user declines.
> 🤖 Generated by Coder Agents
Depends on #24642
Adds per-owner digest notifications onto the chat auto-archive
subsystem.
Each tick's archived rows are grouped by owner, the top 25 titles per
owner are rendered into a new `Chats Auto-Archived` notification
template, and any remainder surfaces as `and N more`. Each digest is
per-tick, so users with large amounts of purgeable data may get multiple
notifications in sequence (one per user per tick).
The template body branches on `retention_days`: when retention is
disabled (`retention_days=0`), users are told archived chats are kept
indefinitely rather than falsely claiming imminent deletion.
### Changes
- migration `000XXX_chat_auto_archive_notification_template` adds new
notification template
- `dbpurge`: threads `notifications.Enqueuer` through `New`; and
enqueues notification message.
- `cli/server.go`: passes `options.NotificationsEnqueuer` into
`dbpurge.New`.
- `coderd/notifications/events.go`: new `TemplateChatAutoArchiveDigest`
UUID.
- `coderd/inboxnotifications.go`: inbox registration.
- Docs: adds a `Notifications` section to `chat-auto-archive.md`.
> 🤖
> Mux working on behalf of Mike.
## Summary
Clarifies that GitHub PR body prose should rely on soft wrapping instead
of manual fixed-width hard wraps. Updates the examples and key
principles to match that guidance.
## Validation
- `pnpm exec markdownlint-cli2 .claude/docs/PR_STYLE_GUIDE.md`
- `make lint/emdash`
- `make pre-commit-light`
The `--login-type none` option for `coder users create` is deprecated.
This adds deprecation warnings to all docs that reference it and updates
the CI/CD tutorial to recommend the replacement flows.
Refs DEVEX-224
<details>
<summary>Changes</summary>
- `cli/usercreate.go`: Append deprecation notice to `--login-type` flag
description.
- `docs/tutorials/testing-templates.md`: Replace `--login-type none`
example with separate Premium (`--service-account`) and OSS
(`--login-type password`) examples.
- `docs/reference/cli/users_create.md`: Regenerated from CLI source.
- `cli/testdata/coder_users_create_--help.golden`: Updated golden
snapshot.
</details>
> [!NOTE]
> Generated by Coder Agents.
Currently when a user clicks either the Cancel or Allow button on the
authorization page the client app URI is executed but the page does not
land to the main dashboard page, leaving the two buttons open for
multiple clicks from the user. Aside from the potential problems it
might cause by activating the callback URI multiple times, the page also
provides poor UX because users usually expect the authorization tab to
return to the dashboard.
The consent page now executes the OAuth2 callback (auth code on Allow,
`access_denied` on Cancel) and hides the two buttons and updates the
existing description with a user instruction to close the window.
Initial implementation relied on a pop-up window executing the callback
while the main window was redirected to the dashboard main page.
- resolves https://github.com/coder/coder/issues/20323
<!--
If you have used AI to produce some or all of this PR, please ensure you
have read our [AI Contribution
guidelines](https://coder.com/docs/about/contributing/AI_CONTRIBUTING)
before submitting.
-->
The agent chat thinking disclosure used a smaller label with the caret
on the left, which made collapsed and in-progress thinking look
different from collapsible tool calls.
Align the thinking disclosure with the shared tool-call row treatment by
using the same label size, trailing caret placement, and hover color
while preserving the streaming shimmer. Adds a Storybook story that
renders collapsed thinking next to a tool call.
<details>
<summary>Storybook screenshots</summary>
Captured from Storybook:
-
`pages/AgentsPage/ChatConversation/ConversationTimeline/ThinkingBlockWithToolCall`
-
`pages/AgentsPage/ChatConversation/ConversationTimeline/ThinkingBlockWithToolCall`
hovered
-
`pages/AgentsPage/ChatConversation/StreamingOutput/ThinkingDuringStreamingWithToolCalls`
Screenshots are attached in the Coder task.
</details>
Generated by Coder Agents.
The deprecation notice on the [MCP Tools
Injection](https://coder.com/docs/ai-coder/ai-gateway/mcp) page
currently states the feature "will be removed in a future release,"
which may cause concern for users relying on it today.
This updates the warning to clarify that the feature will remain
functional and will not be removed until its replacement, MCP Gateway,
is released.
> [!NOTE]
> Generated by Coder Agents
---------
Co-authored-by: david-fraley <67079030+david-fraley@users.noreply.github.com>
> Mux is acting on Mike's behalf.
Adds explicit star, edit, and duplicate actions to each Agents model
configuration row, replacing the chevron-only affordance.
Duplicate opens a prefilled create form backed by the existing create
mutation when the provider can manage models. The form copies editable
model fields and provider config while clearing default status so saving
a duplicate does not change the current default model.
Bumps coder/terraform-provider-coder to v2.16.0 to pick up the `coder_secret`
data source that enables expressing a required user secret in a template.
This change passes user secrets from coderd to the Terraform process at
workspace build time so the `data.coder_secret` data source in
terraform-provider-coder can resolve values at plan time.
Secrets traverse two proto hops: `provisionerdserver` fetches them
via`ListUserSecretsWithValues`, attaches them to
`AcquiredJob.WorkspaceBuild.user_secrets` on `provisionerd.proto`;
`runner.go` forwards into `PlanRequest.user_secrets` on
`provisioner.proto`; the Terraform provisioner encodes each as
`CODER_SECRET_ENV_<name>` or `CODER_SECRET_FILE_<hex(path)>` before
invoking `terraform plan`. Only plan requests carry secrets; apply runs
with `nil` because values are baked into plan state.
Fetch is gated on a workspace transitioning to start. stop and delete
transitions never carry secrets, so revoking or deleting a stored secret
cannot make a workspace unstoppable. DB errors on the fetch fail the job
outright rather than silently continuing with an empty secret set.
Note that user secrets will be stored in the workspace_builds table in
provisioner_state with other Terraform state (including other sensitive data).
The terminal page auto-executed commands from the `?command=` query
parameter
on page load without user confirmation. Because session auth uses
`SameSite=Lax`
cookies, an attacker could craft a link (phishing email, Slack DM,
external page)
that executes arbitrary commands in a victim's workspace when clicked.
Adds a `ConfirmDialog` that shows the exact command and requires
explicit user
approval before it is passed to the terminal WebSocket. Canceling
removes the
`command` parameter from the URL and opens a plain terminal.
<details>
<summary>Implementation details</summary>
### Data flow (before)
`TerminalPage.tsx` reads `searchParams.get("command")` and passes it
directly
as `initialCommand` to `WorkspaceTerminal`, which embeds it in the
WebSocket
URL. `proxy.go` forwards it to the agent, which runs `bash -c
"<command>"`
immediately.
### Fix
- Added `commandConfirmed` state and `commandPendingConfirmation` flag
in
`TerminalPage.tsx`.
- The `loading` prop passed to `WorkspaceTerminal` includes
`commandPendingConfirmation`, keeping the terminal in loading state
until
the user confirms or cancels.
- The command is only passed as `initialCommand` after the user clicks
"Run command" in the confirmation dialog.
- Trusted `?app=` commands (resolved from agent apps) bypass the dialog.
- Cancel removes the `?command=` parameter from the URL entirely.
- No backend changes needed; the frontend gates the command before it
reaches the WebSocket.
### Terminal focus after dialog
`WorkspaceTerminal`'s autoFocus effect previously depended on
`[terminal, isVisible, autoFocus]` but not `loading`. It fired while the
Radix dialog's focus trap was active, so `terminal.focus()` was
intercepted. When `loading` became false after confirming the dialog,
the
effect did not re-fire. Fixed by adding `loading` to the effect deps and
skipping focus while `loading` is true.
### Files changed
| File | Change |
|------|--------|
| `site/src/pages/TerminalPage/TerminalPage.tsx` | Confirmation dialog,
`commandPendingConfirmation` in loading prop |
| `site/src/pages/TerminalPage/TerminalCommandConsentDialog.tsx` | New
dialog component |
| `site/src/pages/TerminalPage/TerminalCommandConsentDialog.stories.tsx`
| Storybook story for dialog |
| `site/src/pages/TerminalPage/TerminalPage.stories.tsx` |
`CommandConfirmation` story |
| `site/src/pages/TerminalPage/TerminalPage.test.tsx` | 4 new dialog
tests, `renderTerminalRaw` helper for non-blocking render |
| `site/src/modules/terminal/WorkspaceTerminal.tsx` | Add `loading` to
autoFocus effect deps |
| `site/e2e/helpers.ts` | Dismiss dialog in `openTerminalWindow` helper
|
| `site/e2e/tests/webTerminal.spec.ts` | Wait for
`data-status="connected"` + click terminal for focus |
</details>
> 🤖 Generated by Coder Agents
---------
Co-authored-by: Jakub Domeracki <jakub@coder.com>
`namedWorkspace` in `cli/root.go` parsed workspace identifiers with
`uuid.Parse` first and returned immediately on success, even when no
workspace had that UUID as its actual ID. This caused 404 errors for any
workspace whose name was a valid 32-char hex string (dashless UUID).
- Add `codersdk.ResolveWorkspace`: tries UUID lookup first, falls back
to name lookup on 404. `NameValid` guard skips the fallback for standard
dashed UUIDs (36 chars > 32-char name limit).
- Export `codersdk.SplitWorkspaceIdentifier`, replacing the duplicate
`splitNamedWorkspace` in `cli/root.go` (uses `strings.Cut`).
- Delete `namedWorkspace` from `cli/root.go`; all 28 call sites now use
`client.ResolveWorkspace` directly.
- Delete `namedWorkspace` and `splitNameAndOwner` from
`codersdk/toolsdk/bash.go`; inline `client.ResolveWorkspace`.
- Simplify `GetWorkspace` tool handler to a single `ResolveWorkspace`
call.
- Unit tests via httptest mock cover UUID, name, owner/name, UUID-like
fallback, not-found, server error, transport error, and invalid
identifier paths.
- Integration tests in `cli/show_test.go` and `codersdk/toolsdk` for
workspaces with UUID-like names.
> Generated with Coder Agents