chore(db): remove workspace_agent_id from chats table (#22442)

## Summary

Remove the `workspace_agent_id` column from the `chats` table and
dynamically look up the first workspace agent instead.

## Problem

When a workspace is stopped and restarted, the workspace agent gets a
new ID. The `workspace_agent_id` stored on the chat at creation time
becomes stale, making the agent unreachable. This caused chats to break
after workspace restarts.

## Solution

Instead of persisting the agent ID, dynamically look up the first agent
from the workspace's latest build via
`GetWorkspaceAgentsInLatestBuildByWorkspaceID` whenever an agent
connection is needed. The `workspace_id` on the chat remains stable
across restarts.

This behavior may be refined later (e.g., agent selection heuristics),
but picking the first agent resolves the immediate breakage.

## Changes

- **Migration 000425**: Drop `workspace_agent_id` column from `chats`
- **SQL queries**: Remove `workspace_agent_id` from `InsertChat` and
`UpdateChatWorkspace`
- **chatd.go**: `getWorkspaceConn` and `resolveInstructions` now look up
agents dynamically from workspace ID
- **chatd.go**: Remove `refreshChatWorkspaceSnapshot` (no longer needed)
- **createworkspace.go**: Stop persisting agent ID when associating
workspace with chat
- **subagent.go**: Stop passing agent ID to child chats
- **SDK/frontend**: Remove `WorkspaceAgentID` / `workspace_agent_id`
from Chat type

---------

Co-authored-by: Kyle Carberry <kylecarbs@gmail.com>
This commit is contained in:
Kyle Carberry
2026-02-28 16:46:51 -05:00
committed by GitHub
co-authored by Kyle Carberry
parent c316d0a3e7
commit 34d9392e37
18 changed files with 47 additions and 136 deletions
+1 -23
View File
@@ -258,7 +258,6 @@ func (api *API) postChats(rw http.ResponseWriter, r *http.Request) {
chat, err := api.chatDaemon.CreateChat(ctx, chatd.CreateOptions{
OwnerID: apiKey.UserID,
WorkspaceID: workspaceSelection.WorkspaceID,
WorkspaceAgentID: workspaceSelection.WorkspaceAgentID,
Title: title,
ModelConfigID: modelConfigID,
SystemPrompt: defaultChatSystemPrompt(),
@@ -1854,8 +1853,7 @@ func parseGitHubPullRequestURL(raw string) (githubPullRequestRef, bool) {
}
type createChatWorkspaceSelection struct {
WorkspaceID uuid.NullUUID
WorkspaceAgentID uuid.NullUUID
WorkspaceID uuid.NullUUID
}
func (api *API) validateCreateChatWorkspaceSelection(
@@ -1888,23 +1886,6 @@ func (api *API) validateCreateChatWorkspaceSelection(
Valid: true,
}
workspaceAgents, err := api.Database.GetWorkspaceAgentsInLatestBuildByWorkspaceID(
ctx,
workspace.ID,
)
if err != nil {
return selection, http.StatusInternalServerError, &codersdk.Response{
Message: "Failed to get workspace agents.",
Detail: err.Error(),
}
}
if len(workspaceAgents) > 0 {
selection.WorkspaceAgentID = uuid.NullUUID{
UUID: workspaceAgents[0].ID,
Valid: true,
}
}
return selection, 0, nil
}
@@ -2081,9 +2062,6 @@ func convertChat(c database.Chat, diffStatus *database.ChatDiffStatus) codersdk.
if c.WorkspaceID.Valid {
chat.WorkspaceID = &c.WorkspaceID.UUID
}
if c.WorkspaceAgentID.Valid {
chat.WorkspaceAgentID = &c.WorkspaceAgentID.UUID
}
if diffStatus != nil {
convertedDiffStatus := convertChatDiffStatus(c.ID, diffStatus)
chat.DiffStatus = &convertedDiffStatus