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
+20 -49
View File
@@ -133,7 +133,6 @@ var (
type CreateOptions struct {
OwnerID uuid.UUID
WorkspaceID uuid.NullUUID
WorkspaceAgentID uuid.NullUUID
ParentChatID uuid.NullUUID
RootChatID uuid.NullUUID
Title string
@@ -212,7 +211,6 @@ func (p *Server) CreateChat(ctx context.Context, opts CreateOptions) (database.C
insertedChat, err := tx.InsertChat(ctx, database.InsertChatParams{
OwnerID: opts.OwnerID,
WorkspaceID: opts.WorkspaceID,
WorkspaceAgentID: opts.WorkspaceAgentID,
ParentChatID: opts.ParentChatID,
RootChatID: opts.RootChatID,
LastModelConfigID: opts.ModelConfigID,
@@ -1421,10 +1419,6 @@ func (p *Server) publishChatPubsubEvent(chat database.Chat, kind coderdpubsub.Ch
if chat.WorkspaceID.Valid {
sdkChat.WorkspaceID = &chat.WorkspaceID.UUID
}
if chat.WorkspaceAgentID.Valid {
sdkChat.WorkspaceAgentID = &chat.WorkspaceAgentID.UUID
}
event := coderdpubsub.ChatEvent{
Kind: kind,
Chat: sdkChat,
@@ -1825,13 +1819,6 @@ func (p *Server) runChat(
}()
currentChat := chat
loadChatSnapshot := func(
loadCtx context.Context,
chatID uuid.UUID,
) (database.Chat, error) {
//nolint:gocritic // System context required to load chat snapshots for the stream.
return p.db.GetChatByID(dbauthz.AsSystemRestricted(loadCtx), chatID)
}
var (
chatStateMu sync.Mutex
workspaceMu sync.Mutex
@@ -1860,28 +1847,20 @@ func (p *Server) runChat(
return nil, xerrors.New("workspace agent connector is not configured")
}
if !chatSnapshot.WorkspaceAgentID.Valid {
refreshedChat, refreshErr := refreshChatWorkspaceSnapshot(
ctx,
chatSnapshot,
loadChatSnapshot,
)
if refreshErr != nil {
return nil, refreshErr
}
if refreshedChat.WorkspaceAgentID.Valid {
chatStateMu.Lock()
currentChat = refreshedChat
chatSnapshot = refreshedChat
chatStateMu.Unlock()
}
if !chatSnapshot.WorkspaceID.Valid {
return nil, xerrors.New("chat has no workspace")
}
if !chatSnapshot.WorkspaceAgentID.Valid {
//nolint:gocritic // System context needed to look up workspace agents.
agents, err := p.db.GetWorkspaceAgentsInLatestBuildByWorkspaceID(
dbauthz.AsSystemRestricted(ctx),
chatSnapshot.WorkspaceID.UUID,
)
if err != nil || len(agents) == 0 {
return nil, xerrors.New("chat has no workspace agent")
}
agentConn, agentRelease, err := p.agentConnFn(ctx, chatSnapshot.WorkspaceAgentID.UUID)
agentConn, agentRelease, err := p.agentConnFn(ctx, agents[0].ID)
if err != nil {
return nil, xerrors.Errorf("connect to workspace agent: %w", err)
}
@@ -2376,23 +2355,6 @@ func usageNullInt64(value int64, valid bool) sql.NullInt64 {
}
}
func refreshChatWorkspaceSnapshot(
ctx context.Context,
chat database.Chat,
loadChat func(context.Context, uuid.UUID) (database.Chat, error),
) (database.Chat, error) {
if chat.WorkspaceAgentID.Valid || loadChat == nil {
return chat, nil
}
refreshedChat, err := loadChat(ctx, chat.ID)
if err != nil {
return chat, xerrors.Errorf("reload chat workspace state: %w", err)
}
return refreshedChat, nil
}
// resolveInstructions returns the combined system instructions for the
// workspace agent. It reads the home-level (~/.coder/AGENTS.md) and
// working-directory-level (<pwd>/AGENTS.md) instruction files, combines
@@ -2402,10 +2364,19 @@ func (p *Server) resolveInstructions(
chat database.Chat,
getWorkspaceConn func(context.Context) (workspacesdk.AgentConn, error),
) string {
if !chat.WorkspaceAgentID.Valid {
if !chat.WorkspaceID.Valid {
return ""
}
agentID := chat.WorkspaceAgentID.UUID
//nolint:gocritic // System context needed to look up workspace agents.
agents, agentsErr := p.db.GetWorkspaceAgentsInLatestBuildByWorkspaceID(
dbauthz.AsSystemRestricted(ctx),
chat.WorkspaceID.UUID,
)
if agentsErr != nil || len(agents) == 0 {
return ""
}
agentID := agents[0].ID
p.instructionCacheMu.Lock()
cached, ok := p.instructionCache[agentID]
+3 -6
View File
@@ -191,10 +191,6 @@ func CreateWorkspace(options CreateWorkspaceOptions) fantasy.AgentTool {
UUID: workspace.ID,
Valid: true,
},
WorkspaceAgentID: uuid.NullUUID{
UUID: workspaceAgentID,
Valid: workspaceAgentID != uuid.Nil,
},
})
}
@@ -281,14 +277,15 @@ func checkExistingWorkspace(
case database.ProvisionerJobStatusSucceeded:
// Build succeeded — check if agent is reachable.
if chat.WorkspaceAgentID.Valid && agentConnFn != nil {
agents, agentsErr := db.GetWorkspaceAgentsInLatestBuildByWorkspaceID(ctx, ws.ID)
if agentsErr == nil && len(agents) > 0 && agentConnFn != nil {
pingCtx, cancel := context.WithTimeout(
ctx, agentPingTimeout,
)
defer cancel()
conn, release, connErr := agentConnFn(
pingCtx, chat.WorkspaceAgentID.UUID,
pingCtx, agents[0].ID,
)
if connErr == nil {
release()
+2 -3
View File
@@ -240,9 +240,8 @@ func (p *Server) createChildSubagentChat(
}
child, err := p.CreateChat(ctx, CreateOptions{
OwnerID: parent.OwnerID,
WorkspaceID: parent.WorkspaceID,
WorkspaceAgentID: parent.WorkspaceAgentID,
OwnerID: parent.OwnerID,
WorkspaceID: parent.WorkspaceID,
ParentChatID: uuid.NullUUID{
UUID: parent.ID,
Valid: true,
+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
-4
View File
@@ -77,7 +77,6 @@ func TestPostChats(t *testing.T) {
require.NotZero(t, chat.CreatedAt)
require.NotZero(t, chat.UpdatedAt)
require.Nil(t, chat.WorkspaceID)
require.Nil(t, chat.WorkspaceAgentID)
require.NotNil(t, chat.RootChatID)
require.Equal(t, chat.ID, *chat.RootChatID)
@@ -206,8 +205,6 @@ func TestPostChats(t *testing.T) {
require.NoError(t, err)
require.NotNil(t, chat.WorkspaceID)
require.Equal(t, workspaceBuild.Workspace.ID, *chat.WorkspaceID)
require.NotNil(t, chat.WorkspaceAgentID)
require.Equal(t, workspaceBuild.Agents[0].ID, *chat.WorkspaceAgentID)
require.Equal(t, modelConfig.ID, chat.LastModelConfigID)
})
@@ -342,7 +339,6 @@ func TestListChats(t *testing.T) {
require.NotZero(t, chat.UpdatedAt)
require.Nil(t, chat.ParentChatID)
require.Nil(t, chat.WorkspaceID)
require.Nil(t, chat.WorkspaceAgentID)
require.NotNil(t, chat.RootChatID)
require.Equal(t, chat.ID, *chat.RootChatID)
require.NotNil(t, chat.DiffStatus)
+2 -3
View File
@@ -694,9 +694,8 @@ func (s *MethodTestSuite) TestChats() {
s.Run("UpdateChatWorkspace", s.Mocked(func(dbm *dbmock.MockStore, faker *gofakeit.Faker, check *expects) {
chat := testutil.Fake(s.T(), faker, database.Chat{})
arg := database.UpdateChatWorkspaceParams{
ID: chat.ID,
WorkspaceID: uuid.NullUUID{UUID: uuid.New(), Valid: true},
WorkspaceAgentID: uuid.NullUUID{UUID: uuid.New(), Valid: true},
ID: chat.ID,
WorkspaceID: uuid.NullUUID{UUID: uuid.New(), Valid: true},
}
updatedChat := testutil.Fake(s.T(), faker, database.Chat{ID: chat.ID})
dbm.EXPECT().GetChatByID(gomock.Any(), chat.ID).Return(chat, nil).AnyTimes()
-4
View File
@@ -1263,7 +1263,6 @@ CREATE TABLE chats (
id uuid DEFAULT gen_random_uuid() NOT NULL,
owner_id uuid NOT NULL,
workspace_id uuid,
workspace_agent_id uuid,
title text DEFAULT 'New Chat'::text NOT NULL,
status chat_status DEFAULT 'waiting'::chat_status NOT NULL,
worker_id uuid,
@@ -3790,9 +3789,6 @@ ALTER TABLE ONLY chats
ALTER TABLE ONLY chats
ADD CONSTRAINT chats_root_chat_id_fkey FOREIGN KEY (root_chat_id) REFERENCES chats(id) ON DELETE SET NULL;
ALTER TABLE ONLY chats
ADD CONSTRAINT chats_workspace_agent_id_fkey FOREIGN KEY (workspace_agent_id) REFERENCES workspace_agents(id) ON DELETE SET NULL;
ALTER TABLE ONLY chats
ADD CONSTRAINT chats_workspace_id_fkey FOREIGN KEY (workspace_id) REFERENCES workspaces(id) ON DELETE SET NULL;
@@ -21,7 +21,6 @@ const (
ForeignKeyChatsOwnerID ForeignKeyConstraint = "chats_owner_id_fkey" // ALTER TABLE ONLY chats ADD CONSTRAINT chats_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES users(id) ON DELETE CASCADE;
ForeignKeyChatsParentChatID ForeignKeyConstraint = "chats_parent_chat_id_fkey" // ALTER TABLE ONLY chats ADD CONSTRAINT chats_parent_chat_id_fkey FOREIGN KEY (parent_chat_id) REFERENCES chats(id) ON DELETE SET NULL;
ForeignKeyChatsRootChatID ForeignKeyConstraint = "chats_root_chat_id_fkey" // ALTER TABLE ONLY chats ADD CONSTRAINT chats_root_chat_id_fkey FOREIGN KEY (root_chat_id) REFERENCES chats(id) ON DELETE SET NULL;
ForeignKeyChatsWorkspaceAgentID ForeignKeyConstraint = "chats_workspace_agent_id_fkey" // ALTER TABLE ONLY chats ADD CONSTRAINT chats_workspace_agent_id_fkey FOREIGN KEY (workspace_agent_id) REFERENCES workspace_agents(id) ON DELETE SET NULL;
ForeignKeyChatsWorkspaceID ForeignKeyConstraint = "chats_workspace_id_fkey" // ALTER TABLE ONLY chats ADD CONSTRAINT chats_workspace_id_fkey FOREIGN KEY (workspace_id) REFERENCES workspaces(id) ON DELETE SET NULL;
ForeignKeyConnectionLogsOrganizationID ForeignKeyConstraint = "connection_logs_organization_id_fkey" // ALTER TABLE ONLY connection_logs ADD CONSTRAINT connection_logs_organization_id_fkey FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE;
ForeignKeyConnectionLogsWorkspaceID ForeignKeyConstraint = "connection_logs_workspace_id_fkey" // ALTER TABLE ONLY connection_logs ADD CONSTRAINT connection_logs_workspace_id_fkey FOREIGN KEY (workspace_id) REFERENCES workspaces(id) ON DELETE CASCADE;
@@ -0,0 +1 @@
ALTER TABLE chats ADD COLUMN workspace_agent_id UUID REFERENCES workspace_agents(id) ON DELETE SET NULL;
@@ -0,0 +1 @@
ALTER TABLE chats DROP COLUMN workspace_agent_id;
-1
View File
@@ -3889,7 +3889,6 @@ type Chat struct {
ID uuid.UUID `db:"id" json:"id"`
OwnerID uuid.UUID `db:"owner_id" json:"owner_id"`
WorkspaceID uuid.NullUUID `db:"workspace_id" json:"workspace_id"`
WorkspaceAgentID uuid.NullUUID `db:"workspace_agent_id" json:"workspace_agent_id"`
Title string `db:"title" json:"title"`
Status ChatStatus `db:"status" json:"status"`
WorkerID uuid.NullUUID `db:"worker_id" json:"worker_id"`
+16 -33
View File
@@ -2842,7 +2842,7 @@ WHERE
1
)
RETURNING
id, owner_id, workspace_id, workspace_agent_id, title, status, worker_id, started_at, heartbeat_at, created_at, updated_at, parent_chat_id, root_chat_id, last_model_config_id, archived, last_error
id, owner_id, workspace_id, title, status, worker_id, started_at, heartbeat_at, created_at, updated_at, parent_chat_id, root_chat_id, last_model_config_id, archived, last_error
`
type AcquireChatParams struct {
@@ -2859,7 +2859,6 @@ func (q *sqlQuerier) AcquireChat(ctx context.Context, arg AcquireChatParams) (Ch
&i.ID,
&i.OwnerID,
&i.WorkspaceID,
&i.WorkspaceAgentID,
&i.Title,
&i.Status,
&i.WorkerID,
@@ -2940,7 +2939,7 @@ func (q *sqlQuerier) DeleteChatQueuedMessage(ctx context.Context, arg DeleteChat
const getChatByID = `-- name: GetChatByID :one
SELECT
id, owner_id, workspace_id, workspace_agent_id, title, status, worker_id, started_at, heartbeat_at, created_at, updated_at, parent_chat_id, root_chat_id, last_model_config_id, archived, last_error
id, owner_id, workspace_id, title, status, worker_id, started_at, heartbeat_at, created_at, updated_at, parent_chat_id, root_chat_id, last_model_config_id, archived, last_error
FROM
chats
WHERE
@@ -2954,7 +2953,6 @@ func (q *sqlQuerier) GetChatByID(ctx context.Context, id uuid.UUID) (Chat, error
&i.ID,
&i.OwnerID,
&i.WorkspaceID,
&i.WorkspaceAgentID,
&i.Title,
&i.Status,
&i.WorkerID,
@@ -2972,7 +2970,7 @@ func (q *sqlQuerier) GetChatByID(ctx context.Context, id uuid.UUID) (Chat, error
}
const getChatByIDForUpdate = `-- name: GetChatByIDForUpdate :one
SELECT id, owner_id, workspace_id, workspace_agent_id, title, status, worker_id, started_at, heartbeat_at, created_at, updated_at, parent_chat_id, root_chat_id, last_model_config_id, archived, last_error FROM chats WHERE id = $1::uuid FOR UPDATE
SELECT id, owner_id, workspace_id, title, status, worker_id, started_at, heartbeat_at, created_at, updated_at, parent_chat_id, root_chat_id, last_model_config_id, archived, last_error FROM chats WHERE id = $1::uuid FOR UPDATE
`
func (q *sqlQuerier) GetChatByIDForUpdate(ctx context.Context, id uuid.UUID) (Chat, error) {
@@ -2982,7 +2980,6 @@ func (q *sqlQuerier) GetChatByIDForUpdate(ctx context.Context, id uuid.UUID) (Ch
&i.ID,
&i.OwnerID,
&i.WorkspaceID,
&i.WorkspaceAgentID,
&i.Title,
&i.Status,
&i.WorkerID,
@@ -3291,7 +3288,7 @@ func (q *sqlQuerier) GetChatQueuedMessages(ctx context.Context, chatID uuid.UUID
const getChatsByOwnerID = `-- name: GetChatsByOwnerID :many
SELECT
id, owner_id, workspace_id, workspace_agent_id, title, status, worker_id, started_at, heartbeat_at, created_at, updated_at, parent_chat_id, root_chat_id, last_model_config_id, archived, last_error
id, owner_id, workspace_id, title, status, worker_id, started_at, heartbeat_at, created_at, updated_at, parent_chat_id, root_chat_id, last_model_config_id, archived, last_error
FROM
chats
WHERE
@@ -3314,7 +3311,6 @@ func (q *sqlQuerier) GetChatsByOwnerID(ctx context.Context, ownerID uuid.UUID) (
&i.ID,
&i.OwnerID,
&i.WorkspaceID,
&i.WorkspaceAgentID,
&i.Title,
&i.Status,
&i.WorkerID,
@@ -3343,7 +3339,7 @@ func (q *sqlQuerier) GetChatsByOwnerID(ctx context.Context, ownerID uuid.UUID) (
const getStaleChats = `-- name: GetStaleChats :many
SELECT
id, owner_id, workspace_id, workspace_agent_id, title, status, worker_id, started_at, heartbeat_at, created_at, updated_at, parent_chat_id, root_chat_id, last_model_config_id, archived, last_error
id, owner_id, workspace_id, title, status, worker_id, started_at, heartbeat_at, created_at, updated_at, parent_chat_id, root_chat_id, last_model_config_id, archived, last_error
FROM
chats
WHERE
@@ -3366,7 +3362,6 @@ func (q *sqlQuerier) GetStaleChats(ctx context.Context, staleThreshold time.Time
&i.ID,
&i.OwnerID,
&i.WorkspaceID,
&i.WorkspaceAgentID,
&i.Title,
&i.Status,
&i.WorkerID,
@@ -3397,7 +3392,6 @@ const insertChat = `-- name: InsertChat :one
INSERT INTO chats (
owner_id,
workspace_id,
workspace_agent_id,
parent_chat_id,
root_chat_id,
last_model_config_id,
@@ -3408,17 +3402,15 @@ INSERT INTO chats (
$3::uuid,
$4::uuid,
$5::uuid,
$6::uuid,
$7::text
$6::text
)
RETURNING
id, owner_id, workspace_id, workspace_agent_id, title, status, worker_id, started_at, heartbeat_at, created_at, updated_at, parent_chat_id, root_chat_id, last_model_config_id, archived, last_error
id, owner_id, workspace_id, title, status, worker_id, started_at, heartbeat_at, created_at, updated_at, parent_chat_id, root_chat_id, last_model_config_id, archived, last_error
`
type InsertChatParams struct {
OwnerID uuid.UUID `db:"owner_id" json:"owner_id"`
WorkspaceID uuid.NullUUID `db:"workspace_id" json:"workspace_id"`
WorkspaceAgentID uuid.NullUUID `db:"workspace_agent_id" json:"workspace_agent_id"`
ParentChatID uuid.NullUUID `db:"parent_chat_id" json:"parent_chat_id"`
RootChatID uuid.NullUUID `db:"root_chat_id" json:"root_chat_id"`
LastModelConfigID uuid.UUID `db:"last_model_config_id" json:"last_model_config_id"`
@@ -3429,7 +3421,6 @@ func (q *sqlQuerier) InsertChat(ctx context.Context, arg InsertChatParams) (Chat
row := q.db.QueryRowContext(ctx, insertChat,
arg.OwnerID,
arg.WorkspaceID,
arg.WorkspaceAgentID,
arg.ParentChatID,
arg.RootChatID,
arg.LastModelConfigID,
@@ -3440,7 +3431,6 @@ func (q *sqlQuerier) InsertChat(ctx context.Context, arg InsertChatParams) (Chat
&i.ID,
&i.OwnerID,
&i.WorkspaceID,
&i.WorkspaceAgentID,
&i.Title,
&i.Status,
&i.WorkerID,
@@ -3578,7 +3568,7 @@ func (q *sqlQuerier) InsertChatQueuedMessage(ctx context.Context, arg InsertChat
const listChatsByRootID = `-- name: ListChatsByRootID :many
SELECT
id, owner_id, workspace_id, workspace_agent_id, title, status, worker_id, started_at, heartbeat_at, created_at, updated_at, parent_chat_id, root_chat_id, last_model_config_id, archived, last_error
id, owner_id, workspace_id, title, status, worker_id, started_at, heartbeat_at, created_at, updated_at, parent_chat_id, root_chat_id, last_model_config_id, archived, last_error
FROM
chats
WHERE
@@ -3600,7 +3590,6 @@ func (q *sqlQuerier) ListChatsByRootID(ctx context.Context, rootChatID uuid.UUID
&i.ID,
&i.OwnerID,
&i.WorkspaceID,
&i.WorkspaceAgentID,
&i.Title,
&i.Status,
&i.WorkerID,
@@ -3629,7 +3618,7 @@ func (q *sqlQuerier) ListChatsByRootID(ctx context.Context, rootChatID uuid.UUID
const listChildChatsByParentID = `-- name: ListChildChatsByParentID :many
SELECT
id, owner_id, workspace_id, workspace_agent_id, title, status, worker_id, started_at, heartbeat_at, created_at, updated_at, parent_chat_id, root_chat_id, last_model_config_id, archived, last_error
id, owner_id, workspace_id, title, status, worker_id, started_at, heartbeat_at, created_at, updated_at, parent_chat_id, root_chat_id, last_model_config_id, archived, last_error
FROM
chats
WHERE
@@ -3651,7 +3640,6 @@ func (q *sqlQuerier) ListChildChatsByParentID(ctx context.Context, parentChatID
&i.ID,
&i.OwnerID,
&i.WorkspaceID,
&i.WorkspaceAgentID,
&i.Title,
&i.Status,
&i.WorkerID,
@@ -3719,7 +3707,7 @@ SET
WHERE
id = $2::uuid
RETURNING
id, owner_id, workspace_id, workspace_agent_id, title, status, worker_id, started_at, heartbeat_at, created_at, updated_at, parent_chat_id, root_chat_id, last_model_config_id, archived, last_error
id, owner_id, workspace_id, title, status, worker_id, started_at, heartbeat_at, created_at, updated_at, parent_chat_id, root_chat_id, last_model_config_id, archived, last_error
`
type UpdateChatByIDParams struct {
@@ -3734,7 +3722,6 @@ func (q *sqlQuerier) UpdateChatByID(ctx context.Context, arg UpdateChatByIDParam
&i.ID,
&i.OwnerID,
&i.WorkspaceID,
&i.WorkspaceAgentID,
&i.Title,
&i.Status,
&i.WorkerID,
@@ -3831,7 +3818,7 @@ SET
WHERE
id = $6::uuid
RETURNING
id, owner_id, workspace_id, workspace_agent_id, title, status, worker_id, started_at, heartbeat_at, created_at, updated_at, parent_chat_id, root_chat_id, last_model_config_id, archived, last_error
id, owner_id, workspace_id, title, status, worker_id, started_at, heartbeat_at, created_at, updated_at, parent_chat_id, root_chat_id, last_model_config_id, archived, last_error
`
type UpdateChatStatusParams struct {
@@ -3857,7 +3844,6 @@ func (q *sqlQuerier) UpdateChatStatus(ctx context.Context, arg UpdateChatStatusP
&i.ID,
&i.OwnerID,
&i.WorkspaceID,
&i.WorkspaceAgentID,
&i.Title,
&i.Status,
&i.WorkerID,
@@ -3879,28 +3865,25 @@ UPDATE
chats
SET
workspace_id = $1::uuid,
workspace_agent_id = $2::uuid,
updated_at = NOW()
WHERE
id = $3::uuid
id = $2::uuid
RETURNING
id, owner_id, workspace_id, workspace_agent_id, title, status, worker_id, started_at, heartbeat_at, created_at, updated_at, parent_chat_id, root_chat_id, last_model_config_id, archived, last_error
id, owner_id, workspace_id, title, status, worker_id, started_at, heartbeat_at, created_at, updated_at, parent_chat_id, root_chat_id, last_model_config_id, archived, last_error
`
type UpdateChatWorkspaceParams struct {
WorkspaceID uuid.NullUUID `db:"workspace_id" json:"workspace_id"`
WorkspaceAgentID uuid.NullUUID `db:"workspace_agent_id" json:"workspace_agent_id"`
ID uuid.UUID `db:"id" json:"id"`
WorkspaceID uuid.NullUUID `db:"workspace_id" json:"workspace_id"`
ID uuid.UUID `db:"id" json:"id"`
}
func (q *sqlQuerier) UpdateChatWorkspace(ctx context.Context, arg UpdateChatWorkspaceParams) (Chat, error) {
row := q.db.QueryRowContext(ctx, updateChatWorkspace, arg.WorkspaceID, arg.WorkspaceAgentID, arg.ID)
row := q.db.QueryRowContext(ctx, updateChatWorkspace, arg.WorkspaceID, arg.ID)
var i Chat
err := row.Scan(
&i.ID,
&i.OwnerID,
&i.WorkspaceID,
&i.WorkspaceAgentID,
&i.Title,
&i.Status,
&i.WorkerID,
-3
View File
@@ -136,7 +136,6 @@ ORDER BY
INSERT INTO chats (
owner_id,
workspace_id,
workspace_agent_id,
parent_chat_id,
root_chat_id,
last_model_config_id,
@@ -144,7 +143,6 @@ INSERT INTO chats (
) VALUES (
@owner_id::uuid,
sqlc.narg('workspace_id')::uuid,
sqlc.narg('workspace_agent_id')::uuid,
sqlc.narg('parent_chat_id')::uuid,
sqlc.narg('root_chat_id')::uuid,
@last_model_config_id::uuid,
@@ -222,7 +220,6 @@ UPDATE
chats
SET
workspace_id = sqlc.narg('workspace_id')::uuid,
workspace_agent_id = sqlc.narg('workspace_agent_id')::uuid,
updated_at = NOW()
WHERE
id = @id::uuid
-1
View File
@@ -64,7 +64,6 @@ func TestChatParam(t *testing.T) {
chat, err := db.InsertChat(context.Background(), database.InsertChatParams{
OwnerID: ownerID,
WorkspaceID: uuid.NullUUID{},
WorkspaceAgentID: uuid.NullUUID{},
ParentChatID: uuid.NullUUID{},
RootChatID: uuid.NullUUID{},
LastModelConfigID: modelConfig.ID,
-1
View File
@@ -32,7 +32,6 @@ type Chat struct {
ID uuid.UUID `json:"id" format:"uuid"`
OwnerID uuid.UUID `json:"owner_id" format:"uuid"`
WorkspaceID *uuid.UUID `json:"workspace_id,omitempty" format:"uuid"`
WorkspaceAgentID *uuid.UUID `json:"workspace_agent_id,omitempty" format:"uuid"`
ParentChatID *uuid.UUID `json:"parent_chat_id,omitempty" format:"uuid"`
RootChatID *uuid.UUID `json:"root_chat_id,omitempty" format:"uuid"`
LastModelConfigID uuid.UUID `json:"last_model_config_id" format:"uuid"`
-1
View File
@@ -1054,7 +1054,6 @@ export interface Chat {
readonly id: string;
readonly owner_id: string;
readonly workspace_id?: string;
readonly workspace_agent_id?: string;
readonly parent_chat_id?: string;
readonly root_chat_id?: string;
readonly last_model_config_id: string;
@@ -117,7 +117,6 @@ const mockModelCatalog: TypesGen.ChatModelsResponse = {
const baseChatFields = {
owner_id: "owner-id",
workspace_id: mockWorkspace.id,
workspace_agent_id: mockWorkspaceAgent.id,
last_model_config_id: "model-config-1",
created_at: "2026-02-18T00:00:00.000Z",
updated_at: "2026-02-18T00:00:00.000Z",
+1 -2
View File
@@ -494,7 +494,6 @@ const AgentDetail: FC = () => {
});
const chatsQuery = useQuery(chats());
const workspaceId = chatQuery.data?.chat?.workspace_id;
const workspaceAgentId = chatQuery.data?.chat?.workspace_agent_id;
const workspaceQuery = useQuery({
...workspaceById(workspaceId ?? ""),
enabled: Boolean(workspaceId),
@@ -507,7 +506,7 @@ const AgentDetail: FC = () => {
const chatModelConfigsQuery = useQuery(chatModelConfigs());
const hasDiffStatus = Boolean(diffStatusQuery.data?.url);
const workspace = workspaceQuery.data;
const workspaceAgent = getWorkspaceAgent(workspace, workspaceAgentId);
const workspaceAgent = getWorkspaceAgent(workspace, undefined);
const chatData = chatQuery.data;
const chatRecord = chatData?.chat;
const chatMessages = chatData?.messages;