feat(chatd): add start_workspace tool to agent flow (#22646)

## Summary

When a chat's workspace is stopped, the LLM previously had no way to
start it — `create_workspace` would either create a duplicate workspace
or fail. This adds a dedicated `start_workspace` tool to the agent flow.

## Changes

### New: `start_workspace` tool
(`coderd/chatd/chattool/startworkspace.go`)
- Detects if the chat's workspace is stopped and starts it via a new
build with `transition=start`
- Reuses the existing `waitForBuild` and `waitForAgent` helpers (shared
logic)
- Shares the workspace mutex with `create_workspace` to prevent races
- Idempotent: returns immediately if the workspace is already running or
building
- Returns a `no_agent` / `not_ready` status if the agent isn't available
yet (non-fatal)

### Updated: `create_workspace` stopped-workspace hint
- `checkExistingWorkspace` now returns a `stopped` status with message
`"use start_workspace to start it"` when it detects the chat's workspace
is stopped, instead of falling through to create a new workspace

### Wiring
- `chatd.Config` / `chatd.Server`: new `StartWorkspace` /
`startWorkspaceFn` field
- `coderd/chats.go`: new `chatStartWorkspace` method that calls
`postWorkspaceBuildsInternal` with proper RBAC context
- `coderd/coderd.go`: passes `chatStartWorkspace` into chatd config
- Tool registered alongside `create_workspace` for root chats only (not
subagents)

### Tests (`startworkspace_test.go`)
- `NoWorkspace`: error when chat has no workspace
- `AlreadyRunning`: idempotent return for workspace with successful
start build
- `StoppedWorkspace`: verifies StartFn is called, build is waited on,
and success response returned
This commit is contained in:
Kyle Carberry
2026-03-05 15:34:24 +00:00
committed by GitHub
parent 26205b9888
commit 6520159045
7 changed files with 613 additions and 0 deletions
+11
View File
@@ -66,6 +66,7 @@ type Server struct {
agentConnFn AgentConnFunc
createWorkspaceFn chattool.CreateWorkspaceFn
startWorkspaceFn chattool.StartWorkspaceFn
pubsub pubsub.Pubsub
webpushDispatcher webpush.Dispatcher
providerAPIKeys chatprovider.ProviderAPIKeys
@@ -852,6 +853,7 @@ type Config struct {
InFlightChatStaleAfter time.Duration
AgentConn AgentConnFunc
CreateWorkspace chattool.CreateWorkspaceFn
StartWorkspace chattool.StartWorkspaceFn
Pubsub pubsub.Pubsub
ProviderAPIKeys chatprovider.ProviderAPIKeys
WebpushDispatcher webpush.Dispatcher
@@ -887,6 +889,7 @@ func New(cfg Config) *Server {
subscribeFn: cfg.SubscribeFn,
agentConnFn: cfg.AgentConn,
createWorkspaceFn: cfg.CreateWorkspace,
startWorkspaceFn: cfg.StartWorkspace,
pubsub: cfg.Pubsub,
webpushDispatcher: cfg.WebpushDispatcher,
providerAPIKeys: cfg.ProviderAPIKeys,
@@ -2210,6 +2213,14 @@ func (p *Server) runChat(
AgentConnFn: chattool.AgentConnFunc(p.agentConnFn),
WorkspaceMu: &workspaceMu,
}),
chattool.StartWorkspace(chattool.StartWorkspaceOptions{
DB: p.db,
OwnerID: chat.OwnerID,
ChatID: chat.ID,
StartFn: p.startWorkspaceFn,
AgentConnFn: chattool.AgentConnFunc(p.agentConnFn),
WorkspaceMu: &workspaceMu,
}),
)
tools = append(tools, p.subagentTools(func() database.Chat {
return chat