diff --git a/coderd/database/dbauthz/dbauthz.go b/coderd/database/dbauthz/dbauthz.go
index d084514dd8..78e7a6fc6b 100644
--- a/coderd/database/dbauthz/dbauthz.go
+++ b/coderd/database/dbauthz/dbauthz.go
@@ -1845,6 +1845,10 @@ func (q *querier) CountAuditLogs(ctx context.Context, arg database.CountAuditLog
return q.db.CountAuthorizedAuditLogs(ctx, arg, prep)
}
+func (q *querier) CountChatQueuedMessages(ctx context.Context, chatID uuid.UUID) (int64, error) {
+ panic("not implemented")
+}
+
func (q *querier) CountConnectionLogs(ctx context.Context, arg database.CountConnectionLogsParams) (int64, error) {
// Just like the actual query, shortcut if the user is an owner.
err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceConnectionLog)
@@ -1942,6 +1946,10 @@ func (q *querier) DeleteAPIKeysByUserID(ctx context.Context, userID uuid.UUID) e
return q.db.DeleteAPIKeysByUserID(ctx, userID)
}
+func (q *querier) DeleteAllChatHeartbeats(ctx context.Context, chatID uuid.UUID) error {
+ panic("not implemented")
+}
+
func (q *querier) DeleteAllChatQueuedMessages(ctx context.Context, chatID uuid.UUID) error {
chat, err := q.db.GetChatByID(ctx, chatID)
if err != nil {
@@ -1953,6 +1961,10 @@ func (q *querier) DeleteAllChatQueuedMessages(ctx context.Context, chatID uuid.U
return q.db.DeleteAllChatQueuedMessages(ctx, chatID)
}
+func (q *querier) DeleteAllChatQueuedMessagesReturningCount(ctx context.Context, chatID uuid.UUID) (int64, error) {
+ panic("not implemented")
+}
+
func (q *querier) DeleteAllTailnetTunnels(ctx context.Context, arg database.DeleteAllTailnetTunnelsParams) ([]database.DeleteAllTailnetTunnelsRow, error) {
if err := q.authorizeContext(ctx, policy.ActionDelete, rbac.ResourceTailnetCoordinator); err != nil {
return nil, err
@@ -1999,6 +2011,10 @@ func (q *querier) DeleteChatDebugDataByChatID(ctx context.Context, arg database.
return q.db.DeleteChatDebugDataByChatID(ctx, arg)
}
+func (q *querier) DeleteChatHeartbeats(ctx context.Context, arg database.DeleteChatHeartbeatsParams) (int64, error) {
+ panic("not implemented")
+}
+
func (q *querier) DeleteChatModelConfigByID(ctx context.Context, id uuid.UUID) error {
if err := q.authorizeContext(ctx, policy.ActionUpdate, rbac.ResourceDeploymentConfig); err != nil {
return err
@@ -2031,6 +2047,10 @@ func (q *querier) DeleteChatQueuedMessage(ctx context.Context, arg database.Dele
return q.db.DeleteChatQueuedMessage(ctx, arg)
}
+func (q *querier) DeleteChatQueuedMessageReturningCount(ctx context.Context, arg database.DeleteChatQueuedMessageReturningCountParams) (int64, error) {
+ panic("not implemented")
+}
+
func (q *querier) DeleteChatUsageLimitGroupOverride(ctx context.Context, groupID uuid.UUID) error {
if err := q.authorizeContext(ctx, policy.ActionUpdate, rbac.ResourceDeploymentConfig); err != nil {
return err
@@ -2303,6 +2323,10 @@ func (q *querier) DeleteRuntimeConfig(ctx context.Context, key string) error {
return q.db.DeleteRuntimeConfig(ctx, key)
}
+func (q *querier) DeleteStaleChatHeartbeats(ctx context.Context, staleSeconds int32) (int64, error) {
+ panic("not implemented")
+}
+
func (q *querier) DeleteTailnetPeer(ctx context.Context, arg database.DeleteTailnetPeerParams) (database.DeleteTailnetPeerRow, error) {
if err := q.authorizeContext(ctx, policy.ActionDelete, rbac.ResourceTailnetCoordinator); err != nil {
return database.DeleteTailnetPeerRow{}, err
@@ -2814,6 +2838,10 @@ func (q *querier) GetAuthorizationUserRoles(ctx context.Context, userID uuid.UUI
return q.db.GetAuthorizationUserRoles(ctx, userID)
}
+func (q *querier) GetAutoArchiveInactiveChatCandidates(ctx context.Context, arg database.GetAutoArchiveInactiveChatCandidatesParams) ([]database.GetAutoArchiveInactiveChatCandidatesRow, error) {
+ panic("not implemented")
+}
+
func (q *querier) GetBoundaryLogByID(ctx context.Context, id uuid.UUID) (database.BoundaryLog, error) {
if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceBoundaryLog); err != nil {
return database.BoundaryLog{}, err
@@ -2865,6 +2893,10 @@ func (q *querier) GetChatByID(ctx context.Context, id uuid.UUID) (database.Chat,
return fetch(q.log, q.auth, q.db.GetChatByID)(ctx, id)
}
+func (q *querier) GetChatByIDForShare(ctx context.Context, id uuid.UUID) (database.Chat, error) {
+ panic("not implemented")
+}
+
func (q *querier) GetChatByIDForUpdate(ctx context.Context, id uuid.UUID) (database.Chat, error) {
return fetch(q.log, q.auth, q.db.GetChatByIDForUpdate)(ctx, id)
}
@@ -3036,6 +3068,10 @@ func (q *querier) GetChatExploreModelOverride(ctx context.Context) (string, erro
return q.db.GetChatExploreModelOverride(ctx)
}
+func (q *querier) GetChatFamilyIDsByRootID(ctx context.Context, id uuid.UUID) ([]uuid.UUID, error) {
+ panic("not implemented")
+}
+
func (q *querier) GetChatFileByID(ctx context.Context, id uuid.UUID) (database.ChatFile, error) {
file, err := q.db.GetChatFileByID(ctx, id)
if err != nil {
@@ -3102,6 +3138,10 @@ func (q *querier) GetChatGeneralModelOverride(ctx context.Context) (string, erro
return q.db.GetChatGeneralModelOverride(ctx)
}
+func (q *querier) GetChatHeartbeat(ctx context.Context, arg database.GetChatHeartbeatParams) (database.ChatHeartbeat, error) {
+ panic("not implemented")
+}
+
func (q *querier) GetChatIncludeDefaultSystemPrompt(ctx context.Context) (bool, error) {
// The include-default-system-prompt flag is a deployment-wide setting read
// during chat creation by every authenticated user, so no RBAC policy
@@ -3162,6 +3202,10 @@ func (q *querier) GetChatMessagesByChatIDDescPaginated(ctx context.Context, arg
return q.db.GetChatMessagesByChatIDDescPaginated(ctx, arg)
}
+func (q *querier) GetChatMessagesByRevisionForStream(ctx context.Context, arg database.GetChatMessagesByRevisionForStreamParams) ([]database.ChatMessage, error) {
+ panic("not implemented")
+}
+
func (q *querier) GetChatMessagesForPromptByChatID(ctx context.Context, chatID uuid.UUID) ([]database.ChatMessage, error) {
// Authorize read on the parent chat.
_, err := q.GetChatByID(ctx, chatID)
@@ -3210,6 +3254,14 @@ func (q *querier) GetChatPlanModeInstructions(ctx context.Context) (string, erro
return q.db.GetChatPlanModeInstructions(ctx)
}
+func (q *querier) GetChatQueuedMessageByID(ctx context.Context, arg database.GetChatQueuedMessageByIDParams) (database.ChatQueuedMessage, error) {
+ panic("not implemented")
+}
+
+func (q *querier) GetChatQueuedMessageHead(ctx context.Context, chatID uuid.UUID) (database.ChatQueuedMessage, error) {
+ panic("not implemented")
+}
+
func (q *querier) GetChatQueuedMessages(ctx context.Context, chatID uuid.UUID) ([]database.ChatQueuedMessage, error) {
_, err := q.GetChatByID(ctx, chatID)
if err != nil {
@@ -3218,6 +3270,10 @@ func (q *querier) GetChatQueuedMessages(ctx context.Context, chatID uuid.UUID) (
return q.db.GetChatQueuedMessages(ctx, chatID)
}
+func (q *querier) GetChatQueuedMessagesByPosition(ctx context.Context, chatID uuid.UUID) ([]database.ChatQueuedMessage, error) {
+ panic("not implemented")
+}
+
func (q *querier) GetChatRetentionDays(ctx context.Context) (int32, error) {
// Chat retention is a deployment-wide config read by dbpurge.
// Only requires a valid actor in context.
@@ -3227,6 +3283,10 @@ func (q *querier) GetChatRetentionDays(ctx context.Context) (int32, error) {
return q.db.GetChatRetentionDays(ctx)
}
+func (q *querier) GetChatStreamSyncRows(ctx context.Context, ids []uuid.UUID) ([]database.GetChatStreamSyncRowsRow, error) {
+ panic("not implemented")
+}
+
func (q *querier) GetChatSystemPrompt(ctx context.Context) (string, error) {
// The system prompt is a deployment-wide setting read during chat
// creation by every authenticated user, so no RBAC policy check
@@ -3299,6 +3359,10 @@ func (q *querier) GetChatUserPromptsByChatID(ctx context.Context, arg database.G
return q.db.GetChatUserPromptsByChatID(ctx, arg)
}
+func (q *querier) GetChatWorkerAcquisitionCandidates(ctx context.Context, arg database.GetChatWorkerAcquisitionCandidatesParams) ([]database.GetChatWorkerAcquisitionCandidatesRow, error) {
+ panic("not implemented")
+}
+
func (q *querier) GetChatWorkspaceTTL(ctx context.Context) (string, error) {
// The workspace-TTL setting is a deployment-wide value read by any
// authenticated chat user. We only require that an explicit actor is
@@ -3321,6 +3385,10 @@ func (q *querier) GetChatsByChatFileID(ctx context.Context, fileID uuid.UUID) ([
return fetchWithPostFilter(q.auth, policy.ActionRead, q.db.GetChatsByChatFileID)(ctx, fileID)
}
+func (q *querier) GetChatsByIDsForRunnerSync(ctx context.Context, ids []uuid.UUID) ([]database.Chat, error) {
+ panic("not implemented")
+}
+
func (q *querier) GetChatsByWorkspaceIDs(ctx context.Context, ids []uuid.UUID) ([]database.Chat, error) {
return fetchWithPostFilter(q.auth, policy.ActionRead, q.db.GetChatsByWorkspaceIDs)(ctx, ids)
}
@@ -3391,6 +3459,10 @@ func (q *querier) GetDERPMeshKey(ctx context.Context) (string, error) {
return q.db.GetDERPMeshKey(ctx)
}
+func (q *querier) GetDatabaseNow(ctx context.Context) (time.Time, error) {
+ panic("not implemented")
+}
+
func (q *querier) GetDefaultChatModelConfig(ctx context.Context) (database.ChatModelConfig, error) {
// Reading the default model config is needed for chat creation.
// TODO(CODAGT-161): scope this check when org context is available.
@@ -5435,6 +5507,10 @@ func (q *querier) GetWorkspacesForWorkspaceMetrics(ctx context.Context) ([]datab
return q.db.GetWorkspacesForWorkspaceMetrics(ctx)
}
+func (q *querier) IncrementChatGenerationAttempt(ctx context.Context, id uuid.UUID) (int64, error) {
+ panic("not implemented")
+}
+
func (q *querier) InsertAIBridgeInterception(ctx context.Context, arg database.InsertAIBridgeInterceptionParams) (database.AIBridgeInterception, error) {
return insert(q.log, q.auth, rbac.ResourceAibridgeInterception.WithOwner(arg.InitiatorID.String()), q.db.InsertAIBridgeInterception)(ctx, arg)
}
@@ -5605,6 +5681,10 @@ func (q *querier) InsertChatQueuedMessage(ctx context.Context, arg database.Inse
return q.db.InsertChatQueuedMessage(ctx, arg)
}
+func (q *querier) InsertChatQueuedMessageWithCreator(ctx context.Context, arg database.InsertChatQueuedMessageWithCreatorParams) (database.ChatQueuedMessage, error) {
+ panic("not implemented")
+}
+
func (q *querier) InsertCryptoKey(ctx context.Context, arg database.InsertCryptoKeyParams) (database.CryptoKey, error) {
if err := q.authorizeContext(ctx, policy.ActionCreate, rbac.ResourceCryptoKey); err != nil {
return database.CryptoKey{}, err
@@ -6163,6 +6243,10 @@ func (q *querier) InsertWorkspaceResourceMetadata(ctx context.Context, arg datab
return q.db.InsertWorkspaceResourceMetadata(ctx, arg)
}
+func (q *querier) IsChatHeartbeatStale(ctx context.Context, arg database.IsChatHeartbeatStaleParams) (bool, error) {
+ panic("not implemented")
+}
+
func (q *querier) LinkChatFiles(ctx context.Context, arg database.LinkChatFilesParams) (int32, error) {
chat, err := q.db.GetChatByID(ctx, arg.ChatID)
if err != nil {
@@ -6355,6 +6439,10 @@ func (q *querier) ListWorkspaceAgentPortShares(ctx context.Context, workspaceID
return q.db.ListWorkspaceAgentPortShares(ctx, workspaceID)
}
+func (q *querier) LockChatAndBumpSnapshotVersion(ctx context.Context, id uuid.UUID) (database.Chat, error) {
+ panic("not implemented")
+}
+
func (q *querier) MarkAllInboxNotificationsAsRead(ctx context.Context, arg database.MarkAllInboxNotificationsAsReadParams) error {
resource := rbac.ResourceInboxNotification.WithOwner(arg.UserID.String())
@@ -6461,6 +6549,10 @@ func (q *querier) ReorderChatQueuedMessageToFront(ctx context.Context, arg datab
return q.db.ReorderChatQueuedMessageToFront(ctx, arg)
}
+func (q *querier) ReorderChatQueuedMessageToHead(ctx context.Context, arg database.ReorderChatQueuedMessageToHeadParams) (int64, error) {
+ panic("not implemented")
+}
+
func (q *querier) ResolveUserChatSpendLimit(ctx context.Context, arg database.ResolveUserChatSpendLimitParams) (database.ResolveUserChatSpendLimitRow, error) {
if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceChat.WithOwner(arg.UserID.String())); err != nil {
return database.ResolveUserChatSpendLimitRow{}, err
@@ -6703,6 +6795,10 @@ func (q *querier) UpdateChatDebugStep(ctx context.Context, arg database.UpdateCh
return q.db.UpdateChatDebugStep(ctx, arg)
}
+func (q *querier) UpdateChatExecutionState(ctx context.Context, arg database.UpdateChatExecutionStateParams) (database.Chat, error) {
+ panic("not implemented")
+}
+
func (q *querier) UpdateChatHeartbeats(ctx context.Context, arg database.UpdateChatHeartbeatsParams) ([]uuid.UUID, error) {
// The batch heartbeat is a system-level operation filtered by
// worker_id. Authorization is enforced by the AsChatd context
@@ -6825,6 +6921,10 @@ func (q *querier) UpdateChatPlanModeByID(ctx context.Context, arg database.Updat
return q.db.UpdateChatPlanModeByID(ctx, arg)
}
+func (q *querier) UpdateChatRetryState(ctx context.Context, arg database.UpdateChatRetryStateParams) (database.Chat, error) {
+ panic("not implemented")
+}
+
func (q *querier) UpdateChatStatus(ctx context.Context, arg database.UpdateChatStatusParams) (database.Chat, error) {
// UpdateChatStatus is used by the chat processor to change chat status.
// It should be called with system context.
@@ -8164,6 +8264,14 @@ func (q *querier) UpsertChatGeneralModelOverride(ctx context.Context, value stri
return q.db.UpsertChatGeneralModelOverride(ctx, value)
}
+func (q *querier) UpsertChatHeartbeat(ctx context.Context, arg database.UpsertChatHeartbeatParams) error {
+ panic("not implemented")
+}
+
+func (q *querier) UpsertChatHeartbeats(ctx context.Context, arg database.UpsertChatHeartbeatsParams) error {
+ panic("not implemented")
+}
+
func (q *querier) UpsertChatIncludeDefaultSystemPrompt(ctx context.Context, includeDefaultSystemPrompt bool) error {
if err := q.authorizeContext(ctx, policy.ActionUpdate, rbac.ResourceDeploymentConfig); err != nil {
return err
diff --git a/coderd/database/dbmetrics/querymetrics.go b/coderd/database/dbmetrics/querymetrics.go
index 7f68852baf..00c535f8b3 100644
--- a/coderd/database/dbmetrics/querymetrics.go
+++ b/coderd/database/dbmetrics/querymetrics.go
@@ -321,6 +321,14 @@ func (m queryMetricsStore) CountAuditLogs(ctx context.Context, arg database.Coun
return r0, r1
}
+func (m queryMetricsStore) CountChatQueuedMessages(ctx context.Context, chatID uuid.UUID) (int64, error) {
+ start := time.Now()
+ r0, r1 := m.s.CountChatQueuedMessages(ctx, chatID)
+ m.queryLatencies.WithLabelValues("CountChatQueuedMessages").Observe(time.Since(start).Seconds())
+ m.queryCounts.WithLabelValues(httpmw.ExtractHTTPRoute(ctx), httpmw.ExtractHTTPMethod(ctx), "CountChatQueuedMessages").Inc()
+ return r0, r1
+}
+
func (m queryMetricsStore) CountConnectionLogs(ctx context.Context, arg database.CountConnectionLogsParams) (int64, error) {
start := time.Now()
r0, r1 := m.s.CountConnectionLogs(ctx, arg)
@@ -417,6 +425,14 @@ func (m queryMetricsStore) DeleteAPIKeysByUserID(ctx context.Context, userID uui
return r0
}
+func (m queryMetricsStore) DeleteAllChatHeartbeats(ctx context.Context, chatID uuid.UUID) error {
+ start := time.Now()
+ r0 := m.s.DeleteAllChatHeartbeats(ctx, chatID)
+ m.queryLatencies.WithLabelValues("DeleteAllChatHeartbeats").Observe(time.Since(start).Seconds())
+ m.queryCounts.WithLabelValues(httpmw.ExtractHTTPRoute(ctx), httpmw.ExtractHTTPMethod(ctx), "DeleteAllChatHeartbeats").Inc()
+ return r0
+}
+
func (m queryMetricsStore) DeleteAllChatQueuedMessages(ctx context.Context, chatID uuid.UUID) error {
start := time.Now()
r0 := m.s.DeleteAllChatQueuedMessages(ctx, chatID)
@@ -425,6 +441,14 @@ func (m queryMetricsStore) DeleteAllChatQueuedMessages(ctx context.Context, chat
return r0
}
+func (m queryMetricsStore) DeleteAllChatQueuedMessagesReturningCount(ctx context.Context, chatID uuid.UUID) (int64, error) {
+ start := time.Now()
+ r0, r1 := m.s.DeleteAllChatQueuedMessagesReturningCount(ctx, chatID)
+ m.queryLatencies.WithLabelValues("DeleteAllChatQueuedMessagesReturningCount").Observe(time.Since(start).Seconds())
+ m.queryCounts.WithLabelValues(httpmw.ExtractHTTPRoute(ctx), httpmw.ExtractHTTPMethod(ctx), "DeleteAllChatQueuedMessagesReturningCount").Inc()
+ return r0, r1
+}
+
func (m queryMetricsStore) DeleteAllTailnetTunnels(ctx context.Context, arg database.DeleteAllTailnetTunnelsParams) ([]database.DeleteAllTailnetTunnelsRow, error) {
start := time.Now()
r0, r1 := m.s.DeleteAllTailnetTunnels(ctx, arg)
@@ -465,6 +489,14 @@ func (m queryMetricsStore) DeleteChatDebugDataByChatID(ctx context.Context, chat
return r0, r1
}
+func (m queryMetricsStore) DeleteChatHeartbeats(ctx context.Context, arg database.DeleteChatHeartbeatsParams) (int64, error) {
+ start := time.Now()
+ r0, r1 := m.s.DeleteChatHeartbeats(ctx, arg)
+ m.queryLatencies.WithLabelValues("DeleteChatHeartbeats").Observe(time.Since(start).Seconds())
+ m.queryCounts.WithLabelValues(httpmw.ExtractHTTPRoute(ctx), httpmw.ExtractHTTPMethod(ctx), "DeleteChatHeartbeats").Inc()
+ return r0, r1
+}
+
func (m queryMetricsStore) DeleteChatModelConfigByID(ctx context.Context, id uuid.UUID) error {
start := time.Now()
r0 := m.s.DeleteChatModelConfigByID(ctx, id)
@@ -497,6 +529,14 @@ func (m queryMetricsStore) DeleteChatQueuedMessage(ctx context.Context, arg data
return r0
}
+func (m queryMetricsStore) DeleteChatQueuedMessageReturningCount(ctx context.Context, arg database.DeleteChatQueuedMessageReturningCountParams) (int64, error) {
+ start := time.Now()
+ r0, r1 := m.s.DeleteChatQueuedMessageReturningCount(ctx, arg)
+ m.queryLatencies.WithLabelValues("DeleteChatQueuedMessageReturningCount").Observe(time.Since(start).Seconds())
+ m.queryCounts.WithLabelValues(httpmw.ExtractHTTPRoute(ctx), httpmw.ExtractHTTPMethod(ctx), "DeleteChatQueuedMessageReturningCount").Inc()
+ return r0, r1
+}
+
func (m queryMetricsStore) DeleteChatUsageLimitGroupOverride(ctx context.Context, groupID uuid.UUID) error {
start := time.Now()
r0 := m.s.DeleteChatUsageLimitGroupOverride(ctx, groupID)
@@ -777,6 +817,14 @@ func (m queryMetricsStore) DeleteRuntimeConfig(ctx context.Context, key string)
return r0
}
+func (m queryMetricsStore) DeleteStaleChatHeartbeats(ctx context.Context, staleSeconds int32) (int64, error) {
+ start := time.Now()
+ r0, r1 := m.s.DeleteStaleChatHeartbeats(ctx, staleSeconds)
+ m.queryLatencies.WithLabelValues("DeleteStaleChatHeartbeats").Observe(time.Since(start).Seconds())
+ m.queryCounts.WithLabelValues(httpmw.ExtractHTTPRoute(ctx), httpmw.ExtractHTTPMethod(ctx), "DeleteStaleChatHeartbeats").Inc()
+ return r0, r1
+}
+
func (m queryMetricsStore) DeleteTailnetPeer(ctx context.Context, arg database.DeleteTailnetPeerParams) (database.DeleteTailnetPeerRow, error) {
start := time.Now()
r0, r1 := m.s.DeleteTailnetPeer(ctx, arg)
@@ -1273,6 +1321,14 @@ func (m queryMetricsStore) GetAuthorizationUserRoles(ctx context.Context, userID
return r0, r1
}
+func (m queryMetricsStore) GetAutoArchiveInactiveChatCandidates(ctx context.Context, arg database.GetAutoArchiveInactiveChatCandidatesParams) ([]database.GetAutoArchiveInactiveChatCandidatesRow, error) {
+ start := time.Now()
+ r0, r1 := m.s.GetAutoArchiveInactiveChatCandidates(ctx, arg)
+ m.queryLatencies.WithLabelValues("GetAutoArchiveInactiveChatCandidates").Observe(time.Since(start).Seconds())
+ m.queryCounts.WithLabelValues(httpmw.ExtractHTTPRoute(ctx), httpmw.ExtractHTTPMethod(ctx), "GetAutoArchiveInactiveChatCandidates").Inc()
+ return r0, r1
+}
+
func (m queryMetricsStore) GetBoundaryLogByID(ctx context.Context, id uuid.UUID) (database.BoundaryLog, error) {
start := time.Now()
r0, r1 := m.s.GetBoundaryLogByID(ctx, id)
@@ -1321,6 +1377,14 @@ func (m queryMetricsStore) GetChatByID(ctx context.Context, id uuid.UUID) (datab
return r0, r1
}
+func (m queryMetricsStore) GetChatByIDForShare(ctx context.Context, id uuid.UUID) (database.Chat, error) {
+ start := time.Now()
+ r0, r1 := m.s.GetChatByIDForShare(ctx, id)
+ m.queryLatencies.WithLabelValues("GetChatByIDForShare").Observe(time.Since(start).Seconds())
+ m.queryCounts.WithLabelValues(httpmw.ExtractHTTPRoute(ctx), httpmw.ExtractHTTPMethod(ctx), "GetChatByIDForShare").Inc()
+ return r0, r1
+}
+
func (m queryMetricsStore) GetChatByIDForUpdate(ctx context.Context, id uuid.UUID) (database.Chat, error) {
start := time.Now()
r0, r1 := m.s.GetChatByIDForUpdate(ctx, id)
@@ -1449,6 +1513,14 @@ func (m queryMetricsStore) GetChatExploreModelOverride(ctx context.Context) (str
return r0, r1
}
+func (m queryMetricsStore) GetChatFamilyIDsByRootID(ctx context.Context, id uuid.UUID) ([]uuid.UUID, error) {
+ start := time.Now()
+ r0, r1 := m.s.GetChatFamilyIDsByRootID(ctx, id)
+ m.queryLatencies.WithLabelValues("GetChatFamilyIDsByRootID").Observe(time.Since(start).Seconds())
+ m.queryCounts.WithLabelValues(httpmw.ExtractHTTPRoute(ctx), httpmw.ExtractHTTPMethod(ctx), "GetChatFamilyIDsByRootID").Inc()
+ return r0, r1
+}
+
func (m queryMetricsStore) GetChatFileByID(ctx context.Context, id uuid.UUID) (database.ChatFile, error) {
start := time.Now()
r0, r1 := m.s.GetChatFileByID(ctx, id)
@@ -1481,6 +1553,14 @@ func (m queryMetricsStore) GetChatGeneralModelOverride(ctx context.Context) (str
return r0, r1
}
+func (m queryMetricsStore) GetChatHeartbeat(ctx context.Context, arg database.GetChatHeartbeatParams) (database.ChatHeartbeat, error) {
+ start := time.Now()
+ r0, r1 := m.s.GetChatHeartbeat(ctx, arg)
+ m.queryLatencies.WithLabelValues("GetChatHeartbeat").Observe(time.Since(start).Seconds())
+ m.queryCounts.WithLabelValues(httpmw.ExtractHTTPRoute(ctx), httpmw.ExtractHTTPMethod(ctx), "GetChatHeartbeat").Inc()
+ return r0, r1
+}
+
func (m queryMetricsStore) GetChatIncludeDefaultSystemPrompt(ctx context.Context) (bool, error) {
start := time.Now()
r0, r1 := m.s.GetChatIncludeDefaultSystemPrompt(ctx)
@@ -1529,6 +1609,14 @@ func (m queryMetricsStore) GetChatMessagesByChatIDDescPaginated(ctx context.Cont
return r0, r1
}
+func (m queryMetricsStore) GetChatMessagesByRevisionForStream(ctx context.Context, arg database.GetChatMessagesByRevisionForStreamParams) ([]database.ChatMessage, error) {
+ start := time.Now()
+ r0, r1 := m.s.GetChatMessagesByRevisionForStream(ctx, arg)
+ m.queryLatencies.WithLabelValues("GetChatMessagesByRevisionForStream").Observe(time.Since(start).Seconds())
+ m.queryCounts.WithLabelValues(httpmw.ExtractHTTPRoute(ctx), httpmw.ExtractHTTPMethod(ctx), "GetChatMessagesByRevisionForStream").Inc()
+ return r0, r1
+}
+
func (m queryMetricsStore) GetChatMessagesForPromptByChatID(ctx context.Context, chatID uuid.UUID) ([]database.ChatMessage, error) {
start := time.Now()
r0, r1 := m.s.GetChatMessagesForPromptByChatID(ctx, chatID)
@@ -1577,6 +1665,22 @@ func (m queryMetricsStore) GetChatPlanModeInstructions(ctx context.Context) (str
return r0, r1
}
+func (m queryMetricsStore) GetChatQueuedMessageByID(ctx context.Context, arg database.GetChatQueuedMessageByIDParams) (database.ChatQueuedMessage, error) {
+ start := time.Now()
+ r0, r1 := m.s.GetChatQueuedMessageByID(ctx, arg)
+ m.queryLatencies.WithLabelValues("GetChatQueuedMessageByID").Observe(time.Since(start).Seconds())
+ m.queryCounts.WithLabelValues(httpmw.ExtractHTTPRoute(ctx), httpmw.ExtractHTTPMethod(ctx), "GetChatQueuedMessageByID").Inc()
+ return r0, r1
+}
+
+func (m queryMetricsStore) GetChatQueuedMessageHead(ctx context.Context, chatID uuid.UUID) (database.ChatQueuedMessage, error) {
+ start := time.Now()
+ r0, r1 := m.s.GetChatQueuedMessageHead(ctx, chatID)
+ m.queryLatencies.WithLabelValues("GetChatQueuedMessageHead").Observe(time.Since(start).Seconds())
+ m.queryCounts.WithLabelValues(httpmw.ExtractHTTPRoute(ctx), httpmw.ExtractHTTPMethod(ctx), "GetChatQueuedMessageHead").Inc()
+ return r0, r1
+}
+
func (m queryMetricsStore) GetChatQueuedMessages(ctx context.Context, chatID uuid.UUID) ([]database.ChatQueuedMessage, error) {
start := time.Now()
r0, r1 := m.s.GetChatQueuedMessages(ctx, chatID)
@@ -1585,6 +1689,14 @@ func (m queryMetricsStore) GetChatQueuedMessages(ctx context.Context, chatID uui
return r0, r1
}
+func (m queryMetricsStore) GetChatQueuedMessagesByPosition(ctx context.Context, chatID uuid.UUID) ([]database.ChatQueuedMessage, error) {
+ start := time.Now()
+ r0, r1 := m.s.GetChatQueuedMessagesByPosition(ctx, chatID)
+ m.queryLatencies.WithLabelValues("GetChatQueuedMessagesByPosition").Observe(time.Since(start).Seconds())
+ m.queryCounts.WithLabelValues(httpmw.ExtractHTTPRoute(ctx), httpmw.ExtractHTTPMethod(ctx), "GetChatQueuedMessagesByPosition").Inc()
+ return r0, r1
+}
+
func (m queryMetricsStore) GetChatRetentionDays(ctx context.Context) (int32, error) {
start := time.Now()
r0, r1 := m.s.GetChatRetentionDays(ctx)
@@ -1593,6 +1705,14 @@ func (m queryMetricsStore) GetChatRetentionDays(ctx context.Context) (int32, err
return r0, r1
}
+func (m queryMetricsStore) GetChatStreamSyncRows(ctx context.Context, ids []uuid.UUID) ([]database.GetChatStreamSyncRowsRow, error) {
+ start := time.Now()
+ r0, r1 := m.s.GetChatStreamSyncRows(ctx, ids)
+ m.queryLatencies.WithLabelValues("GetChatStreamSyncRows").Observe(time.Since(start).Seconds())
+ m.queryCounts.WithLabelValues(httpmw.ExtractHTTPRoute(ctx), httpmw.ExtractHTTPMethod(ctx), "GetChatStreamSyncRows").Inc()
+ return r0, r1
+}
+
func (m queryMetricsStore) GetChatSystemPrompt(ctx context.Context) (string, error) {
start := time.Now()
r0, r1 := m.s.GetChatSystemPrompt(ctx)
@@ -1657,6 +1777,14 @@ func (m queryMetricsStore) GetChatUserPromptsByChatID(ctx context.Context, arg d
return r0, r1
}
+func (m queryMetricsStore) GetChatWorkerAcquisitionCandidates(ctx context.Context, arg database.GetChatWorkerAcquisitionCandidatesParams) ([]database.GetChatWorkerAcquisitionCandidatesRow, error) {
+ start := time.Now()
+ r0, r1 := m.s.GetChatWorkerAcquisitionCandidates(ctx, arg)
+ m.queryLatencies.WithLabelValues("GetChatWorkerAcquisitionCandidates").Observe(time.Since(start).Seconds())
+ m.queryCounts.WithLabelValues(httpmw.ExtractHTTPRoute(ctx), httpmw.ExtractHTTPMethod(ctx), "GetChatWorkerAcquisitionCandidates").Inc()
+ return r0, r1
+}
+
func (m queryMetricsStore) GetChatWorkspaceTTL(ctx context.Context) (string, error) {
start := time.Now()
r0, r1 := m.s.GetChatWorkspaceTTL(ctx)
@@ -1681,6 +1809,14 @@ func (m queryMetricsStore) GetChatsByChatFileID(ctx context.Context, fileID uuid
return r0, r1
}
+func (m queryMetricsStore) GetChatsByIDsForRunnerSync(ctx context.Context, ids []uuid.UUID) ([]database.Chat, error) {
+ start := time.Now()
+ r0, r1 := m.s.GetChatsByIDsForRunnerSync(ctx, ids)
+ m.queryLatencies.WithLabelValues("GetChatsByIDsForRunnerSync").Observe(time.Since(start).Seconds())
+ m.queryCounts.WithLabelValues(httpmw.ExtractHTTPRoute(ctx), httpmw.ExtractHTTPMethod(ctx), "GetChatsByIDsForRunnerSync").Inc()
+ return r0, r1
+}
+
func (m queryMetricsStore) GetChatsByWorkspaceIDs(ctx context.Context, ids []uuid.UUID) ([]database.Chat, error) {
start := time.Now()
r0, r1 := m.s.GetChatsByWorkspaceIDs(ctx, ids)
@@ -1753,6 +1889,14 @@ func (m queryMetricsStore) GetDERPMeshKey(ctx context.Context) (string, error) {
return r0, r1
}
+func (m queryMetricsStore) GetDatabaseNow(ctx context.Context) (time.Time, error) {
+ start := time.Now()
+ r0, r1 := m.s.GetDatabaseNow(ctx)
+ m.queryLatencies.WithLabelValues("GetDatabaseNow").Observe(time.Since(start).Seconds())
+ m.queryCounts.WithLabelValues(httpmw.ExtractHTTPRoute(ctx), httpmw.ExtractHTTPMethod(ctx), "GetDatabaseNow").Inc()
+ return r0, r1
+}
+
func (m queryMetricsStore) GetDefaultChatModelConfig(ctx context.Context) (database.ChatModelConfig, error) {
start := time.Now()
r0, r1 := m.s.GetDefaultChatModelConfig(ctx)
@@ -3689,6 +3833,14 @@ func (m queryMetricsStore) GetWorkspacesForWorkspaceMetrics(ctx context.Context)
return r0, r1
}
+func (m queryMetricsStore) IncrementChatGenerationAttempt(ctx context.Context, id uuid.UUID) (int64, error) {
+ start := time.Now()
+ r0, r1 := m.s.IncrementChatGenerationAttempt(ctx, id)
+ m.queryLatencies.WithLabelValues("IncrementChatGenerationAttempt").Observe(time.Since(start).Seconds())
+ m.queryCounts.WithLabelValues(httpmw.ExtractHTTPRoute(ctx), httpmw.ExtractHTTPMethod(ctx), "IncrementChatGenerationAttempt").Inc()
+ return r0, r1
+}
+
func (m queryMetricsStore) InsertAIBridgeInterception(ctx context.Context, arg database.InsertAIBridgeInterceptionParams) (database.AIBridgeInterception, error) {
start := time.Now()
r0, r1 := m.s.InsertAIBridgeInterception(ctx, arg)
@@ -3849,6 +4001,14 @@ func (m queryMetricsStore) InsertChatQueuedMessage(ctx context.Context, arg data
return r0, r1
}
+func (m queryMetricsStore) InsertChatQueuedMessageWithCreator(ctx context.Context, arg database.InsertChatQueuedMessageWithCreatorParams) (database.ChatQueuedMessage, error) {
+ start := time.Now()
+ r0, r1 := m.s.InsertChatQueuedMessageWithCreator(ctx, arg)
+ m.queryLatencies.WithLabelValues("InsertChatQueuedMessageWithCreator").Observe(time.Since(start).Seconds())
+ m.queryCounts.WithLabelValues(httpmw.ExtractHTTPRoute(ctx), httpmw.ExtractHTTPMethod(ctx), "InsertChatQueuedMessageWithCreator").Inc()
+ return r0, r1
+}
+
func (m queryMetricsStore) InsertCryptoKey(ctx context.Context, arg database.InsertCryptoKeyParams) (database.CryptoKey, error) {
start := time.Now()
r0, r1 := m.s.InsertCryptoKey(ctx, arg)
@@ -4345,6 +4505,14 @@ func (m queryMetricsStore) InsertWorkspaceResourceMetadata(ctx context.Context,
return r0, r1
}
+func (m queryMetricsStore) IsChatHeartbeatStale(ctx context.Context, arg database.IsChatHeartbeatStaleParams) (bool, error) {
+ start := time.Now()
+ r0, r1 := m.s.IsChatHeartbeatStale(ctx, arg)
+ m.queryLatencies.WithLabelValues("IsChatHeartbeatStale").Observe(time.Since(start).Seconds())
+ m.queryCounts.WithLabelValues(httpmw.ExtractHTTPRoute(ctx), httpmw.ExtractHTTPMethod(ctx), "IsChatHeartbeatStale").Inc()
+ return r0, r1
+}
+
func (m queryMetricsStore) LinkChatFiles(ctx context.Context, arg database.LinkChatFilesParams) (int32, error) {
start := time.Now()
r0, r1 := m.s.LinkChatFiles(ctx, arg)
@@ -4537,6 +4705,14 @@ func (m queryMetricsStore) ListWorkspaceAgentPortShares(ctx context.Context, wor
return r0, r1
}
+func (m queryMetricsStore) LockChatAndBumpSnapshotVersion(ctx context.Context, id uuid.UUID) (database.Chat, error) {
+ start := time.Now()
+ r0, r1 := m.s.LockChatAndBumpSnapshotVersion(ctx, id)
+ m.queryLatencies.WithLabelValues("LockChatAndBumpSnapshotVersion").Observe(time.Since(start).Seconds())
+ m.queryCounts.WithLabelValues(httpmw.ExtractHTTPRoute(ctx), httpmw.ExtractHTTPMethod(ctx), "LockChatAndBumpSnapshotVersion").Inc()
+ return r0, r1
+}
+
func (m queryMetricsStore) MarkAllInboxNotificationsAsRead(ctx context.Context, arg database.MarkAllInboxNotificationsAsReadParams) error {
start := time.Now()
r0 := m.s.MarkAllInboxNotificationsAsRead(ctx, arg)
@@ -4625,6 +4801,14 @@ func (m queryMetricsStore) ReorderChatQueuedMessageToFront(ctx context.Context,
return r0, r1
}
+func (m queryMetricsStore) ReorderChatQueuedMessageToHead(ctx context.Context, arg database.ReorderChatQueuedMessageToHeadParams) (int64, error) {
+ start := time.Now()
+ r0, r1 := m.s.ReorderChatQueuedMessageToHead(ctx, arg)
+ m.queryLatencies.WithLabelValues("ReorderChatQueuedMessageToHead").Observe(time.Since(start).Seconds())
+ m.queryCounts.WithLabelValues(httpmw.ExtractHTTPRoute(ctx), httpmw.ExtractHTTPMethod(ctx), "ReorderChatQueuedMessageToHead").Inc()
+ return r0, r1
+}
+
func (m queryMetricsStore) ResolveUserChatSpendLimit(ctx context.Context, userID database.ResolveUserChatSpendLimitParams) (database.ResolveUserChatSpendLimitRow, error) {
start := time.Now()
r0, r1 := m.s.ResolveUserChatSpendLimit(ctx, userID)
@@ -4817,6 +5001,14 @@ func (m queryMetricsStore) UpdateChatDebugStep(ctx context.Context, arg database
return r0, r1
}
+func (m queryMetricsStore) UpdateChatExecutionState(ctx context.Context, arg database.UpdateChatExecutionStateParams) (database.Chat, error) {
+ start := time.Now()
+ r0, r1 := m.s.UpdateChatExecutionState(ctx, arg)
+ m.queryLatencies.WithLabelValues("UpdateChatExecutionState").Observe(time.Since(start).Seconds())
+ m.queryCounts.WithLabelValues(httpmw.ExtractHTTPRoute(ctx), httpmw.ExtractHTTPMethod(ctx), "UpdateChatExecutionState").Inc()
+ return r0, r1
+}
+
func (m queryMetricsStore) UpdateChatHeartbeats(ctx context.Context, arg database.UpdateChatHeartbeatsParams) ([]uuid.UUID, error) {
start := time.Now()
r0, r1 := m.s.UpdateChatHeartbeats(ctx, arg)
@@ -4905,6 +5097,14 @@ func (m queryMetricsStore) UpdateChatPlanModeByID(ctx context.Context, arg datab
return r0, r1
}
+func (m queryMetricsStore) UpdateChatRetryState(ctx context.Context, arg database.UpdateChatRetryStateParams) (database.Chat, error) {
+ start := time.Now()
+ r0, r1 := m.s.UpdateChatRetryState(ctx, arg)
+ m.queryLatencies.WithLabelValues("UpdateChatRetryState").Observe(time.Since(start).Seconds())
+ m.queryCounts.WithLabelValues(httpmw.ExtractHTTPRoute(ctx), httpmw.ExtractHTTPMethod(ctx), "UpdateChatRetryState").Inc()
+ return r0, r1
+}
+
func (m queryMetricsStore) UpdateChatStatus(ctx context.Context, arg database.UpdateChatStatusParams) (database.Chat, error) {
start := time.Now()
r0, r1 := m.s.UpdateChatStatus(ctx, arg)
@@ -5849,6 +6049,22 @@ func (m queryMetricsStore) UpsertChatGeneralModelOverride(ctx context.Context, v
return r0
}
+func (m queryMetricsStore) UpsertChatHeartbeat(ctx context.Context, arg database.UpsertChatHeartbeatParams) error {
+ start := time.Now()
+ r0 := m.s.UpsertChatHeartbeat(ctx, arg)
+ m.queryLatencies.WithLabelValues("UpsertChatHeartbeat").Observe(time.Since(start).Seconds())
+ m.queryCounts.WithLabelValues(httpmw.ExtractHTTPRoute(ctx), httpmw.ExtractHTTPMethod(ctx), "UpsertChatHeartbeat").Inc()
+ return r0
+}
+
+func (m queryMetricsStore) UpsertChatHeartbeats(ctx context.Context, arg database.UpsertChatHeartbeatsParams) error {
+ start := time.Now()
+ r0 := m.s.UpsertChatHeartbeats(ctx, arg)
+ m.queryLatencies.WithLabelValues("UpsertChatHeartbeats").Observe(time.Since(start).Seconds())
+ m.queryCounts.WithLabelValues(httpmw.ExtractHTTPRoute(ctx), httpmw.ExtractHTTPMethod(ctx), "UpsertChatHeartbeats").Inc()
+ return r0
+}
+
func (m queryMetricsStore) UpsertChatIncludeDefaultSystemPrompt(ctx context.Context, includeDefaultSystemPrompt bool) error {
start := time.Now()
r0 := m.s.UpsertChatIncludeDefaultSystemPrompt(ctx, includeDefaultSystemPrompt)
diff --git a/coderd/database/dbmock/dbmock.go b/coderd/database/dbmock/dbmock.go
index 8321983028..97209c6bc1 100644
--- a/coderd/database/dbmock/dbmock.go
+++ b/coderd/database/dbmock/dbmock.go
@@ -498,6 +498,21 @@ func (mr *MockStoreMockRecorder) CountAuthorizedConnectionLogs(ctx, arg, prepare
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CountAuthorizedConnectionLogs", reflect.TypeOf((*MockStore)(nil).CountAuthorizedConnectionLogs), ctx, arg, prepared)
}
+// CountChatQueuedMessages mocks base method.
+func (m *MockStore) CountChatQueuedMessages(ctx context.Context, chatID uuid.UUID) (int64, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "CountChatQueuedMessages", ctx, chatID)
+ ret0, _ := ret[0].(int64)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// CountChatQueuedMessages indicates an expected call of CountChatQueuedMessages.
+func (mr *MockStoreMockRecorder) CountChatQueuedMessages(ctx, chatID any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CountChatQueuedMessages", reflect.TypeOf((*MockStore)(nil).CountChatQueuedMessages), ctx, chatID)
+}
+
// CountConnectionLogs mocks base method.
func (m *MockStore) CountConnectionLogs(ctx context.Context, arg database.CountConnectionLogsParams) (int64, error) {
m.ctrl.T.Helper()
@@ -674,6 +689,20 @@ func (mr *MockStoreMockRecorder) DeleteAPIKeysByUserID(ctx, userID any) *gomock.
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteAPIKeysByUserID", reflect.TypeOf((*MockStore)(nil).DeleteAPIKeysByUserID), ctx, userID)
}
+// DeleteAllChatHeartbeats mocks base method.
+func (m *MockStore) DeleteAllChatHeartbeats(ctx context.Context, chatID uuid.UUID) error {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "DeleteAllChatHeartbeats", ctx, chatID)
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+// DeleteAllChatHeartbeats indicates an expected call of DeleteAllChatHeartbeats.
+func (mr *MockStoreMockRecorder) DeleteAllChatHeartbeats(ctx, chatID any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteAllChatHeartbeats", reflect.TypeOf((*MockStore)(nil).DeleteAllChatHeartbeats), ctx, chatID)
+}
+
// DeleteAllChatQueuedMessages mocks base method.
func (m *MockStore) DeleteAllChatQueuedMessages(ctx context.Context, chatID uuid.UUID) error {
m.ctrl.T.Helper()
@@ -688,6 +717,21 @@ func (mr *MockStoreMockRecorder) DeleteAllChatQueuedMessages(ctx, chatID any) *g
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteAllChatQueuedMessages", reflect.TypeOf((*MockStore)(nil).DeleteAllChatQueuedMessages), ctx, chatID)
}
+// DeleteAllChatQueuedMessagesReturningCount mocks base method.
+func (m *MockStore) DeleteAllChatQueuedMessagesReturningCount(ctx context.Context, chatID uuid.UUID) (int64, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "DeleteAllChatQueuedMessagesReturningCount", ctx, chatID)
+ ret0, _ := ret[0].(int64)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// DeleteAllChatQueuedMessagesReturningCount indicates an expected call of DeleteAllChatQueuedMessagesReturningCount.
+func (mr *MockStoreMockRecorder) DeleteAllChatQueuedMessagesReturningCount(ctx, chatID any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteAllChatQueuedMessagesReturningCount", reflect.TypeOf((*MockStore)(nil).DeleteAllChatQueuedMessagesReturningCount), ctx, chatID)
+}
+
// DeleteAllTailnetTunnels mocks base method.
func (m *MockStore) DeleteAllTailnetTunnels(ctx context.Context, arg database.DeleteAllTailnetTunnelsParams) ([]database.DeleteAllTailnetTunnelsRow, error) {
m.ctrl.T.Helper()
@@ -761,6 +805,21 @@ func (mr *MockStoreMockRecorder) DeleteChatDebugDataByChatID(ctx, arg any) *gomo
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteChatDebugDataByChatID", reflect.TypeOf((*MockStore)(nil).DeleteChatDebugDataByChatID), ctx, arg)
}
+// DeleteChatHeartbeats mocks base method.
+func (m *MockStore) DeleteChatHeartbeats(ctx context.Context, arg database.DeleteChatHeartbeatsParams) (int64, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "DeleteChatHeartbeats", ctx, arg)
+ ret0, _ := ret[0].(int64)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// DeleteChatHeartbeats indicates an expected call of DeleteChatHeartbeats.
+func (mr *MockStoreMockRecorder) DeleteChatHeartbeats(ctx, arg any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteChatHeartbeats", reflect.TypeOf((*MockStore)(nil).DeleteChatHeartbeats), ctx, arg)
+}
+
// DeleteChatModelConfigByID mocks base method.
func (m *MockStore) DeleteChatModelConfigByID(ctx context.Context, id uuid.UUID) error {
m.ctrl.T.Helper()
@@ -817,6 +876,21 @@ func (mr *MockStoreMockRecorder) DeleteChatQueuedMessage(ctx, arg any) *gomock.C
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteChatQueuedMessage", reflect.TypeOf((*MockStore)(nil).DeleteChatQueuedMessage), ctx, arg)
}
+// DeleteChatQueuedMessageReturningCount mocks base method.
+func (m *MockStore) DeleteChatQueuedMessageReturningCount(ctx context.Context, arg database.DeleteChatQueuedMessageReturningCountParams) (int64, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "DeleteChatQueuedMessageReturningCount", ctx, arg)
+ ret0, _ := ret[0].(int64)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// DeleteChatQueuedMessageReturningCount indicates an expected call of DeleteChatQueuedMessageReturningCount.
+func (mr *MockStoreMockRecorder) DeleteChatQueuedMessageReturningCount(ctx, arg any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteChatQueuedMessageReturningCount", reflect.TypeOf((*MockStore)(nil).DeleteChatQueuedMessageReturningCount), ctx, arg)
+}
+
// DeleteChatUsageLimitGroupOverride mocks base method.
func (m *MockStore) DeleteChatUsageLimitGroupOverride(ctx context.Context, groupID uuid.UUID) error {
m.ctrl.T.Helper()
@@ -1319,6 +1393,21 @@ func (mr *MockStoreMockRecorder) DeleteRuntimeConfig(ctx, key any) *gomock.Call
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteRuntimeConfig", reflect.TypeOf((*MockStore)(nil).DeleteRuntimeConfig), ctx, key)
}
+// DeleteStaleChatHeartbeats mocks base method.
+func (m *MockStore) DeleteStaleChatHeartbeats(ctx context.Context, staleSeconds int32) (int64, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "DeleteStaleChatHeartbeats", ctx, staleSeconds)
+ ret0, _ := ret[0].(int64)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// DeleteStaleChatHeartbeats indicates an expected call of DeleteStaleChatHeartbeats.
+func (mr *MockStoreMockRecorder) DeleteStaleChatHeartbeats(ctx, staleSeconds any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteStaleChatHeartbeats", reflect.TypeOf((*MockStore)(nil).DeleteStaleChatHeartbeats), ctx, staleSeconds)
+}
+
// DeleteTailnetPeer mocks base method.
func (m *MockStore) DeleteTailnetPeer(ctx context.Context, arg database.DeleteTailnetPeerParams) (database.DeleteTailnetPeerRow, error) {
m.ctrl.T.Helper()
@@ -2355,6 +2444,21 @@ func (mr *MockStoreMockRecorder) GetAuthorizedWorkspacesAndAgentsByOwnerID(ctx,
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAuthorizedWorkspacesAndAgentsByOwnerID", reflect.TypeOf((*MockStore)(nil).GetAuthorizedWorkspacesAndAgentsByOwnerID), ctx, ownerID, prepared)
}
+// GetAutoArchiveInactiveChatCandidates mocks base method.
+func (m *MockStore) GetAutoArchiveInactiveChatCandidates(ctx context.Context, arg database.GetAutoArchiveInactiveChatCandidatesParams) ([]database.GetAutoArchiveInactiveChatCandidatesRow, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "GetAutoArchiveInactiveChatCandidates", ctx, arg)
+ ret0, _ := ret[0].([]database.GetAutoArchiveInactiveChatCandidatesRow)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// GetAutoArchiveInactiveChatCandidates indicates an expected call of GetAutoArchiveInactiveChatCandidates.
+func (mr *MockStoreMockRecorder) GetAutoArchiveInactiveChatCandidates(ctx, arg any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAutoArchiveInactiveChatCandidates", reflect.TypeOf((*MockStore)(nil).GetAutoArchiveInactiveChatCandidates), ctx, arg)
+}
+
// GetBoundaryLogByID mocks base method.
func (m *MockStore) GetBoundaryLogByID(ctx context.Context, id uuid.UUID) (database.BoundaryLog, error) {
m.ctrl.T.Helper()
@@ -2445,6 +2549,21 @@ func (mr *MockStoreMockRecorder) GetChatByID(ctx, id any) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetChatByID", reflect.TypeOf((*MockStore)(nil).GetChatByID), ctx, id)
}
+// GetChatByIDForShare mocks base method.
+func (m *MockStore) GetChatByIDForShare(ctx context.Context, id uuid.UUID) (database.Chat, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "GetChatByIDForShare", ctx, id)
+ ret0, _ := ret[0].(database.Chat)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// GetChatByIDForShare indicates an expected call of GetChatByIDForShare.
+func (mr *MockStoreMockRecorder) GetChatByIDForShare(ctx, id any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetChatByIDForShare", reflect.TypeOf((*MockStore)(nil).GetChatByIDForShare), ctx, id)
+}
+
// GetChatByIDForUpdate mocks base method.
func (m *MockStore) GetChatByIDForUpdate(ctx context.Context, id uuid.UUID) (database.Chat, error) {
m.ctrl.T.Helper()
@@ -2685,6 +2804,21 @@ func (mr *MockStoreMockRecorder) GetChatExploreModelOverride(ctx any) *gomock.Ca
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetChatExploreModelOverride", reflect.TypeOf((*MockStore)(nil).GetChatExploreModelOverride), ctx)
}
+// GetChatFamilyIDsByRootID mocks base method.
+func (m *MockStore) GetChatFamilyIDsByRootID(ctx context.Context, id uuid.UUID) ([]uuid.UUID, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "GetChatFamilyIDsByRootID", ctx, id)
+ ret0, _ := ret[0].([]uuid.UUID)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// GetChatFamilyIDsByRootID indicates an expected call of GetChatFamilyIDsByRootID.
+func (mr *MockStoreMockRecorder) GetChatFamilyIDsByRootID(ctx, id any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetChatFamilyIDsByRootID", reflect.TypeOf((*MockStore)(nil).GetChatFamilyIDsByRootID), ctx, id)
+}
+
// GetChatFileByID mocks base method.
func (m *MockStore) GetChatFileByID(ctx context.Context, id uuid.UUID) (database.ChatFile, error) {
m.ctrl.T.Helper()
@@ -2745,6 +2879,21 @@ func (mr *MockStoreMockRecorder) GetChatGeneralModelOverride(ctx any) *gomock.Ca
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetChatGeneralModelOverride", reflect.TypeOf((*MockStore)(nil).GetChatGeneralModelOverride), ctx)
}
+// GetChatHeartbeat mocks base method.
+func (m *MockStore) GetChatHeartbeat(ctx context.Context, arg database.GetChatHeartbeatParams) (database.ChatHeartbeat, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "GetChatHeartbeat", ctx, arg)
+ ret0, _ := ret[0].(database.ChatHeartbeat)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// GetChatHeartbeat indicates an expected call of GetChatHeartbeat.
+func (mr *MockStoreMockRecorder) GetChatHeartbeat(ctx, arg any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetChatHeartbeat", reflect.TypeOf((*MockStore)(nil).GetChatHeartbeat), ctx, arg)
+}
+
// GetChatIncludeDefaultSystemPrompt mocks base method.
func (m *MockStore) GetChatIncludeDefaultSystemPrompt(ctx context.Context) (bool, error) {
m.ctrl.T.Helper()
@@ -2835,6 +2984,21 @@ func (mr *MockStoreMockRecorder) GetChatMessagesByChatIDDescPaginated(ctx, arg a
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetChatMessagesByChatIDDescPaginated", reflect.TypeOf((*MockStore)(nil).GetChatMessagesByChatIDDescPaginated), ctx, arg)
}
+// GetChatMessagesByRevisionForStream mocks base method.
+func (m *MockStore) GetChatMessagesByRevisionForStream(ctx context.Context, arg database.GetChatMessagesByRevisionForStreamParams) ([]database.ChatMessage, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "GetChatMessagesByRevisionForStream", ctx, arg)
+ ret0, _ := ret[0].([]database.ChatMessage)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// GetChatMessagesByRevisionForStream indicates an expected call of GetChatMessagesByRevisionForStream.
+func (mr *MockStoreMockRecorder) GetChatMessagesByRevisionForStream(ctx, arg any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetChatMessagesByRevisionForStream", reflect.TypeOf((*MockStore)(nil).GetChatMessagesByRevisionForStream), ctx, arg)
+}
+
// GetChatMessagesForPromptByChatID mocks base method.
func (m *MockStore) GetChatMessagesForPromptByChatID(ctx context.Context, chatID uuid.UUID) ([]database.ChatMessage, error) {
m.ctrl.T.Helper()
@@ -2925,6 +3089,36 @@ func (mr *MockStoreMockRecorder) GetChatPlanModeInstructions(ctx any) *gomock.Ca
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetChatPlanModeInstructions", reflect.TypeOf((*MockStore)(nil).GetChatPlanModeInstructions), ctx)
}
+// GetChatQueuedMessageByID mocks base method.
+func (m *MockStore) GetChatQueuedMessageByID(ctx context.Context, arg database.GetChatQueuedMessageByIDParams) (database.ChatQueuedMessage, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "GetChatQueuedMessageByID", ctx, arg)
+ ret0, _ := ret[0].(database.ChatQueuedMessage)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// GetChatQueuedMessageByID indicates an expected call of GetChatQueuedMessageByID.
+func (mr *MockStoreMockRecorder) GetChatQueuedMessageByID(ctx, arg any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetChatQueuedMessageByID", reflect.TypeOf((*MockStore)(nil).GetChatQueuedMessageByID), ctx, arg)
+}
+
+// GetChatQueuedMessageHead mocks base method.
+func (m *MockStore) GetChatQueuedMessageHead(ctx context.Context, chatID uuid.UUID) (database.ChatQueuedMessage, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "GetChatQueuedMessageHead", ctx, chatID)
+ ret0, _ := ret[0].(database.ChatQueuedMessage)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// GetChatQueuedMessageHead indicates an expected call of GetChatQueuedMessageHead.
+func (mr *MockStoreMockRecorder) GetChatQueuedMessageHead(ctx, chatID any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetChatQueuedMessageHead", reflect.TypeOf((*MockStore)(nil).GetChatQueuedMessageHead), ctx, chatID)
+}
+
// GetChatQueuedMessages mocks base method.
func (m *MockStore) GetChatQueuedMessages(ctx context.Context, chatID uuid.UUID) ([]database.ChatQueuedMessage, error) {
m.ctrl.T.Helper()
@@ -2940,6 +3134,21 @@ func (mr *MockStoreMockRecorder) GetChatQueuedMessages(ctx, chatID any) *gomock.
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetChatQueuedMessages", reflect.TypeOf((*MockStore)(nil).GetChatQueuedMessages), ctx, chatID)
}
+// GetChatQueuedMessagesByPosition mocks base method.
+func (m *MockStore) GetChatQueuedMessagesByPosition(ctx context.Context, chatID uuid.UUID) ([]database.ChatQueuedMessage, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "GetChatQueuedMessagesByPosition", ctx, chatID)
+ ret0, _ := ret[0].([]database.ChatQueuedMessage)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// GetChatQueuedMessagesByPosition indicates an expected call of GetChatQueuedMessagesByPosition.
+func (mr *MockStoreMockRecorder) GetChatQueuedMessagesByPosition(ctx, chatID any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetChatQueuedMessagesByPosition", reflect.TypeOf((*MockStore)(nil).GetChatQueuedMessagesByPosition), ctx, chatID)
+}
+
// GetChatRetentionDays mocks base method.
func (m *MockStore) GetChatRetentionDays(ctx context.Context) (int32, error) {
m.ctrl.T.Helper()
@@ -2955,6 +3164,21 @@ func (mr *MockStoreMockRecorder) GetChatRetentionDays(ctx any) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetChatRetentionDays", reflect.TypeOf((*MockStore)(nil).GetChatRetentionDays), ctx)
}
+// GetChatStreamSyncRows mocks base method.
+func (m *MockStore) GetChatStreamSyncRows(ctx context.Context, ids []uuid.UUID) ([]database.GetChatStreamSyncRowsRow, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "GetChatStreamSyncRows", ctx, ids)
+ ret0, _ := ret[0].([]database.GetChatStreamSyncRowsRow)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// GetChatStreamSyncRows indicates an expected call of GetChatStreamSyncRows.
+func (mr *MockStoreMockRecorder) GetChatStreamSyncRows(ctx, ids any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetChatStreamSyncRows", reflect.TypeOf((*MockStore)(nil).GetChatStreamSyncRows), ctx, ids)
+}
+
// GetChatSystemPrompt mocks base method.
func (m *MockStore) GetChatSystemPrompt(ctx context.Context) (string, error) {
m.ctrl.T.Helper()
@@ -3075,6 +3299,21 @@ func (mr *MockStoreMockRecorder) GetChatUserPromptsByChatID(ctx, arg any) *gomoc
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetChatUserPromptsByChatID", reflect.TypeOf((*MockStore)(nil).GetChatUserPromptsByChatID), ctx, arg)
}
+// GetChatWorkerAcquisitionCandidates mocks base method.
+func (m *MockStore) GetChatWorkerAcquisitionCandidates(ctx context.Context, arg database.GetChatWorkerAcquisitionCandidatesParams) ([]database.GetChatWorkerAcquisitionCandidatesRow, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "GetChatWorkerAcquisitionCandidates", ctx, arg)
+ ret0, _ := ret[0].([]database.GetChatWorkerAcquisitionCandidatesRow)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// GetChatWorkerAcquisitionCandidates indicates an expected call of GetChatWorkerAcquisitionCandidates.
+func (mr *MockStoreMockRecorder) GetChatWorkerAcquisitionCandidates(ctx, arg any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetChatWorkerAcquisitionCandidates", reflect.TypeOf((*MockStore)(nil).GetChatWorkerAcquisitionCandidates), ctx, arg)
+}
+
// GetChatWorkspaceTTL mocks base method.
func (m *MockStore) GetChatWorkspaceTTL(ctx context.Context) (string, error) {
m.ctrl.T.Helper()
@@ -3120,6 +3359,21 @@ func (mr *MockStoreMockRecorder) GetChatsByChatFileID(ctx, fileID any) *gomock.C
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetChatsByChatFileID", reflect.TypeOf((*MockStore)(nil).GetChatsByChatFileID), ctx, fileID)
}
+// GetChatsByIDsForRunnerSync mocks base method.
+func (m *MockStore) GetChatsByIDsForRunnerSync(ctx context.Context, ids []uuid.UUID) ([]database.Chat, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "GetChatsByIDsForRunnerSync", ctx, ids)
+ ret0, _ := ret[0].([]database.Chat)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// GetChatsByIDsForRunnerSync indicates an expected call of GetChatsByIDsForRunnerSync.
+func (mr *MockStoreMockRecorder) GetChatsByIDsForRunnerSync(ctx, ids any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetChatsByIDsForRunnerSync", reflect.TypeOf((*MockStore)(nil).GetChatsByIDsForRunnerSync), ctx, ids)
+}
+
// GetChatsByWorkspaceIDs mocks base method.
func (m *MockStore) GetChatsByWorkspaceIDs(ctx context.Context, ids []uuid.UUID) ([]database.Chat, error) {
m.ctrl.T.Helper()
@@ -3255,6 +3509,21 @@ func (mr *MockStoreMockRecorder) GetDERPMeshKey(ctx any) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDERPMeshKey", reflect.TypeOf((*MockStore)(nil).GetDERPMeshKey), ctx)
}
+// GetDatabaseNow mocks base method.
+func (m *MockStore) GetDatabaseNow(ctx context.Context) (time.Time, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "GetDatabaseNow", ctx)
+ ret0, _ := ret[0].(time.Time)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// GetDatabaseNow indicates an expected call of GetDatabaseNow.
+func (mr *MockStoreMockRecorder) GetDatabaseNow(ctx any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDatabaseNow", reflect.TypeOf((*MockStore)(nil).GetDatabaseNow), ctx)
+}
+
// GetDefaultChatModelConfig mocks base method.
func (m *MockStore) GetDefaultChatModelConfig(ctx context.Context) (database.ChatModelConfig, error) {
m.ctrl.T.Helper()
@@ -6929,6 +7198,21 @@ func (mr *MockStoreMockRecorder) InTx(arg0, arg1 any) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InTx", reflect.TypeOf((*MockStore)(nil).InTx), arg0, arg1)
}
+// IncrementChatGenerationAttempt mocks base method.
+func (m *MockStore) IncrementChatGenerationAttempt(ctx context.Context, id uuid.UUID) (int64, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "IncrementChatGenerationAttempt", ctx, id)
+ ret0, _ := ret[0].(int64)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// IncrementChatGenerationAttempt indicates an expected call of IncrementChatGenerationAttempt.
+func (mr *MockStoreMockRecorder) IncrementChatGenerationAttempt(ctx, id any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IncrementChatGenerationAttempt", reflect.TypeOf((*MockStore)(nil).IncrementChatGenerationAttempt), ctx, id)
+}
+
// InsertAIBridgeInterception mocks base method.
func (m *MockStore) InsertAIBridgeInterception(ctx context.Context, arg database.InsertAIBridgeInterceptionParams) (database.AIBridgeInterception, error) {
m.ctrl.T.Helper()
@@ -7229,6 +7513,21 @@ func (mr *MockStoreMockRecorder) InsertChatQueuedMessage(ctx, arg any) *gomock.C
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertChatQueuedMessage", reflect.TypeOf((*MockStore)(nil).InsertChatQueuedMessage), ctx, arg)
}
+// InsertChatQueuedMessageWithCreator mocks base method.
+func (m *MockStore) InsertChatQueuedMessageWithCreator(ctx context.Context, arg database.InsertChatQueuedMessageWithCreatorParams) (database.ChatQueuedMessage, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "InsertChatQueuedMessageWithCreator", ctx, arg)
+ ret0, _ := ret[0].(database.ChatQueuedMessage)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// InsertChatQueuedMessageWithCreator indicates an expected call of InsertChatQueuedMessageWithCreator.
+func (mr *MockStoreMockRecorder) InsertChatQueuedMessageWithCreator(ctx, arg any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertChatQueuedMessageWithCreator", reflect.TypeOf((*MockStore)(nil).InsertChatQueuedMessageWithCreator), ctx, arg)
+}
+
// InsertCryptoKey mocks base method.
func (m *MockStore) InsertCryptoKey(ctx context.Context, arg database.InsertCryptoKeyParams) (database.CryptoKey, error) {
m.ctrl.T.Helper()
@@ -8144,6 +8443,21 @@ func (mr *MockStoreMockRecorder) InsertWorkspaceResourceMetadata(ctx, arg any) *
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertWorkspaceResourceMetadata", reflect.TypeOf((*MockStore)(nil).InsertWorkspaceResourceMetadata), ctx, arg)
}
+// IsChatHeartbeatStale mocks base method.
+func (m *MockStore) IsChatHeartbeatStale(ctx context.Context, arg database.IsChatHeartbeatStaleParams) (bool, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "IsChatHeartbeatStale", ctx, arg)
+ ret0, _ := ret[0].(bool)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// IsChatHeartbeatStale indicates an expected call of IsChatHeartbeatStale.
+func (mr *MockStoreMockRecorder) IsChatHeartbeatStale(ctx, arg any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsChatHeartbeatStale", reflect.TypeOf((*MockStore)(nil).IsChatHeartbeatStale), ctx, arg)
+}
+
// LinkChatFiles mocks base method.
func (m *MockStore) LinkChatFiles(ctx context.Context, arg database.LinkChatFilesParams) (int32, error) {
m.ctrl.T.Helper()
@@ -8579,6 +8893,21 @@ func (mr *MockStoreMockRecorder) ListWorkspaceAgentPortShares(ctx, workspaceID a
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListWorkspaceAgentPortShares", reflect.TypeOf((*MockStore)(nil).ListWorkspaceAgentPortShares), ctx, workspaceID)
}
+// LockChatAndBumpSnapshotVersion mocks base method.
+func (m *MockStore) LockChatAndBumpSnapshotVersion(ctx context.Context, id uuid.UUID) (database.Chat, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "LockChatAndBumpSnapshotVersion", ctx, id)
+ ret0, _ := ret[0].(database.Chat)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// LockChatAndBumpSnapshotVersion indicates an expected call of LockChatAndBumpSnapshotVersion.
+func (mr *MockStoreMockRecorder) LockChatAndBumpSnapshotVersion(ctx, id any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LockChatAndBumpSnapshotVersion", reflect.TypeOf((*MockStore)(nil).LockChatAndBumpSnapshotVersion), ctx, id)
+}
+
// MarkAllInboxNotificationsAsRead mocks base method.
func (m *MockStore) MarkAllInboxNotificationsAsRead(ctx context.Context, arg database.MarkAllInboxNotificationsAsReadParams) error {
m.ctrl.T.Helper()
@@ -8771,6 +9100,21 @@ func (mr *MockStoreMockRecorder) ReorderChatQueuedMessageToFront(ctx, arg any) *
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReorderChatQueuedMessageToFront", reflect.TypeOf((*MockStore)(nil).ReorderChatQueuedMessageToFront), ctx, arg)
}
+// ReorderChatQueuedMessageToHead mocks base method.
+func (m *MockStore) ReorderChatQueuedMessageToHead(ctx context.Context, arg database.ReorderChatQueuedMessageToHeadParams) (int64, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "ReorderChatQueuedMessageToHead", ctx, arg)
+ ret0, _ := ret[0].(int64)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// ReorderChatQueuedMessageToHead indicates an expected call of ReorderChatQueuedMessageToHead.
+func (mr *MockStoreMockRecorder) ReorderChatQueuedMessageToHead(ctx, arg any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReorderChatQueuedMessageToHead", reflect.TypeOf((*MockStore)(nil).ReorderChatQueuedMessageToHead), ctx, arg)
+}
+
// ResolveUserChatSpendLimit mocks base method.
func (m *MockStore) ResolveUserChatSpendLimit(ctx context.Context, arg database.ResolveUserChatSpendLimitParams) (database.ResolveUserChatSpendLimitRow, error) {
m.ctrl.T.Helper()
@@ -9117,6 +9461,21 @@ func (mr *MockStoreMockRecorder) UpdateChatDebugStep(ctx, arg any) *gomock.Call
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateChatDebugStep", reflect.TypeOf((*MockStore)(nil).UpdateChatDebugStep), ctx, arg)
}
+// UpdateChatExecutionState mocks base method.
+func (m *MockStore) UpdateChatExecutionState(ctx context.Context, arg database.UpdateChatExecutionStateParams) (database.Chat, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "UpdateChatExecutionState", ctx, arg)
+ ret0, _ := ret[0].(database.Chat)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// UpdateChatExecutionState indicates an expected call of UpdateChatExecutionState.
+func (mr *MockStoreMockRecorder) UpdateChatExecutionState(ctx, arg any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateChatExecutionState", reflect.TypeOf((*MockStore)(nil).UpdateChatExecutionState), ctx, arg)
+}
+
// UpdateChatHeartbeats mocks base method.
func (m *MockStore) UpdateChatHeartbeats(ctx context.Context, arg database.UpdateChatHeartbeatsParams) ([]uuid.UUID, error) {
m.ctrl.T.Helper()
@@ -9280,6 +9639,21 @@ func (mr *MockStoreMockRecorder) UpdateChatPlanModeByID(ctx, arg any) *gomock.Ca
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateChatPlanModeByID", reflect.TypeOf((*MockStore)(nil).UpdateChatPlanModeByID), ctx, arg)
}
+// UpdateChatRetryState mocks base method.
+func (m *MockStore) UpdateChatRetryState(ctx context.Context, arg database.UpdateChatRetryStateParams) (database.Chat, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "UpdateChatRetryState", ctx, arg)
+ ret0, _ := ret[0].(database.Chat)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// UpdateChatRetryState indicates an expected call of UpdateChatRetryState.
+func (mr *MockStoreMockRecorder) UpdateChatRetryState(ctx, arg any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateChatRetryState", reflect.TypeOf((*MockStore)(nil).UpdateChatRetryState), ctx, arg)
+}
+
// UpdateChatStatus mocks base method.
func (m *MockStore) UpdateChatStatus(ctx context.Context, arg database.UpdateChatStatusParams) (database.Chat, error) {
m.ctrl.T.Helper()
@@ -10989,6 +11363,34 @@ func (mr *MockStoreMockRecorder) UpsertChatGeneralModelOverride(ctx, value any)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpsertChatGeneralModelOverride", reflect.TypeOf((*MockStore)(nil).UpsertChatGeneralModelOverride), ctx, value)
}
+// UpsertChatHeartbeat mocks base method.
+func (m *MockStore) UpsertChatHeartbeat(ctx context.Context, arg database.UpsertChatHeartbeatParams) error {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "UpsertChatHeartbeat", ctx, arg)
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+// UpsertChatHeartbeat indicates an expected call of UpsertChatHeartbeat.
+func (mr *MockStoreMockRecorder) UpsertChatHeartbeat(ctx, arg any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpsertChatHeartbeat", reflect.TypeOf((*MockStore)(nil).UpsertChatHeartbeat), ctx, arg)
+}
+
+// UpsertChatHeartbeats mocks base method.
+func (m *MockStore) UpsertChatHeartbeats(ctx context.Context, arg database.UpsertChatHeartbeatsParams) error {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "UpsertChatHeartbeats", ctx, arg)
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+// UpsertChatHeartbeats indicates an expected call of UpsertChatHeartbeats.
+func (mr *MockStoreMockRecorder) UpsertChatHeartbeats(ctx, arg any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpsertChatHeartbeats", reflect.TypeOf((*MockStore)(nil).UpsertChatHeartbeats), ctx, arg)
+}
+
// UpsertChatIncludeDefaultSystemPrompt mocks base method.
func (m *MockStore) UpsertChatIncludeDefaultSystemPrompt(ctx context.Context, includeDefaultSystemPrompt bool) error {
m.ctrl.T.Helper()
diff --git a/coderd/database/dump.sql b/coderd/database/dump.sql
index ea09f4300f..35c04ab5c1 100644
--- a/coderd/database/dump.sql
+++ b/coderd/database/dump.sql
@@ -341,7 +341,8 @@ CREATE TYPE chat_status AS ENUM (
'paused',
'completed',
'error',
- 'requires_action'
+ 'requires_action',
+ 'interrupting'
);
CREATE TYPE connection_status AS ENUM (
@@ -715,6 +716,29 @@ BEGIN
END;
$$;
+CREATE FUNCTION bump_chat_queue_version_on_queued_message_change() RETURNS trigger
+ LANGUAGE plpgsql
+ AS $$
+DECLARE
+ changed_chat_id uuid;
+BEGIN
+ IF TG_OP = 'DELETE' THEN
+ changed_chat_id = OLD.chat_id;
+ ELSE
+ changed_chat_id = NEW.chat_id;
+ END IF;
+
+ UPDATE chats
+ SET queue_version = snapshot_version
+ WHERE id = changed_chat_id;
+
+ IF TG_OP = 'DELETE' THEN
+ RETURN OLD;
+ END IF;
+ RETURN NEW;
+END;
+$$;
+
CREATE FUNCTION check_workspace_agent_name_unique() RETURNS trigger
LANGUAGE plpgsql
AS $$
@@ -1292,6 +1316,103 @@ BEGIN
END;
$$;
+CREATE FUNCTION set_chat_message_revision_before() RETURNS trigger
+ LANGUAGE plpgsql
+ AS $$
+DECLARE
+ chat_snapshot_version bigint;
+BEGIN
+ IF TG_OP = 'INSERT' AND NEW.revision IS NOT NULL THEN
+ RAISE EXCEPTION 'chat_messages.revision must be assigned by trigger';
+ END IF;
+
+ IF TG_OP = 'UPDATE' THEN
+ IF OLD.chat_id IS DISTINCT FROM NEW.chat_id THEN
+ RAISE EXCEPTION 'chat_messages.chat_id is immutable';
+ END IF;
+
+ IF OLD.revision IS DISTINCT FROM NEW.revision THEN
+ RAISE EXCEPTION 'chat_messages.revision must be assigned by trigger';
+ END IF;
+
+ IF OLD IS NOT DISTINCT FROM NEW THEN
+ RETURN NEW;
+ END IF;
+ END IF;
+
+ SELECT snapshot_version INTO chat_snapshot_version
+ FROM chats WHERE id = NEW.chat_id;
+
+ IF chat_snapshot_version IS NULL THEN
+ RAISE EXCEPTION 'chat % does not exist', NEW.chat_id;
+ END IF;
+
+ NEW.revision = chat_snapshot_version;
+ RETURN NEW;
+END;
+$$;
+
+CREATE FUNCTION sync_chat_retry_state() RETURNS trigger
+ LANGUAGE plpgsql
+ AS $$
+BEGIN
+ IF OLD.retry_state_version IS DISTINCT FROM NEW.retry_state_version THEN
+ RAISE EXCEPTION 'chats.retry_state_version must be assigned by trigger';
+ END IF;
+
+ IF NEW.generation_attempt IS DISTINCT FROM OLD.generation_attempt THEN
+ NEW.retry_state = NULL;
+ END IF;
+
+ IF NEW.retry_state IS DISTINCT FROM OLD.retry_state THEN
+ NEW.retry_state_version = NEW.snapshot_version;
+ END IF;
+
+ RETURN NEW;
+END;
+$$;
+
+CREATE FUNCTION update_chat_history_after_message_insert() RETURNS trigger
+ LANGUAGE plpgsql
+ AS $$
+BEGIN
+ UPDATE chats c
+ SET history_version = c.snapshot_version,
+ generation_attempt = 0
+ FROM (
+ SELECT DISTINCT chat_id FROM chat_message_history_new_rows
+ ) AS affected
+ WHERE c.id = affected.chat_id
+ AND (
+ c.history_version IS DISTINCT FROM c.snapshot_version
+ OR c.generation_attempt <> 0
+ );
+ RETURN NULL;
+END;
+$$;
+
+CREATE FUNCTION update_chat_history_after_message_update() RETURNS trigger
+ LANGUAGE plpgsql
+ AS $$
+BEGIN
+ UPDATE chats c
+ SET history_version = c.snapshot_version,
+ generation_attempt = 0
+ FROM (
+ SELECT DISTINCT n.chat_id
+ FROM chat_message_history_new_rows n
+ JOIN chat_message_history_old_rows o ON o.id = n.id
+ WHERE o IS DISTINCT FROM n
+ ) AS affected
+ WHERE c.id = affected.chat_id
+ AND (
+ c.history_version IS DISTINCT FROM c.snapshot_version
+ OR c.generation_attempt <> 0
+ );
+ RETURN NULL;
+END;
+$$;
+
CREATE TABLE ai_gateway_keys (
id uuid NOT NULL,
created_at timestamp with time zone NOT NULL,
@@ -1669,6 +1790,12 @@ CREATE TABLE chat_files (
data bytea NOT NULL
);
+CREATE UNLOGGED TABLE chat_heartbeats (
+ chat_id uuid NOT NULL,
+ runner_id uuid NOT NULL,
+ heartbeat_at timestamp with time zone NOT NULL
+);
+
CREATE TABLE chat_messages (
id bigint NOT NULL,
chat_id uuid NOT NULL,
@@ -1691,7 +1818,8 @@ CREATE TABLE chat_messages (
runtime_ms bigint,
deleted boolean DEFAULT false NOT NULL,
provider_response_id text,
- api_key_id text
+ api_key_id text,
+ revision bigint NOT NULL
);
CREATE SEQUENCE chat_messages_id_seq
@@ -1725,13 +1853,22 @@ CREATE TABLE chat_model_configs (
CONSTRAINT chat_model_configs_context_limit_check CHECK ((context_limit > 0))
);
+CREATE SEQUENCE chat_queued_messages_position_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
CREATE TABLE chat_queued_messages (
id bigint NOT NULL,
chat_id uuid NOT NULL,
content jsonb NOT NULL,
created_at timestamp with time zone DEFAULT now() NOT NULL,
model_config_id uuid,
- api_key_id text
+ api_key_id text,
+ "position" bigint DEFAULT nextval('chat_queued_messages_position_seq'::regclass) NOT NULL,
+ created_by uuid NOT NULL
);
CREATE SEQUENCE chat_queued_messages_id_seq
@@ -1796,6 +1933,14 @@ CREATE TABLE chats (
last_turn_summary text,
user_acl jsonb DEFAULT '{}'::jsonb NOT NULL,
group_acl jsonb DEFAULT '{}'::jsonb NOT NULL,
+ snapshot_version bigint DEFAULT 1 NOT NULL,
+ history_version bigint DEFAULT 0 NOT NULL,
+ queue_version bigint DEFAULT 0 NOT NULL,
+ generation_attempt bigint DEFAULT 0 NOT NULL,
+ retry_state jsonb,
+ retry_state_version bigint DEFAULT 0 NOT NULL,
+ runner_id uuid,
+ requires_action_deadline_at timestamp with time zone,
CONSTRAINT chat_acl_only_on_root_chats CHECK ((((parent_chat_id IS NULL) AND (root_chat_id IS NULL)) OR ((user_acl = '{}'::jsonb) AND (group_acl = '{}'::jsonb)))),
CONSTRAINT chat_group_acl_not_null_jsonb CHECK (((group_acl IS NOT NULL) AND (jsonb_typeof(group_acl) = 'object'::text))),
CONSTRAINT chat_user_acl_not_null_jsonb CHECK (((user_acl IS NOT NULL) AND (jsonb_typeof(user_acl) = 'object'::text))),
@@ -1883,6 +2028,14 @@ CREATE VIEW chats_expanded AS
c.plan_mode,
c.client_type,
c.last_turn_summary,
+ c.snapshot_version,
+ c.history_version,
+ c.queue_version,
+ c.generation_attempt,
+ c.retry_state,
+ c.retry_state_version,
+ c.runner_id,
+ c.requires_action_deadline_at,
COALESCE(root.user_acl, c.user_acl) AS user_acl,
COALESCE(root.group_acl, c.group_acl) AS group_acl,
owner.username AS owner_username,
@@ -3844,6 +3997,9 @@ ALTER TABLE ONLY chat_file_links
ALTER TABLE ONLY chat_files
ADD CONSTRAINT chat_files_pkey PRIMARY KEY (id);
+ALTER TABLE ONLY chat_heartbeats
+ ADD CONSTRAINT chat_heartbeats_pkey PRIMARY KEY (chat_id, runner_id);
+
ALTER TABLE ONLY chat_messages
ADD CONSTRAINT chat_messages_pkey PRIMARY KEY (id);
@@ -4186,6 +4342,8 @@ CREATE INDEX api_keys_last_used_idx ON api_keys USING btree (last_used DESC);
COMMENT ON INDEX api_keys_last_used_idx IS 'Index for optimizing api_keys queries filtering by last_used';
+CREATE INDEX chat_heartbeats_heartbeat_at_idx ON chat_heartbeats USING btree (heartbeat_at);
+
CREATE INDEX idx_agent_stats_created_at ON workspace_agent_stats USING btree (created_at);
CREATE INDEX idx_agent_stats_user_id ON workspace_agent_stats USING btree (user_id);
@@ -4298,6 +4456,8 @@ CREATE UNIQUE INDEX idx_chat_model_configs_single_default ON chat_model_configs
CREATE INDEX idx_chat_queued_messages_chat_id ON chat_queued_messages USING btree (chat_id);
+CREATE INDEX idx_chat_queued_messages_chat_position_id ON chat_queued_messages USING btree (chat_id, "position", id);
+
CREATE INDEX idx_chats_agent_id ON chats USING btree (agent_id) WHERE (agent_id IS NOT NULL);
CREATE INDEX idx_chats_auto_archive_candidates ON chats USING btree (created_at) WHERE ((archived = false) AND (pin_order = 0) AND (parent_chat_id IS NULL));
@@ -4546,6 +4706,12 @@ COMMENT ON TRIGGER remove_organization_member_custom_role ON custom_roles IS 'Wh
CREATE TRIGGER trigger_aggregate_usage_event AFTER INSERT ON usage_events FOR EACH ROW EXECUTE FUNCTION aggregate_usage_event();
+CREATE TRIGGER trigger_bump_chat_queue_version_on_queued_message_delete AFTER DELETE ON chat_queued_messages FOR EACH ROW EXECUTE FUNCTION bump_chat_queue_version_on_queued_message_change();
+
+CREATE TRIGGER trigger_bump_chat_queue_version_on_queued_message_insert AFTER INSERT ON chat_queued_messages FOR EACH ROW EXECUTE FUNCTION bump_chat_queue_version_on_queued_message_change();
+
+CREATE TRIGGER trigger_bump_chat_queue_version_on_queued_message_update AFTER UPDATE OF content, model_config_id, "position", created_by ON chat_queued_messages FOR EACH ROW EXECUTE FUNCTION bump_chat_queue_version_on_queued_message_change();
+
CREATE TRIGGER trigger_delete_group_members_on_org_member_delete BEFORE DELETE ON organization_members FOR EACH ROW EXECUTE FUNCTION delete_group_members_on_org_member_delete();
CREATE TRIGGER trigger_delete_oauth2_provider_app_token AFTER DELETE ON oauth2_provider_app_tokens FOR EACH ROW EXECUTE FUNCTION delete_deleted_oauth2_provider_app_token_api_key();
@@ -4562,6 +4728,16 @@ CREATE TRIGGER trigger_insert_organization_system_roles AFTER INSERT ON organiza
CREATE TRIGGER trigger_nullify_next_start_at_on_workspace_autostart_modificati AFTER UPDATE ON workspaces FOR EACH ROW EXECUTE FUNCTION nullify_next_start_at_on_workspace_autostart_modification();
+CREATE TRIGGER trigger_set_chat_message_revision_on_insert BEFORE INSERT ON chat_messages FOR EACH ROW EXECUTE FUNCTION set_chat_message_revision_before();
+
+CREATE TRIGGER trigger_set_chat_message_revision_on_update BEFORE UPDATE ON chat_messages FOR EACH ROW EXECUTE FUNCTION set_chat_message_revision_before();
+
+CREATE TRIGGER trigger_sync_chat_retry_state BEFORE UPDATE OF retry_state, retry_state_version, generation_attempt ON chats FOR EACH ROW EXECUTE FUNCTION sync_chat_retry_state();
+
+CREATE TRIGGER trigger_update_chat_history_after_message_insert AFTER INSERT ON chat_messages REFERENCING NEW TABLE AS chat_message_history_new_rows FOR EACH STATEMENT EXECUTE FUNCTION update_chat_history_after_message_insert();
+
+CREATE TRIGGER trigger_update_chat_history_after_message_update AFTER UPDATE ON chat_messages REFERENCING OLD TABLE AS chat_message_history_old_rows NEW TABLE AS chat_message_history_new_rows FOR EACH STATEMENT EXECUTE FUNCTION update_chat_history_after_message_update();
+
CREATE TRIGGER trigger_update_users AFTER INSERT OR UPDATE ON users FOR EACH ROW WHEN ((new.deleted = true)) EXECUTE FUNCTION delete_deleted_user_resources();
CREATE TRIGGER trigger_upsert_user_links BEFORE INSERT OR UPDATE ON user_links FOR EACH ROW EXECUTE FUNCTION insert_user_links_fail_if_user_deleted();
@@ -4632,6 +4808,9 @@ ALTER TABLE ONLY chat_files
ALTER TABLE ONLY chat_files
ADD CONSTRAINT chat_files_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES users(id) ON DELETE CASCADE;
+ALTER TABLE ONLY chat_heartbeats
+ ADD CONSTRAINT chat_heartbeats_chat_id_fkey FOREIGN KEY (chat_id) REFERENCES chats(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY chat_messages
ADD CONSTRAINT chat_messages_api_key_id_fkey FOREIGN KEY (api_key_id) REFERENCES api_keys(id) ON DELETE SET NULL;
diff --git a/coderd/database/foreign_key_constraint.go b/coderd/database/foreign_key_constraint.go
index 8109f2564f..159040d142 100644
--- a/coderd/database/foreign_key_constraint.go
+++ b/coderd/database/foreign_key_constraint.go
@@ -22,6 +22,7 @@ const (
ForeignKeyChatFileLinksFileID ForeignKeyConstraint = "chat_file_links_file_id_fkey" // ALTER TABLE ONLY chat_file_links ADD CONSTRAINT chat_file_links_file_id_fkey FOREIGN KEY (file_id) REFERENCES chat_files(id) ON DELETE CASCADE;
ForeignKeyChatFilesOrganizationID ForeignKeyConstraint = "chat_files_organization_id_fkey" // ALTER TABLE ONLY chat_files ADD CONSTRAINT chat_files_organization_id_fkey FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE;
ForeignKeyChatFilesOwnerID ForeignKeyConstraint = "chat_files_owner_id_fkey" // ALTER TABLE ONLY chat_files ADD CONSTRAINT chat_files_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES users(id) ON DELETE CASCADE;
+ ForeignKeyChatHeartbeatsChatID ForeignKeyConstraint = "chat_heartbeats_chat_id_fkey" // ALTER TABLE ONLY chat_heartbeats ADD CONSTRAINT chat_heartbeats_chat_id_fkey FOREIGN KEY (chat_id) REFERENCES chats(id) ON DELETE CASCADE;
ForeignKeyChatMessagesAPIKeyID ForeignKeyConstraint = "chat_messages_api_key_id_fkey" // ALTER TABLE ONLY chat_messages ADD CONSTRAINT chat_messages_api_key_id_fkey FOREIGN KEY (api_key_id) REFERENCES api_keys(id) ON DELETE SET NULL;
ForeignKeyChatMessagesChatID ForeignKeyConstraint = "chat_messages_chat_id_fkey" // ALTER TABLE ONLY chat_messages ADD CONSTRAINT chat_messages_chat_id_fkey FOREIGN KEY (chat_id) REFERENCES chats(id) ON DELETE CASCADE;
ForeignKeyChatMessagesModelConfigID ForeignKeyConstraint = "chat_messages_model_config_id_fkey" // ALTER TABLE ONLY chat_messages ADD CONSTRAINT chat_messages_model_config_id_fkey FOREIGN KEY (model_config_id) REFERENCES chat_model_configs(id);
diff --git a/coderd/database/models.go b/coderd/database/models.go
index 8a6aa5fd4d..3d93e54aa8 100644
--- a/coderd/database/models.go
+++ b/coderd/database/models.go
@@ -1567,6 +1567,7 @@ const (
ChatStatusCompleted ChatStatus = "completed"
ChatStatusError ChatStatus = "error"
ChatStatusRequiresAction ChatStatus = "requires_action"
+ ChatStatusInterrupting ChatStatus = "interrupting"
)
func (e *ChatStatus) Scan(src interface{}) error {
@@ -1612,7 +1613,8 @@ func (e ChatStatus) Valid() bool {
ChatStatusPaused,
ChatStatusCompleted,
ChatStatusError,
- ChatStatusRequiresAction:
+ ChatStatusRequiresAction,
+ ChatStatusInterrupting:
return true
}
return false
@@ -1627,6 +1629,7 @@ func AllChatStatusValues() []ChatStatus {
ChatStatusCompleted,
ChatStatusError,
ChatStatusRequiresAction,
+ ChatStatusInterrupting,
}
}
@@ -4604,38 +4607,46 @@ type BoundaryUsageStat struct {
}
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"`
- Title string `db:"title" json:"title"`
- Status ChatStatus `db:"status" json:"status"`
- WorkerID uuid.NullUUID `db:"worker_id" json:"worker_id"`
- StartedAt sql.NullTime `db:"started_at" json:"started_at"`
- HeartbeatAt sql.NullTime `db:"heartbeat_at" json:"heartbeat_at"`
- CreatedAt time.Time `db:"created_at" json:"created_at"`
- UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
- 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"`
- Archived bool `db:"archived" json:"archived"`
- LastError pqtype.NullRawMessage `db:"last_error" json:"last_error"`
- Mode NullChatMode `db:"mode" json:"mode"`
- MCPServerIDs []uuid.UUID `db:"mcp_server_ids" json:"mcp_server_ids"`
- Labels StringMap `db:"labels" json:"labels"`
- BuildID uuid.NullUUID `db:"build_id" json:"build_id"`
- AgentID uuid.NullUUID `db:"agent_id" json:"agent_id"`
- PinOrder int32 `db:"pin_order" json:"pin_order"`
- LastReadMessageID sql.NullInt64 `db:"last_read_message_id" json:"last_read_message_id"`
- LastInjectedContext pqtype.NullRawMessage `db:"last_injected_context" json:"last_injected_context"`
- DynamicTools pqtype.NullRawMessage `db:"dynamic_tools" json:"dynamic_tools"`
- OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"`
- PlanMode NullChatPlanMode `db:"plan_mode" json:"plan_mode"`
- ClientType ChatClientType `db:"client_type" json:"client_type"`
- LastTurnSummary sql.NullString `db:"last_turn_summary" json:"last_turn_summary"`
- UserACL ChatACL `db:"user_acl" json:"user_acl"`
- GroupACL ChatACL `db:"group_acl" json:"group_acl"`
- OwnerUsername string `db:"owner_username" json:"owner_username"`
- OwnerName string `db:"owner_name" json:"owner_name"`
+ 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"`
+ Title string `db:"title" json:"title"`
+ Status ChatStatus `db:"status" json:"status"`
+ WorkerID uuid.NullUUID `db:"worker_id" json:"worker_id"`
+ StartedAt sql.NullTime `db:"started_at" json:"started_at"`
+ HeartbeatAt sql.NullTime `db:"heartbeat_at" json:"heartbeat_at"`
+ CreatedAt time.Time `db:"created_at" json:"created_at"`
+ UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
+ 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"`
+ Archived bool `db:"archived" json:"archived"`
+ LastError pqtype.NullRawMessage `db:"last_error" json:"last_error"`
+ Mode NullChatMode `db:"mode" json:"mode"`
+ MCPServerIDs []uuid.UUID `db:"mcp_server_ids" json:"mcp_server_ids"`
+ Labels StringMap `db:"labels" json:"labels"`
+ BuildID uuid.NullUUID `db:"build_id" json:"build_id"`
+ AgentID uuid.NullUUID `db:"agent_id" json:"agent_id"`
+ PinOrder int32 `db:"pin_order" json:"pin_order"`
+ LastReadMessageID sql.NullInt64 `db:"last_read_message_id" json:"last_read_message_id"`
+ LastInjectedContext pqtype.NullRawMessage `db:"last_injected_context" json:"last_injected_context"`
+ DynamicTools pqtype.NullRawMessage `db:"dynamic_tools" json:"dynamic_tools"`
+ OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"`
+ PlanMode NullChatPlanMode `db:"plan_mode" json:"plan_mode"`
+ ClientType ChatClientType `db:"client_type" json:"client_type"`
+ LastTurnSummary sql.NullString `db:"last_turn_summary" json:"last_turn_summary"`
+ SnapshotVersion int64 `db:"snapshot_version" json:"snapshot_version"`
+ HistoryVersion int64 `db:"history_version" json:"history_version"`
+ QueueVersion int64 `db:"queue_version" json:"queue_version"`
+ GenerationAttempt int64 `db:"generation_attempt" json:"generation_attempt"`
+ RetryState pqtype.NullRawMessage `db:"retry_state" json:"retry_state"`
+ RetryStateVersion int64 `db:"retry_state_version" json:"retry_state_version"`
+ RunnerID uuid.NullUUID `db:"runner_id" json:"runner_id"`
+ RequiresActionDeadlineAt sql.NullTime `db:"requires_action_deadline_at" json:"requires_action_deadline_at"`
+ UserACL ChatACL `db:"user_acl" json:"user_acl"`
+ GroupACL ChatACL `db:"group_acl" json:"group_acl"`
+ OwnerUsername string `db:"owner_username" json:"owner_username"`
+ OwnerName string `db:"owner_name" json:"owner_name"`
}
type ChatDebugRun struct {
@@ -4717,6 +4728,12 @@ type ChatFileLink struct {
FileID uuid.UUID `db:"file_id" json:"file_id"`
}
+type ChatHeartbeat struct {
+ ChatID uuid.UUID `db:"chat_id" json:"chat_id"`
+ RunnerID uuid.UUID `db:"runner_id" json:"runner_id"`
+ HeartbeatAt time.Time `db:"heartbeat_at" json:"heartbeat_at"`
+}
+
type ChatMessage struct {
ID int64 `db:"id" json:"id"`
ChatID uuid.UUID `db:"chat_id" json:"chat_id"`
@@ -4740,6 +4757,7 @@ type ChatMessage struct {
Deleted bool `db:"deleted" json:"deleted"`
ProviderResponseID sql.NullString `db:"provider_response_id" json:"provider_response_id"`
APIKeyID sql.NullString `db:"api_key_id" json:"api_key_id"`
+ Revision int64 `db:"revision" json:"revision"`
}
type ChatModelConfig struct {
@@ -4768,39 +4786,49 @@ type ChatQueuedMessage struct {
CreatedAt time.Time `db:"created_at" json:"created_at"`
ModelConfigID uuid.NullUUID `db:"model_config_id" json:"model_config_id"`
APIKeyID sql.NullString `db:"api_key_id" json:"api_key_id"`
+ Position int64 `db:"position" json:"position"`
+ CreatedBy uuid.UUID `db:"created_by" json:"created_by"`
}
type ChatTable 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"`
- Title string `db:"title" json:"title"`
- Status ChatStatus `db:"status" json:"status"`
- WorkerID uuid.NullUUID `db:"worker_id" json:"worker_id"`
- StartedAt sql.NullTime `db:"started_at" json:"started_at"`
- HeartbeatAt sql.NullTime `db:"heartbeat_at" json:"heartbeat_at"`
- CreatedAt time.Time `db:"created_at" json:"created_at"`
- UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
- 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"`
- Archived bool `db:"archived" json:"archived"`
- LastError pqtype.NullRawMessage `db:"last_error" json:"last_error"`
- Mode NullChatMode `db:"mode" json:"mode"`
- MCPServerIDs []uuid.UUID `db:"mcp_server_ids" json:"mcp_server_ids"`
- Labels StringMap `db:"labels" json:"labels"`
- BuildID uuid.NullUUID `db:"build_id" json:"build_id"`
- AgentID uuid.NullUUID `db:"agent_id" json:"agent_id"`
- PinOrder int32 `db:"pin_order" json:"pin_order"`
- LastReadMessageID sql.NullInt64 `db:"last_read_message_id" json:"last_read_message_id"`
- LastInjectedContext pqtype.NullRawMessage `db:"last_injected_context" json:"last_injected_context"`
- DynamicTools pqtype.NullRawMessage `db:"dynamic_tools" json:"dynamic_tools"`
- OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"`
- PlanMode NullChatPlanMode `db:"plan_mode" json:"plan_mode"`
- ClientType ChatClientType `db:"client_type" json:"client_type"`
- LastTurnSummary sql.NullString `db:"last_turn_summary" json:"last_turn_summary"`
- UserACL ChatACL `db:"user_acl" json:"user_acl"`
- GroupACL ChatACL `db:"group_acl" json:"group_acl"`
+ 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"`
+ Title string `db:"title" json:"title"`
+ Status ChatStatus `db:"status" json:"status"`
+ WorkerID uuid.NullUUID `db:"worker_id" json:"worker_id"`
+ StartedAt sql.NullTime `db:"started_at" json:"started_at"`
+ HeartbeatAt sql.NullTime `db:"heartbeat_at" json:"heartbeat_at"`
+ CreatedAt time.Time `db:"created_at" json:"created_at"`
+ UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
+ 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"`
+ Archived bool `db:"archived" json:"archived"`
+ LastError pqtype.NullRawMessage `db:"last_error" json:"last_error"`
+ Mode NullChatMode `db:"mode" json:"mode"`
+ MCPServerIDs []uuid.UUID `db:"mcp_server_ids" json:"mcp_server_ids"`
+ Labels StringMap `db:"labels" json:"labels"`
+ BuildID uuid.NullUUID `db:"build_id" json:"build_id"`
+ AgentID uuid.NullUUID `db:"agent_id" json:"agent_id"`
+ PinOrder int32 `db:"pin_order" json:"pin_order"`
+ LastReadMessageID sql.NullInt64 `db:"last_read_message_id" json:"last_read_message_id"`
+ LastInjectedContext pqtype.NullRawMessage `db:"last_injected_context" json:"last_injected_context"`
+ DynamicTools pqtype.NullRawMessage `db:"dynamic_tools" json:"dynamic_tools"`
+ OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"`
+ PlanMode NullChatPlanMode `db:"plan_mode" json:"plan_mode"`
+ ClientType ChatClientType `db:"client_type" json:"client_type"`
+ LastTurnSummary sql.NullString `db:"last_turn_summary" json:"last_turn_summary"`
+ UserACL ChatACL `db:"user_acl" json:"user_acl"`
+ GroupACL ChatACL `db:"group_acl" json:"group_acl"`
+ SnapshotVersion int64 `db:"snapshot_version" json:"snapshot_version"`
+ HistoryVersion int64 `db:"history_version" json:"history_version"`
+ QueueVersion int64 `db:"queue_version" json:"queue_version"`
+ GenerationAttempt int64 `db:"generation_attempt" json:"generation_attempt"`
+ RetryState pqtype.NullRawMessage `db:"retry_state" json:"retry_state"`
+ RetryStateVersion int64 `db:"retry_state_version" json:"retry_state_version"`
+ RunnerID uuid.NullUUID `db:"runner_id" json:"runner_id"`
+ RequiresActionDeadlineAt sql.NullTime `db:"requires_action_deadline_at" json:"requires_action_deadline_at"`
}
type ChatUsageLimitConfig struct {
diff --git a/coderd/database/querier.go b/coderd/database/querier.go
index 4b9fa58e01..5b616f653f 100644
--- a/coderd/database/querier.go
+++ b/coderd/database/querier.go
@@ -89,6 +89,9 @@ type sqlcQuerier interface {
CountAIBridgeInterceptions(ctx context.Context, arg CountAIBridgeInterceptionsParams) (int64, error)
CountAIBridgeSessions(ctx context.Context, arg CountAIBridgeSessionsParams) (int64, error)
CountAuditLogs(ctx context.Context, arg CountAuditLogsParams) (int64, error)
+ // Cheap queue-length check used by ChatMachine.Update when deciding
+ // whether the chat is in a "1" sub-state.
+ CountChatQueuedMessages(ctx context.Context, chatID uuid.UUID) (int64, error)
CountConnectionLogs(ctx context.Context, arg CountConnectionLogsParams) (int64, error)
// Counts enabled, non-deleted model configs that lack both input and
// output pricing in their JSONB options.cost configuration.
@@ -106,7 +109,11 @@ type sqlcQuerier interface {
DeleteAIProviderKey(ctx context.Context, id uuid.UUID) error
DeleteAPIKeyByID(ctx context.Context, id string) error
DeleteAPIKeysByUserID(ctx context.Context, userID uuid.UUID) error
+ // Deletes all heartbeat rows for the chat. Used during ownership
+ // transitions that abandon a lease.
+ DeleteAllChatHeartbeats(ctx context.Context, chatID uuid.UUID) error
DeleteAllChatQueuedMessages(ctx context.Context, chatID uuid.UUID) error
+ DeleteAllChatQueuedMessagesReturningCount(ctx context.Context, chatID uuid.UUID) (int64, error)
DeleteAllTailnetTunnels(ctx context.Context, arg DeleteAllTailnetTunnelsParams) ([]DeleteAllTailnetTunnelsRow, error)
// Deletes all existing webpush subscriptions.
// This should be called when the VAPID keypair is regenerated, as the old
@@ -124,10 +131,16 @@ type sqlcQuerier interface {
// window (for example, after an unarchive races with a pending
// archive-cleanup retry).
DeleteChatDebugDataByChatID(ctx context.Context, arg DeleteChatDebugDataByChatIDParams) (int64, error)
+ // Deletes heartbeat rows for the supplied (chat_id, runner_id) pairs.
+ DeleteChatHeartbeats(ctx context.Context, arg DeleteChatHeartbeatsParams) (int64, error)
DeleteChatModelConfigByID(ctx context.Context, id uuid.UUID) error
DeleteChatModelConfigsByAIProviderID(ctx context.Context, aiProviderID uuid.UUID) error
DeleteChatModelConfigsByProvider(ctx context.Context, provider string) error
DeleteChatQueuedMessage(ctx context.Context, arg DeleteChatQueuedMessageParams) error
+ // Deletes a queued message, scoped to the parent chat. Returns the
+ // number of affected rows so callers can detect missing rows without
+ // a follow-up read.
+ DeleteChatQueuedMessageReturningCount(ctx context.Context, arg DeleteChatQueuedMessageReturningCountParams) (int64, error)
DeleteChatUsageLimitGroupOverride(ctx context.Context, groupID uuid.UUID) error
DeleteChatUsageLimitUserOverride(ctx context.Context, userID uuid.UUID) error
DeleteCryptoKey(ctx context.Context, arg DeleteCryptoKeyParams) (CryptoKey, error)
@@ -196,6 +209,7 @@ type sqlcQuerier interface {
DeleteProvisionerKey(ctx context.Context, id uuid.UUID) error
DeleteReplicasUpdatedBefore(ctx context.Context, updatedAt time.Time) error
DeleteRuntimeConfig(ctx context.Context, key string) error
+ DeleteStaleChatHeartbeats(ctx context.Context, staleSeconds int32) (int64, error)
DeleteTailnetPeer(ctx context.Context, arg DeleteTailnetPeerParams) (DeleteTailnetPeerRow, error)
DeleteTailnetTunnel(ctx context.Context, arg DeleteTailnetTunnelParams) (DeleteTailnetTunnelRow, error)
DeleteTask(ctx context.Context, arg DeleteTaskParams) (uuid.UUID, error)
@@ -318,6 +332,10 @@ type sqlcQuerier interface {
// This function returns roles for authorization purposes. Implied member roles
// are included.
GetAuthorizationUserRoles(ctx context.Context, userID uuid.UUID) (GetAuthorizationUserRolesRow, error)
+ // Returns read-only root chat candidates for state-machine-backed
+ // auto-archive. Activity is computed across the root family. The query
+ // limits roots, not total family members.
+ GetAutoArchiveInactiveChatCandidates(ctx context.Context, arg GetAutoArchiveInactiveChatCandidatesParams) ([]GetAutoArchiveInactiveChatCandidatesRow, error)
GetBoundaryLogByID(ctx context.Context, id uuid.UUID) (BoundaryLog, error)
GetBoundarySessionByID(ctx context.Context, id uuid.UUID) (BoundarySession, error)
GetChatACLByID(ctx context.Context, id uuid.UUID) (GetChatACLByIDRow, error)
@@ -329,6 +347,7 @@ type sqlcQuerier interface {
// Auto-archive window in days. 0 disables.
GetChatAutoArchiveDays(ctx context.Context, defaultAutoArchiveDays int32) (int32, error)
GetChatByID(ctx context.Context, id uuid.UUID) (Chat, error)
+ GetChatByIDForShare(ctx context.Context, id uuid.UUID) (Chat, error)
GetChatByIDForUpdate(ctx context.Context, id uuid.UUID) (Chat, error)
GetChatComputerUseProvider(ctx context.Context) (string, error)
// Per-root-chat cost breakdown for a single user within a date range.
@@ -366,6 +385,10 @@ type sqlcQuerier interface {
GetChatDiffStatusSummary(ctx context.Context) (GetChatDiffStatusSummaryRow, error)
GetChatDiffStatusesByChatIDs(ctx context.Context, chatIds []uuid.UUID) ([]ChatDiffStatus, error)
GetChatExploreModelOverride(ctx context.Context) (string, error)
+ // Returns the chat IDs of every chat in a family (root + all children)
+ // in deterministic order. The id parameter must be the root id; the
+ // query does not walk up from a child.
+ GetChatFamilyIDsByRootID(ctx context.Context, id uuid.UUID) ([]uuid.UUID, error)
GetChatFileByID(ctx context.Context, id uuid.UUID) (ChatFile, error)
// GetChatFileMetadataByChatID returns lightweight file metadata for
// all files linked to a chat. The data column is excluded to avoid
@@ -373,6 +396,7 @@ type sqlcQuerier interface {
GetChatFileMetadataByChatID(ctx context.Context, chatID uuid.UUID) ([]GetChatFileMetadataByChatIDRow, error)
GetChatFilesByIDs(ctx context.Context, ids []uuid.UUID) ([]ChatFile, error)
GetChatGeneralModelOverride(ctx context.Context) (string, error)
+ GetChatHeartbeat(ctx context.Context, arg GetChatHeartbeatParams) (ChatHeartbeat, error)
// GetChatIncludeDefaultSystemPrompt preserves the legacy default
// for deployments created before the explicit include-default toggle.
// When the toggle is unset, a non-empty custom prompt implies false;
@@ -386,6 +410,7 @@ type sqlcQuerier interface {
GetChatMessagesByChatID(ctx context.Context, arg GetChatMessagesByChatIDParams) ([]ChatMessage, error)
GetChatMessagesByChatIDAscPaginated(ctx context.Context, arg GetChatMessagesByChatIDAscPaginatedParams) ([]ChatMessage, error)
GetChatMessagesByChatIDDescPaginated(ctx context.Context, arg GetChatMessagesByChatIDDescPaginatedParams) ([]ChatMessage, error)
+ GetChatMessagesByRevisionForStream(ctx context.Context, arg GetChatMessagesByRevisionForStreamParams) ([]ChatMessage, error)
GetChatMessagesForPromptByChatID(ctx context.Context, chatID uuid.UUID) ([]ChatMessage, error)
GetChatModelConfigByID(ctx context.Context, id uuid.UUID) (ChatModelConfig, error)
GetChatModelConfigs(ctx context.Context) ([]ChatModelConfig, error)
@@ -395,12 +420,18 @@ type sqlcQuerier interface {
// personal chat model overrides. It defaults to false when unset.
GetChatPersonalModelOverridesEnabled(ctx context.Context) (bool, error)
GetChatPlanModeInstructions(ctx context.Context) (string, error)
+ GetChatQueuedMessageByID(ctx context.Context, arg GetChatQueuedMessageByIDParams) (ChatQueuedMessage, error)
+ // Returns the queue head (lowest position, then lowest id).
+ GetChatQueuedMessageHead(ctx context.Context, chatID uuid.UUID) (ChatQueuedMessage, error)
GetChatQueuedMessages(ctx context.Context, chatID uuid.UUID) ([]ChatQueuedMessage, error)
+ // Returns queued messages in state-machine order (position ASC, id ASC).
+ GetChatQueuedMessagesByPosition(ctx context.Context, chatID uuid.UUID) ([]ChatQueuedMessage, error)
// Returns the chat retention period in days. Chats archived longer
// than this and orphaned chat files older than this are purged by
// dbpurge. Returns 30 (days) when no value has been configured.
// A value of 0 disables chat purging entirely.
GetChatRetentionDays(ctx context.Context) (int32, error)
+ GetChatStreamSyncRows(ctx context.Context, ids []uuid.UUID) ([]GetChatStreamSyncRowsRow, error)
GetChatSystemPrompt(ctx context.Context) (string, error)
// GetChatSystemPromptConfig returns both chat system prompt settings in a
// single read to avoid torn reads between separate site-config lookups.
@@ -425,11 +456,18 @@ type sqlcQuerier interface {
// jsonb_array_elements never raises "cannot extract elements from a
// scalar". Backed by idx_chat_messages_user_prompts.
GetChatUserPromptsByChatID(ctx context.Context, arg GetChatUserPromptsByChatIDParams) ([]GetChatUserPromptsByChatIDRow, error)
+ // Returns worker-runnable chats whose ownership is missing or whose
+ // current runner heartbeat is stale. The runner_id IS NULL predicate is
+ // a robustness extension for inconsistent rows where a worker_id exists
+ // without a runner_id; normal missing ownership is worker_id IS NULL or
+ // a missing or stale heartbeat row.
+ GetChatWorkerAcquisitionCandidates(ctx context.Context, arg GetChatWorkerAcquisitionCandidatesParams) ([]GetChatWorkerAcquisitionCandidatesRow, error)
// Returns the global TTL for chat workspaces as a Go duration string.
// Returns "0s" (disabled) when no value has been configured.
GetChatWorkspaceTTL(ctx context.Context) (string, error)
GetChats(ctx context.Context, arg GetChatsParams) ([]GetChatsRow, error)
GetChatsByChatFileID(ctx context.Context, fileID uuid.UUID) ([]Chat, error)
+ GetChatsByIDsForRunnerSync(ctx context.Context, ids []uuid.UUID) ([]Chat, error)
GetChatsByWorkspaceIDs(ctx context.Context, ids []uuid.UUID) ([]Chat, error)
// Retrieves chats updated after the given timestamp for telemetry
// snapshot collection. Uses updated_at so that long-running chats
@@ -446,6 +484,10 @@ type sqlcQuerier interface {
GetCryptoKeysByFeature(ctx context.Context, feature CryptoKeyFeature) ([]CryptoKey, error)
GetDBCryptKeys(ctx context.Context) ([]DBCryptKey, error)
GetDERPMeshKey(ctx context.Context) (string, error)
+ // Returns the current database timestamp. Used so transitions that
+ // record deadlines or heartbeats rely on a clock that is consistent
+ // with the database rather than the caller's local clock.
+ GetDatabaseNow(ctx context.Context) (time.Time, error)
GetDefaultChatModelConfig(ctx context.Context) (ChatModelConfig, error)
GetDefaultOrganization(ctx context.Context) (Organization, error)
GetDefaultProxyConfig(ctx context.Context) (GetDefaultProxyConfigRow, error)
@@ -910,6 +952,8 @@ type sqlcQuerier interface {
GetWorkspacesByTemplateID(ctx context.Context, templateID uuid.UUID) ([]WorkspaceTable, error)
GetWorkspacesEligibleForTransition(ctx context.Context, now time.Time) ([]GetWorkspacesEligibleForTransitionRow, error)
GetWorkspacesForWorkspaceMetrics(ctx context.Context) ([]GetWorkspacesForWorkspaceMetricsRow, error)
+ // Increments generation_attempt and returns the resulting value.
+ IncrementChatGenerationAttempt(ctx context.Context, id uuid.UUID) (int64, error)
InsertAIBridgeInterception(ctx context.Context, arg InsertAIBridgeInterceptionParams) (AIBridgeInterception, error)
InsertAIBridgeModelThought(ctx context.Context, arg InsertAIBridgeModelThoughtParams) (AIBridgeModelThought, error)
InsertAIBridgeTokenUsage(ctx context.Context, arg InsertAIBridgeTokenUsageParams) (AIBridgeTokenUsage, error)
@@ -940,7 +984,14 @@ type sqlcQuerier interface {
InsertChatFile(ctx context.Context, arg InsertChatFileParams) (InsertChatFileRow, error)
InsertChatMessages(ctx context.Context, arg InsertChatMessagesParams) ([]ChatMessage, error)
InsertChatModelConfig(ctx context.Context, arg InsertChatModelConfigParams) (ChatModelConfig, error)
+ // Legacy queue insertion path. When no caller-supplied creator exists,
+ // preserve the created_by invariant by attributing the queued row to the
+ // chat owner.
InsertChatQueuedMessage(ctx context.Context, arg InsertChatQueuedMessageParams) (ChatQueuedMessage, error)
+ // Inserts a queued message that carries a position (from the default
+ // sequence) and an explicit created_by reference. Use this when the
+ // queued-message creator differs from the chat owner.
+ InsertChatQueuedMessageWithCreator(ctx context.Context, arg InsertChatQueuedMessageWithCreatorParams) (ChatQueuedMessage, error)
InsertCryptoKey(ctx context.Context, arg InsertCryptoKeyParams) (CryptoKey, error)
InsertCustomRole(ctx context.Context, arg InsertCustomRoleParams) (CustomRole, error)
InsertDBCryptKey(ctx context.Context, arg InsertDBCryptKeyParams) error
@@ -1020,6 +1071,11 @@ type sqlcQuerier interface {
InsertWorkspaceProxy(ctx context.Context, arg InsertWorkspaceProxyParams) (WorkspaceProxy, error)
InsertWorkspaceResource(ctx context.Context, arg InsertWorkspaceResourceParams) (WorkspaceResource, error)
InsertWorkspaceResourceMetadata(ctx context.Context, arg InsertWorkspaceResourceMetadataParams) ([]WorkspaceResourceMetadatum, error)
+ // Returns true when there is no heartbeat row for (chat_id, runner_id)
+ // or the existing row is older than @stale_seconds seconds by database
+ // time. chatstate calls this in a single query so the staleness check
+ // is atomic and does not depend on the caller's local clock.
+ IsChatHeartbeatStale(ctx context.Context, arg IsChatHeartbeatStaleParams) (bool, error)
// LinkChatFiles inserts file associations into the chat_file_links
// join table with deduplication (ON CONFLICT DO NOTHING). The INSERT
// is conditional: it only proceeds when the total number of links
@@ -1071,6 +1127,19 @@ type sqlcQuerier interface {
ListUserSecretsWithValues(ctx context.Context, userID uuid.UUID) ([]UserSecret, error)
ListUserSkillMetadataByUserID(ctx context.Context, userID uuid.UUID) ([]ListUserSkillMetadataByUserIDRow, error)
ListWorkspaceAgentPortShares(ctx context.Context, workspaceID uuid.UUID) ([]WorkspaceAgentPortShare, error)
+ // =====================================================================
+ // chatd core state machine queries.
+ //
+ // These are consumed by the coderd/x/chatd/chatstate package. They
+ // are intentionally kept side-by-side with the legacy chatd queries
+ // above so the existing runtime keeps working while the state machine
+ // lands behind it.
+ // =====================================================================
+ // Locks the chat row with FOR UPDATE and atomically increments its
+ // snapshot_version, returning the post-bump chat. This is the single
+ // entry point ChatMachine.Update uses to acquire the row lock and
+ // allocate a new snapshot version in one round trip.
+ LockChatAndBumpSnapshotVersion(ctx context.Context, id uuid.UUID) (Chat, error)
MarkAllInboxNotificationsAsRead(ctx context.Context, arg MarkAllInboxNotificationsAsReadParams) error
OIDCClaimFieldValues(ctx context.Context, arg OIDCClaimFieldValuesParams) ([]string, error)
// OIDCClaimFields returns a list of distinct keys in the the merged_claims fields.
@@ -1095,6 +1164,9 @@ type sqlcQuerier interface {
// Mutates only created_at on the target row; ids are unchanged so
// consumers can keep tracking queued messages by id.
ReorderChatQueuedMessageToFront(ctx context.Context, arg ReorderChatQueuedMessageToFrontParams) (int64, error)
+ // Sets the target queued message's position to one less than the
+ // current minimum position for that chat, moving it to the head.
+ ReorderChatQueuedMessageToHead(ctx context.Context, arg ReorderChatQueuedMessageToHeadParams) (int64, error)
// Resolves the effective spend limit for a user using the hierarchy:
// 1. Individual user override (highest priority, applies globally across
// all organizations since it lives on the users table)
@@ -1196,6 +1268,11 @@ type sqlcQuerier interface {
// parameter keeps updated_at under the caller's clock, matching
// the injectable quartz.Clock used by FinalizeStale sweeps.
UpdateChatDebugStep(ctx context.Context, arg UpdateChatDebugStepParams) (ChatDebugStep, error)
+ // Atomically updates the execution-state-managed fields on a chat:
+ // status, archived, last_error, ownership identifiers, and the
+ // requires-action deadline. Callers compose this with transition
+ // mutations inside a single ChatMachine.Update transaction.
+ UpdateChatExecutionState(ctx context.Context, arg UpdateChatExecutionStateParams) (Chat, error)
// Bumps the heartbeat timestamp for the given set of chat IDs,
// provided they are still running and owned by the specified
// worker. Returns the IDs that were actually updated so the
@@ -1214,11 +1291,9 @@ type sqlcQuerier interface {
// Updates the cached last completed turn summary for sidebar display.
// Empty or whitespace-only summaries are stored as NULL here so direct
// query callers cannot accidentally persist blank sidebar text.
- // This intentionally preserves updated_at. The staleness guard relies on
- // every new-turn query, such as UpdateChatStatus and AcquireChats, bumping
- // updated_at. Future chat-field updates that do not bump updated_at can let
- // stale summaries persist. If this query ever bumps updated_at, later
- // goroutine summary writes will be rejected as stale.
+ // This intentionally preserves updated_at. The staleness guard uses
+ // history_version so worker lifecycle transitions that do not change the
+ // active message history cannot reject final turn summary writes.
// Two summary workers using the same freshness marker are last-write-wins.
UpdateChatLastTurnSummary(ctx context.Context, arg UpdateChatLastTurnSummaryParams) (int64, error)
UpdateChatMCPServerIDs(ctx context.Context, arg UpdateChatMCPServerIDsParams) (Chat, error)
@@ -1226,6 +1301,9 @@ type sqlcQuerier interface {
UpdateChatModelConfig(ctx context.Context, arg UpdateChatModelConfigParams) (ChatModelConfig, error)
UpdateChatPinOrder(ctx context.Context, arg UpdateChatPinOrderParams) error
UpdateChatPlanModeByID(ctx context.Context, arg UpdateChatPlanModeByIDParams) (Chat, error)
+ // Stores the client-visible retry payload. retry_state_version is
+ // assigned by trigger from the current snapshot_version.
+ UpdateChatRetryState(ctx context.Context, arg UpdateChatRetryStateParams) (Chat, error)
UpdateChatStatus(ctx context.Context, arg UpdateChatStatusParams) (Chat, error)
UpdateChatStatusPreserveUpdatedAt(ctx context.Context, arg UpdateChatStatusPreserveUpdatedAtParams) (Chat, error)
UpdateChatTitleByID(ctx context.Context, arg UpdateChatTitleByIDParams) (Chat, error)
@@ -1372,6 +1450,10 @@ type sqlcQuerier interface {
UpsertChatDiffStatusReference(ctx context.Context, arg UpsertChatDiffStatusReferenceParams) (ChatDiffStatus, error)
UpsertChatExploreModelOverride(ctx context.Context, value string) error
UpsertChatGeneralModelOverride(ctx context.Context, value string) error
+ // Upserts a heartbeat row for the (chat_id, runner_id) lease. Uses
+ // database time so callers do not depend on a local clock.
+ UpsertChatHeartbeat(ctx context.Context, arg UpsertChatHeartbeatParams) error
+ UpsertChatHeartbeats(ctx context.Context, arg UpsertChatHeartbeatsParams) error
UpsertChatIncludeDefaultSystemPrompt(ctx context.Context, includeDefaultSystemPrompt bool) error
// UpsertChatPersonalModelOverridesEnabled updates whether users may configure
// personal chat model overrides.
diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go
index 6b7b39c021..6c1406c049 100644
--- a/coderd/database/queries.sql.go
+++ b/coderd/database/queries.sql.go
@@ -5982,7 +5982,7 @@ WHERE
LIMIT
$3::int
)
-RETURNING 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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, user_acl, group_acl
+RETURNING 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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, user_acl, group_acl, snapshot_version, history_version, queue_version, generation_attempt, retry_state, retry_state_version, runner_id, requires_action_deadline_at
),
chats_expanded AS (
SELECT
@@ -6014,6 +6014,14 @@ chats_expanded AS (
acquired_chats.plan_mode,
acquired_chats.client_type,
acquired_chats.last_turn_summary,
+ acquired_chats.snapshot_version,
+ acquired_chats.history_version,
+ acquired_chats.queue_version,
+ acquired_chats.generation_attempt,
+ acquired_chats.retry_state,
+ acquired_chats.retry_state_version,
+ acquired_chats.runner_id,
+ acquired_chats.requires_action_deadline_at,
COALESCE(root.user_acl, acquired_chats.user_acl) AS user_acl,
COALESCE(root.group_acl, acquired_chats.group_acl) AS group_acl,
owner.username AS owner_username,
@@ -6023,7 +6031,7 @@ chats_expanded AS (
LEFT JOIN chats root ON root.id = COALESCE(acquired_chats.root_chat_id, acquired_chats.parent_chat_id)
JOIN visible_users owner ON owner.id = acquired_chats.owner_id
)
-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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, user_acl, group_acl, owner_username, owner_name
+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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, snapshot_version, history_version, queue_version, generation_attempt, retry_state, retry_state_version, runner_id, requires_action_deadline_at, user_acl, group_acl, owner_username, owner_name
FROM chats_expanded
`
@@ -6073,6 +6081,14 @@ func (q *sqlQuerier) AcquireChats(ctx context.Context, arg AcquireChatsParams) (
&i.PlanMode,
&i.ClientType,
&i.LastTurnSummary,
+ &i.SnapshotVersion,
+ &i.HistoryVersion,
+ &i.QueueVersion,
+ &i.GenerationAttempt,
+ &i.RetryState,
+ &i.RetryStateVersion,
+ &i.RunnerID,
+ &i.RequiresActionDeadlineAt,
&i.UserACL,
&i.GroupACL,
&i.OwnerUsername,
@@ -6215,7 +6231,7 @@ WITH updated_chats AS (
UPDATE chats
SET archived = true, pin_order = 0, updated_at = NOW()
WHERE id = $1::uuid OR root_chat_id = $1::uuid
- RETURNING 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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, user_acl, group_acl
+ RETURNING 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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, user_acl, group_acl, snapshot_version, history_version, queue_version, generation_attempt, retry_state, retry_state_version, runner_id, requires_action_deadline_at
),
chats_expanded AS (
SELECT
@@ -6247,6 +6263,14 @@ chats_expanded AS (
updated_chats.plan_mode,
updated_chats.client_type,
updated_chats.last_turn_summary,
+ updated_chats.snapshot_version,
+ updated_chats.history_version,
+ updated_chats.queue_version,
+ updated_chats.generation_attempt,
+ updated_chats.retry_state,
+ updated_chats.retry_state_version,
+ updated_chats.runner_id,
+ updated_chats.requires_action_deadline_at,
COALESCE(root.user_acl, updated_chats.user_acl) AS user_acl,
COALESCE(root.group_acl, updated_chats.group_acl) AS group_acl,
owner.username AS owner_username,
@@ -6256,7 +6280,7 @@ chats_expanded AS (
LEFT JOIN chats root ON root.id = COALESCE(updated_chats.root_chat_id, updated_chats.parent_chat_id)
JOIN visible_users owner ON owner.id = updated_chats.owner_id
)
-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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, user_acl, group_acl, owner_username, owner_name
+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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, snapshot_version, history_version, queue_version, generation_attempt, retry_state, retry_state_version, runner_id, requires_action_deadline_at, user_acl, group_acl, owner_username, owner_name
FROM chats_expanded
ORDER BY (chats_expanded.id = $1::uuid) DESC, chats_expanded.created_at ASC, chats_expanded.id ASC
`
@@ -6299,6 +6323,14 @@ func (q *sqlQuerier) ArchiveChatByID(ctx context.Context, id uuid.UUID) ([]Chat,
&i.PlanMode,
&i.ClientType,
&i.LastTurnSummary,
+ &i.SnapshotVersion,
+ &i.HistoryVersion,
+ &i.QueueVersion,
+ &i.GenerationAttempt,
+ &i.RetryState,
+ &i.RetryStateVersion,
+ &i.RunnerID,
+ &i.RequiresActionDeadlineAt,
&i.UserACL,
&i.GroupACL,
&i.OwnerUsername,
@@ -6353,10 +6385,10 @@ archived AS (
FROM to_archive t
WHERE (c.id = t.id OR c.root_chat_id = t.id) -- cascade to children
AND c.archived = false
- RETURNING c.id, c.owner_id, c.workspace_id, c.title, c.status, c.worker_id, c.started_at, c.heartbeat_at, c.created_at, c.updated_at, c.parent_chat_id, c.root_chat_id, c.last_model_config_id, c.archived, c.last_error, c.mode, c.mcp_server_ids, c.labels, c.build_id, c.agent_id, c.pin_order, c.last_read_message_id, c.last_injected_context, c.dynamic_tools, c.organization_id, c.plan_mode, c.client_type, c.last_turn_summary, c.user_acl, c.group_acl
+ RETURNING c.id, c.owner_id, c.workspace_id, c.title, c.status, c.worker_id, c.started_at, c.heartbeat_at, c.created_at, c.updated_at, c.parent_chat_id, c.root_chat_id, c.last_model_config_id, c.archived, c.last_error, c.mode, c.mcp_server_ids, c.labels, c.build_id, c.agent_id, c.pin_order, c.last_read_message_id, c.last_injected_context, c.dynamic_tools, c.organization_id, c.plan_mode, c.client_type, c.last_turn_summary, c.user_acl, c.group_acl, c.snapshot_version, c.history_version, c.queue_version, c.generation_attempt, c.retry_state, c.retry_state_version, c.runner_id, c.requires_action_deadline_at
)
SELECT
- a.id, a.owner_id, a.workspace_id, a.title, a.status, a.worker_id, a.started_at, a.heartbeat_at, a.created_at, a.updated_at, a.parent_chat_id, a.root_chat_id, a.last_model_config_id, a.archived, a.last_error, a.mode, a.mcp_server_ids, a.labels, a.build_id, a.agent_id, a.pin_order, a.last_read_message_id, a.last_injected_context, a.dynamic_tools, a.organization_id, a.plan_mode, a.client_type, a.last_turn_summary, a.user_acl, a.group_acl,
+ a.id, a.owner_id, a.workspace_id, a.title, a.status, a.worker_id, a.started_at, a.heartbeat_at, a.created_at, a.updated_at, a.parent_chat_id, a.root_chat_id, a.last_model_config_id, a.archived, a.last_error, a.mode, a.mcp_server_ids, a.labels, a.build_id, a.agent_id, a.pin_order, a.last_read_message_id, a.last_injected_context, a.dynamic_tools, a.organization_id, a.plan_mode, a.client_type, a.last_turn_summary, a.user_acl, a.group_acl, a.snapshot_version, a.history_version, a.queue_version, a.generation_attempt, a.retry_state, a.retry_state_version, a.runner_id, a.requires_action_deadline_at,
-- Children inherit their root's activity so last_activity_at is never null.
COALESCE(
t.last_activity_at,
@@ -6374,37 +6406,45 @@ type AutoArchiveInactiveChatsParams struct {
}
type AutoArchiveInactiveChatsRow 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"`
- Title string `db:"title" json:"title"`
- Status ChatStatus `db:"status" json:"status"`
- WorkerID uuid.NullUUID `db:"worker_id" json:"worker_id"`
- StartedAt sql.NullTime `db:"started_at" json:"started_at"`
- HeartbeatAt sql.NullTime `db:"heartbeat_at" json:"heartbeat_at"`
- CreatedAt time.Time `db:"created_at" json:"created_at"`
- UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
- 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"`
- Archived bool `db:"archived" json:"archived"`
- LastError pqtype.NullRawMessage `db:"last_error" json:"last_error"`
- Mode NullChatMode `db:"mode" json:"mode"`
- MCPServerIDs []uuid.UUID `db:"mcp_server_ids" json:"mcp_server_ids"`
- Labels json.RawMessage `db:"labels" json:"labels"`
- BuildID uuid.NullUUID `db:"build_id" json:"build_id"`
- AgentID uuid.NullUUID `db:"agent_id" json:"agent_id"`
- PinOrder int32 `db:"pin_order" json:"pin_order"`
- LastReadMessageID sql.NullInt64 `db:"last_read_message_id" json:"last_read_message_id"`
- LastInjectedContext pqtype.NullRawMessage `db:"last_injected_context" json:"last_injected_context"`
- DynamicTools pqtype.NullRawMessage `db:"dynamic_tools" json:"dynamic_tools"`
- OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"`
- PlanMode NullChatPlanMode `db:"plan_mode" json:"plan_mode"`
- ClientType ChatClientType `db:"client_type" json:"client_type"`
- LastTurnSummary sql.NullString `db:"last_turn_summary" json:"last_turn_summary"`
- UserACL json.RawMessage `db:"user_acl" json:"user_acl"`
- GroupACL json.RawMessage `db:"group_acl" json:"group_acl"`
- LastActivityAt time.Time `db:"last_activity_at" json:"last_activity_at"`
+ 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"`
+ Title string `db:"title" json:"title"`
+ Status ChatStatus `db:"status" json:"status"`
+ WorkerID uuid.NullUUID `db:"worker_id" json:"worker_id"`
+ StartedAt sql.NullTime `db:"started_at" json:"started_at"`
+ HeartbeatAt sql.NullTime `db:"heartbeat_at" json:"heartbeat_at"`
+ CreatedAt time.Time `db:"created_at" json:"created_at"`
+ UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
+ 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"`
+ Archived bool `db:"archived" json:"archived"`
+ LastError pqtype.NullRawMessage `db:"last_error" json:"last_error"`
+ Mode NullChatMode `db:"mode" json:"mode"`
+ MCPServerIDs []uuid.UUID `db:"mcp_server_ids" json:"mcp_server_ids"`
+ Labels json.RawMessage `db:"labels" json:"labels"`
+ BuildID uuid.NullUUID `db:"build_id" json:"build_id"`
+ AgentID uuid.NullUUID `db:"agent_id" json:"agent_id"`
+ PinOrder int32 `db:"pin_order" json:"pin_order"`
+ LastReadMessageID sql.NullInt64 `db:"last_read_message_id" json:"last_read_message_id"`
+ LastInjectedContext pqtype.NullRawMessage `db:"last_injected_context" json:"last_injected_context"`
+ DynamicTools pqtype.NullRawMessage `db:"dynamic_tools" json:"dynamic_tools"`
+ OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"`
+ PlanMode NullChatPlanMode `db:"plan_mode" json:"plan_mode"`
+ ClientType ChatClientType `db:"client_type" json:"client_type"`
+ LastTurnSummary sql.NullString `db:"last_turn_summary" json:"last_turn_summary"`
+ UserACL json.RawMessage `db:"user_acl" json:"user_acl"`
+ GroupACL json.RawMessage `db:"group_acl" json:"group_acl"`
+ SnapshotVersion int64 `db:"snapshot_version" json:"snapshot_version"`
+ HistoryVersion int64 `db:"history_version" json:"history_version"`
+ QueueVersion int64 `db:"queue_version" json:"queue_version"`
+ GenerationAttempt int64 `db:"generation_attempt" json:"generation_attempt"`
+ RetryState pqtype.NullRawMessage `db:"retry_state" json:"retry_state"`
+ RetryStateVersion int64 `db:"retry_state_version" json:"retry_state_version"`
+ RunnerID uuid.NullUUID `db:"runner_id" json:"runner_id"`
+ RequiresActionDeadlineAt sql.NullTime `db:"requires_action_deadline_at" json:"requires_action_deadline_at"`
+ LastActivityAt time.Time `db:"last_activity_at" json:"last_activity_at"`
}
// Archives inactive root chats (pinned and already-archived chats skipped),
@@ -6454,6 +6494,14 @@ func (q *sqlQuerier) AutoArchiveInactiveChats(ctx context.Context, arg AutoArchi
&i.LastTurnSummary,
&i.UserACL,
&i.GroupACL,
+ &i.SnapshotVersion,
+ &i.HistoryVersion,
+ &i.QueueVersion,
+ &i.GenerationAttempt,
+ &i.RetryState,
+ &i.RetryStateVersion,
+ &i.RunnerID,
+ &i.RequiresActionDeadlineAt,
&i.LastActivityAt,
); err != nil {
return nil, err
@@ -6505,6 +6553,21 @@ func (q *sqlQuerier) ClearChatMessageProviderResponseIDsByChatID(ctx context.Con
return err
}
+const countChatQueuedMessages = `-- name: CountChatQueuedMessages :one
+SELECT COUNT(*)::bigint AS count
+FROM chat_queued_messages
+WHERE chat_id = $1::uuid
+`
+
+// Cheap queue-length check used by ChatMachine.Update when deciding
+// whether the chat is in a "1" sub-state.
+func (q *sqlQuerier) CountChatQueuedMessages(ctx context.Context, chatID uuid.UUID) (int64, error) {
+ row := q.db.QueryRowContext(ctx, countChatQueuedMessages, chatID)
+ var count int64
+ err := row.Scan(&count)
+ return count, err
+}
+
const countEnabledModelsWithoutPricing = `-- name: CountEnabledModelsWithoutPricing :one
SELECT COUNT(*)::bigint AS count
FROM chat_model_configs
@@ -6529,6 +6592,17 @@ func (q *sqlQuerier) CountEnabledModelsWithoutPricing(ctx context.Context) (int6
return count, err
}
+const deleteAllChatHeartbeats = `-- name: DeleteAllChatHeartbeats :exec
+DELETE FROM chat_heartbeats WHERE chat_id = $1::uuid
+`
+
+// Deletes all heartbeat rows for the chat. Used during ownership
+// transitions that abandon a lease.
+func (q *sqlQuerier) DeleteAllChatHeartbeats(ctx context.Context, chatID uuid.UUID) error {
+ _, err := q.db.ExecContext(ctx, deleteAllChatHeartbeats, chatID)
+ return err
+}
+
const deleteAllChatQueuedMessages = `-- name: DeleteAllChatQueuedMessages :exec
DELETE FROM chat_queued_messages WHERE chat_id = $1
`
@@ -6538,6 +6612,41 @@ func (q *sqlQuerier) DeleteAllChatQueuedMessages(ctx context.Context, chatID uui
return err
}
+const deleteAllChatQueuedMessagesReturningCount = `-- name: DeleteAllChatQueuedMessagesReturningCount :execrows
+DELETE FROM chat_queued_messages
+WHERE chat_id = $1::uuid
+`
+
+func (q *sqlQuerier) DeleteAllChatQueuedMessagesReturningCount(ctx context.Context, chatID uuid.UUID) (int64, error) {
+ result, err := q.db.ExecContext(ctx, deleteAllChatQueuedMessagesReturningCount, chatID)
+ if err != nil {
+ return 0, err
+ }
+ return result.RowsAffected()
+}
+
+const deleteChatHeartbeats = `-- name: DeleteChatHeartbeats :execrows
+DELETE FROM chat_heartbeats
+USING unnest($1::uuid[]) WITH ORDINALITY AS chat_ids(chat_id, ord)
+JOIN unnest($2::uuid[]) WITH ORDINALITY AS runner_ids(runner_id, ord) USING (ord)
+WHERE chat_heartbeats.chat_id = chat_ids.chat_id
+ AND chat_heartbeats.runner_id = runner_ids.runner_id
+`
+
+type DeleteChatHeartbeatsParams struct {
+ ChatIds []uuid.UUID `db:"chat_ids" json:"chat_ids"`
+ RunnerIds []uuid.UUID `db:"runner_ids" json:"runner_ids"`
+}
+
+// Deletes heartbeat rows for the supplied (chat_id, runner_id) pairs.
+func (q *sqlQuerier) DeleteChatHeartbeats(ctx context.Context, arg DeleteChatHeartbeatsParams) (int64, error) {
+ result, err := q.db.ExecContext(ctx, deleteChatHeartbeats, pq.Array(arg.ChatIds), pq.Array(arg.RunnerIds))
+ if err != nil {
+ return 0, err
+ }
+ return result.RowsAffected()
+}
+
const deleteChatQueuedMessage = `-- name: DeleteChatQueuedMessage :exec
DELETE FROM chat_queued_messages WHERE id = $1 AND chat_id = $2
`
@@ -6552,6 +6661,27 @@ func (q *sqlQuerier) DeleteChatQueuedMessage(ctx context.Context, arg DeleteChat
return err
}
+const deleteChatQueuedMessageReturningCount = `-- name: DeleteChatQueuedMessageReturningCount :execrows
+DELETE FROM chat_queued_messages
+WHERE id = $1::bigint AND chat_id = $2::uuid
+`
+
+type DeleteChatQueuedMessageReturningCountParams struct {
+ ID int64 `db:"id" json:"id"`
+ ChatID uuid.UUID `db:"chat_id" json:"chat_id"`
+}
+
+// Deletes a queued message, scoped to the parent chat. Returns the
+// number of affected rows so callers can detect missing rows without
+// a follow-up read.
+func (q *sqlQuerier) DeleteChatQueuedMessageReturningCount(ctx context.Context, arg DeleteChatQueuedMessageReturningCountParams) (int64, error) {
+ result, err := q.db.ExecContext(ctx, deleteChatQueuedMessageReturningCount, arg.ID, arg.ChatID)
+ if err != nil {
+ return 0, err
+ }
+ return result.RowsAffected()
+}
+
const deleteChatUsageLimitGroupOverride = `-- name: DeleteChatUsageLimitGroupOverride :exec
UPDATE groups SET chat_spend_limit_micros = NULL WHERE id = $1::uuid
`
@@ -6603,8 +6733,21 @@ func (q *sqlQuerier) DeleteOldChats(ctx context.Context, arg DeleteOldChatsParam
return result.RowsAffected()
}
+const deleteStaleChatHeartbeats = `-- name: DeleteStaleChatHeartbeats :execrows
+DELETE FROM chat_heartbeats
+WHERE heartbeat_at < NOW() - (INTERVAL '1 second' * $1::int)
+`
+
+func (q *sqlQuerier) DeleteStaleChatHeartbeats(ctx context.Context, staleSeconds int32) (int64, error) {
+ result, err := q.db.ExecContext(ctx, deleteStaleChatHeartbeats, staleSeconds)
+ if err != nil {
+ return 0, err
+ }
+ return result.RowsAffected()
+}
+
const getActiveChatsByAgentID = `-- name: GetActiveChatsByAgentID :many
-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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, user_acl, group_acl, owner_username, owner_name
+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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, snapshot_version, history_version, queue_version, generation_attempt, retry_state, retry_state_version, runner_id, requires_action_deadline_at, user_acl, group_acl, owner_username, owner_name
FROM chats_expanded
WHERE agent_id = $1::uuid
AND archived = false
@@ -6653,6 +6796,14 @@ func (q *sqlQuerier) GetActiveChatsByAgentID(ctx context.Context, agentID uuid.U
&i.PlanMode,
&i.ClientType,
&i.LastTurnSummary,
+ &i.SnapshotVersion,
+ &i.HistoryVersion,
+ &i.QueueVersion,
+ &i.GenerationAttempt,
+ &i.RetryState,
+ &i.RetryStateVersion,
+ &i.RunnerID,
+ &i.RequiresActionDeadlineAt,
&i.UserACL,
&i.GroupACL,
&i.OwnerUsername,
@@ -6671,6 +6822,152 @@ func (q *sqlQuerier) GetActiveChatsByAgentID(ctx context.Context, agentID uuid.U
return items, nil
}
+const getAutoArchiveInactiveChatCandidates = `-- name: GetAutoArchiveInactiveChatCandidates :many
+SELECT
+ chats_expanded.id, chats_expanded.owner_id, chats_expanded.workspace_id, chats_expanded.title, chats_expanded.status, chats_expanded.worker_id, chats_expanded.started_at, chats_expanded.heartbeat_at, chats_expanded.created_at, chats_expanded.updated_at, chats_expanded.parent_chat_id, chats_expanded.root_chat_id, chats_expanded.last_model_config_id, chats_expanded.archived, chats_expanded.last_error, chats_expanded.mode, chats_expanded.mcp_server_ids, chats_expanded.labels, chats_expanded.build_id, chats_expanded.agent_id, chats_expanded.pin_order, chats_expanded.last_read_message_id, chats_expanded.last_injected_context, chats_expanded.dynamic_tools, chats_expanded.organization_id, chats_expanded.plan_mode, chats_expanded.client_type, chats_expanded.last_turn_summary, chats_expanded.snapshot_version, chats_expanded.history_version, chats_expanded.queue_version, chats_expanded.generation_attempt, chats_expanded.retry_state, chats_expanded.retry_state_version, chats_expanded.runner_id, chats_expanded.requires_action_deadline_at, chats_expanded.user_acl, chats_expanded.group_acl, chats_expanded.owner_username, chats_expanded.owner_name,
+ COALESCE(activity.last_activity_at, chats_expanded.created_at)::timestamptz AS last_activity_at
+FROM chats_expanded
+LEFT JOIN LATERAL (
+ SELECT MAX(chat_messages.created_at) AS last_activity_at
+ FROM chat_messages
+ JOIN chats family_chat ON family_chat.id = chat_messages.chat_id
+ WHERE (family_chat.id = chats_expanded.id OR family_chat.root_chat_id = chats_expanded.id)
+ AND chat_messages.deleted = false
+) activity ON TRUE
+WHERE
+ chats_expanded.archived = false
+ AND chats_expanded.pin_order = 0
+ AND chats_expanded.parent_chat_id IS NULL
+ AND chats_expanded.created_at < $1::timestamptz
+ AND chats_expanded.status NOT IN (
+ 'running'::chat_status,
+ 'interrupting'::chat_status,
+ 'pending'::chat_status,
+ 'paused'::chat_status,
+ 'requires_action'::chat_status
+ )
+ AND COALESCE(activity.last_activity_at, chats_expanded.created_at) < $1::timestamptz
+ORDER BY chats_expanded.created_at ASC
+LIMIT $2::int
+`
+
+type GetAutoArchiveInactiveChatCandidatesParams struct {
+ ArchiveCutoff time.Time `db:"archive_cutoff" json:"archive_cutoff"`
+ LimitCount int32 `db:"limit_count" json:"limit_count"`
+}
+
+type GetAutoArchiveInactiveChatCandidatesRow 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"`
+ Title string `db:"title" json:"title"`
+ Status ChatStatus `db:"status" json:"status"`
+ WorkerID uuid.NullUUID `db:"worker_id" json:"worker_id"`
+ StartedAt sql.NullTime `db:"started_at" json:"started_at"`
+ HeartbeatAt sql.NullTime `db:"heartbeat_at" json:"heartbeat_at"`
+ CreatedAt time.Time `db:"created_at" json:"created_at"`
+ UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
+ 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"`
+ Archived bool `db:"archived" json:"archived"`
+ LastError pqtype.NullRawMessage `db:"last_error" json:"last_error"`
+ Mode NullChatMode `db:"mode" json:"mode"`
+ MCPServerIDs []uuid.UUID `db:"mcp_server_ids" json:"mcp_server_ids"`
+ Labels StringMap `db:"labels" json:"labels"`
+ BuildID uuid.NullUUID `db:"build_id" json:"build_id"`
+ AgentID uuid.NullUUID `db:"agent_id" json:"agent_id"`
+ PinOrder int32 `db:"pin_order" json:"pin_order"`
+ LastReadMessageID sql.NullInt64 `db:"last_read_message_id" json:"last_read_message_id"`
+ LastInjectedContext pqtype.NullRawMessage `db:"last_injected_context" json:"last_injected_context"`
+ DynamicTools pqtype.NullRawMessage `db:"dynamic_tools" json:"dynamic_tools"`
+ OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"`
+ PlanMode NullChatPlanMode `db:"plan_mode" json:"plan_mode"`
+ ClientType ChatClientType `db:"client_type" json:"client_type"`
+ LastTurnSummary sql.NullString `db:"last_turn_summary" json:"last_turn_summary"`
+ SnapshotVersion int64 `db:"snapshot_version" json:"snapshot_version"`
+ HistoryVersion int64 `db:"history_version" json:"history_version"`
+ QueueVersion int64 `db:"queue_version" json:"queue_version"`
+ GenerationAttempt int64 `db:"generation_attempt" json:"generation_attempt"`
+ RetryState pqtype.NullRawMessage `db:"retry_state" json:"retry_state"`
+ RetryStateVersion int64 `db:"retry_state_version" json:"retry_state_version"`
+ RunnerID uuid.NullUUID `db:"runner_id" json:"runner_id"`
+ RequiresActionDeadlineAt sql.NullTime `db:"requires_action_deadline_at" json:"requires_action_deadline_at"`
+ UserACL ChatACL `db:"user_acl" json:"user_acl"`
+ GroupACL ChatACL `db:"group_acl" json:"group_acl"`
+ OwnerUsername string `db:"owner_username" json:"owner_username"`
+ OwnerName string `db:"owner_name" json:"owner_name"`
+ LastActivityAt time.Time `db:"last_activity_at" json:"last_activity_at"`
+}
+
+// Returns read-only root chat candidates for state-machine-backed
+// auto-archive. Activity is computed across the root family. The query
+// limits roots, not total family members.
+func (q *sqlQuerier) GetAutoArchiveInactiveChatCandidates(ctx context.Context, arg GetAutoArchiveInactiveChatCandidatesParams) ([]GetAutoArchiveInactiveChatCandidatesRow, error) {
+ rows, err := q.db.QueryContext(ctx, getAutoArchiveInactiveChatCandidates, arg.ArchiveCutoff, arg.LimitCount)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+ var items []GetAutoArchiveInactiveChatCandidatesRow
+ for rows.Next() {
+ var i GetAutoArchiveInactiveChatCandidatesRow
+ if err := rows.Scan(
+ &i.ID,
+ &i.OwnerID,
+ &i.WorkspaceID,
+ &i.Title,
+ &i.Status,
+ &i.WorkerID,
+ &i.StartedAt,
+ &i.HeartbeatAt,
+ &i.CreatedAt,
+ &i.UpdatedAt,
+ &i.ParentChatID,
+ &i.RootChatID,
+ &i.LastModelConfigID,
+ &i.Archived,
+ &i.LastError,
+ &i.Mode,
+ pq.Array(&i.MCPServerIDs),
+ &i.Labels,
+ &i.BuildID,
+ &i.AgentID,
+ &i.PinOrder,
+ &i.LastReadMessageID,
+ &i.LastInjectedContext,
+ &i.DynamicTools,
+ &i.OrganizationID,
+ &i.PlanMode,
+ &i.ClientType,
+ &i.LastTurnSummary,
+ &i.SnapshotVersion,
+ &i.HistoryVersion,
+ &i.QueueVersion,
+ &i.GenerationAttempt,
+ &i.RetryState,
+ &i.RetryStateVersion,
+ &i.RunnerID,
+ &i.RequiresActionDeadlineAt,
+ &i.UserACL,
+ &i.GroupACL,
+ &i.OwnerUsername,
+ &i.OwnerName,
+ &i.LastActivityAt,
+ ); err != nil {
+ return nil, err
+ }
+ items = append(items, i)
+ }
+ if err := rows.Close(); err != nil {
+ return nil, err
+ }
+ if err := rows.Err(); err != nil {
+ return nil, err
+ }
+ return items, nil
+}
+
const getChatACLByID = `-- name: GetChatACLByID :one
SELECT
user_acl AS users,
@@ -6694,7 +6991,7 @@ func (q *sqlQuerier) GetChatACLByID(ctx context.Context, id uuid.UUID) (GetChatA
}
const getChatByID = `-- name: GetChatByID :one
-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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, user_acl, group_acl, owner_username, owner_name
+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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, snapshot_version, history_version, queue_version, generation_attempt, retry_state, retry_state_version, runner_id, requires_action_deadline_at, user_acl, group_acl, owner_username, owner_name
FROM chats_expanded
WHERE id = $1::uuid
`
@@ -6731,6 +7028,120 @@ func (q *sqlQuerier) GetChatByID(ctx context.Context, id uuid.UUID) (Chat, error
&i.PlanMode,
&i.ClientType,
&i.LastTurnSummary,
+ &i.SnapshotVersion,
+ &i.HistoryVersion,
+ &i.QueueVersion,
+ &i.GenerationAttempt,
+ &i.RetryState,
+ &i.RetryStateVersion,
+ &i.RunnerID,
+ &i.RequiresActionDeadlineAt,
+ &i.UserACL,
+ &i.GroupACL,
+ &i.OwnerUsername,
+ &i.OwnerName,
+ )
+ return i, err
+}
+
+const getChatByIDForShare = `-- name: GetChatByIDForShare :one
+WITH shared_chat AS (
+ 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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, user_acl, group_acl, snapshot_version, history_version, queue_version, generation_attempt, retry_state, retry_state_version, runner_id, requires_action_deadline_at
+ FROM chats
+ WHERE id = $1::uuid
+ FOR SHARE
+),
+chats_expanded AS (
+ SELECT
+ shared_chat.id,
+ shared_chat.owner_id,
+ shared_chat.workspace_id,
+ shared_chat.title,
+ shared_chat.status,
+ shared_chat.worker_id,
+ shared_chat.started_at,
+ shared_chat.heartbeat_at,
+ shared_chat.created_at,
+ shared_chat.updated_at,
+ shared_chat.parent_chat_id,
+ shared_chat.root_chat_id,
+ shared_chat.last_model_config_id,
+ shared_chat.archived,
+ shared_chat.last_error,
+ shared_chat.mode,
+ shared_chat.mcp_server_ids,
+ shared_chat.labels,
+ shared_chat.build_id,
+ shared_chat.agent_id,
+ shared_chat.pin_order,
+ shared_chat.last_read_message_id,
+ shared_chat.last_injected_context,
+ shared_chat.dynamic_tools,
+ shared_chat.organization_id,
+ shared_chat.plan_mode,
+ shared_chat.client_type,
+ shared_chat.last_turn_summary,
+ shared_chat.snapshot_version,
+ shared_chat.history_version,
+ shared_chat.queue_version,
+ shared_chat.generation_attempt,
+ shared_chat.retry_state,
+ shared_chat.retry_state_version,
+ shared_chat.runner_id,
+ shared_chat.requires_action_deadline_at,
+ COALESCE(root.user_acl, shared_chat.user_acl) AS user_acl,
+ COALESCE(root.group_acl, shared_chat.group_acl) AS group_acl,
+ owner.username AS owner_username,
+ owner.name AS owner_name
+ FROM
+ shared_chat
+ LEFT JOIN chats root ON root.id = COALESCE(shared_chat.root_chat_id, shared_chat.parent_chat_id)
+ JOIN visible_users owner ON owner.id = shared_chat.owner_id
+)
+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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, snapshot_version, history_version, queue_version, generation_attempt, retry_state, retry_state_version, runner_id, requires_action_deadline_at, user_acl, group_acl, owner_username, owner_name
+FROM chats_expanded
+`
+
+func (q *sqlQuerier) GetChatByIDForShare(ctx context.Context, id uuid.UUID) (Chat, error) {
+ row := q.db.QueryRowContext(ctx, getChatByIDForShare, id)
+ var i Chat
+ err := row.Scan(
+ &i.ID,
+ &i.OwnerID,
+ &i.WorkspaceID,
+ &i.Title,
+ &i.Status,
+ &i.WorkerID,
+ &i.StartedAt,
+ &i.HeartbeatAt,
+ &i.CreatedAt,
+ &i.UpdatedAt,
+ &i.ParentChatID,
+ &i.RootChatID,
+ &i.LastModelConfigID,
+ &i.Archived,
+ &i.LastError,
+ &i.Mode,
+ pq.Array(&i.MCPServerIDs),
+ &i.Labels,
+ &i.BuildID,
+ &i.AgentID,
+ &i.PinOrder,
+ &i.LastReadMessageID,
+ &i.LastInjectedContext,
+ &i.DynamicTools,
+ &i.OrganizationID,
+ &i.PlanMode,
+ &i.ClientType,
+ &i.LastTurnSummary,
+ &i.SnapshotVersion,
+ &i.HistoryVersion,
+ &i.QueueVersion,
+ &i.GenerationAttempt,
+ &i.RetryState,
+ &i.RetryStateVersion,
+ &i.RunnerID,
+ &i.RequiresActionDeadlineAt,
&i.UserACL,
&i.GroupACL,
&i.OwnerUsername,
@@ -6741,7 +7152,7 @@ func (q *sqlQuerier) GetChatByID(ctx context.Context, id uuid.UUID) (Chat, error
const getChatByIDForUpdate = `-- name: GetChatByIDForUpdate :one
WITH locked_chat AS (
- 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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, user_acl, group_acl
+ 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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, user_acl, group_acl, snapshot_version, history_version, queue_version, generation_attempt, retry_state, retry_state_version, runner_id, requires_action_deadline_at
FROM chats
WHERE id = $1::uuid
FOR UPDATE
@@ -6776,6 +7187,14 @@ chats_expanded AS (
locked_chat.plan_mode,
locked_chat.client_type,
locked_chat.last_turn_summary,
+ locked_chat.snapshot_version,
+ locked_chat.history_version,
+ locked_chat.queue_version,
+ locked_chat.generation_attempt,
+ locked_chat.retry_state,
+ locked_chat.retry_state_version,
+ locked_chat.runner_id,
+ locked_chat.requires_action_deadline_at,
COALESCE(root.user_acl, locked_chat.user_acl) AS user_acl,
COALESCE(root.group_acl, locked_chat.group_acl) AS group_acl,
owner.username AS owner_username,
@@ -6785,7 +7204,7 @@ chats_expanded AS (
LEFT JOIN chats root ON root.id = COALESCE(locked_chat.root_chat_id, locked_chat.parent_chat_id)
JOIN visible_users owner ON owner.id = locked_chat.owner_id
)
-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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, user_acl, group_acl, owner_username, owner_name
+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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, snapshot_version, history_version, queue_version, generation_attempt, retry_state, retry_state_version, runner_id, requires_action_deadline_at, user_acl, group_acl, owner_username, owner_name
FROM chats_expanded
`
@@ -6821,6 +7240,14 @@ func (q *sqlQuerier) GetChatByIDForUpdate(ctx context.Context, id uuid.UUID) (Ch
&i.PlanMode,
&i.ClientType,
&i.LastTurnSummary,
+ &i.SnapshotVersion,
+ &i.HistoryVersion,
+ &i.QueueVersion,
+ &i.GenerationAttempt,
+ &i.RetryState,
+ &i.RetryStateVersion,
+ &i.RunnerID,
+ &i.RequiresActionDeadlineAt,
&i.UserACL,
&i.GroupACL,
&i.OwnerUsername,
@@ -7357,9 +7784,59 @@ func (q *sqlQuerier) GetChatDiffStatusesByChatIDs(ctx context.Context, chatIds [
return items, nil
}
+const getChatFamilyIDsByRootID = `-- name: GetChatFamilyIDsByRootID :many
+SELECT id
+FROM chats
+WHERE id = $1::uuid OR root_chat_id = $1::uuid
+ORDER BY (id = $1::uuid) DESC, created_at ASC, id ASC
+`
+
+// Returns the chat IDs of every chat in a family (root + all children)
+// in deterministic order. The id parameter must be the root id; the
+// query does not walk up from a child.
+func (q *sqlQuerier) GetChatFamilyIDsByRootID(ctx context.Context, id uuid.UUID) ([]uuid.UUID, error) {
+ rows, err := q.db.QueryContext(ctx, getChatFamilyIDsByRootID, id)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+ var items []uuid.UUID
+ for rows.Next() {
+ var id uuid.UUID
+ if err := rows.Scan(&id); err != nil {
+ return nil, err
+ }
+ items = append(items, id)
+ }
+ if err := rows.Close(); err != nil {
+ return nil, err
+ }
+ if err := rows.Err(); err != nil {
+ return nil, err
+ }
+ return items, nil
+}
+
+const getChatHeartbeat = `-- name: GetChatHeartbeat :one
+SELECT chat_id, runner_id, heartbeat_at FROM chat_heartbeats
+WHERE chat_id = $1::uuid AND runner_id = $2::uuid
+`
+
+type GetChatHeartbeatParams struct {
+ ChatID uuid.UUID `db:"chat_id" json:"chat_id"`
+ RunnerID uuid.UUID `db:"runner_id" json:"runner_id"`
+}
+
+func (q *sqlQuerier) GetChatHeartbeat(ctx context.Context, arg GetChatHeartbeatParams) (ChatHeartbeat, error) {
+ row := q.db.QueryRowContext(ctx, getChatHeartbeat, arg.ChatID, arg.RunnerID)
+ var i ChatHeartbeat
+ err := row.Scan(&i.ChatID, &i.RunnerID, &i.HeartbeatAt)
+ return i, err
+}
+
const getChatMessageByID = `-- name: GetChatMessageByID :one
SELECT
- id, chat_id, model_config_id, created_at, role, content, visibility, input_tokens, output_tokens, total_tokens, reasoning_tokens, cache_creation_tokens, cache_read_tokens, context_limit, compressed, created_by, content_version, total_cost_micros, runtime_ms, deleted, provider_response_id, api_key_id
+ id, chat_id, model_config_id, created_at, role, content, visibility, input_tokens, output_tokens, total_tokens, reasoning_tokens, cache_creation_tokens, cache_read_tokens, context_limit, compressed, created_by, content_version, total_cost_micros, runtime_ms, deleted, provider_response_id, api_key_id, revision
FROM
chat_messages
WHERE
@@ -7393,6 +7870,7 @@ func (q *sqlQuerier) GetChatMessageByID(ctx context.Context, id int64) (ChatMess
&i.Deleted,
&i.ProviderResponseID,
&i.APIKeyID,
+ &i.Revision,
)
return i, err
}
@@ -7482,7 +7960,7 @@ func (q *sqlQuerier) GetChatMessageSummariesPerChat(ctx context.Context, created
const getChatMessagesByChatID = `-- name: GetChatMessagesByChatID :many
SELECT
- id, chat_id, model_config_id, created_at, role, content, visibility, input_tokens, output_tokens, total_tokens, reasoning_tokens, cache_creation_tokens, cache_read_tokens, context_limit, compressed, created_by, content_version, total_cost_micros, runtime_ms, deleted, provider_response_id, api_key_id
+ id, chat_id, model_config_id, created_at, role, content, visibility, input_tokens, output_tokens, total_tokens, reasoning_tokens, cache_creation_tokens, cache_read_tokens, context_limit, compressed, created_by, content_version, total_cost_micros, runtime_ms, deleted, provider_response_id, api_key_id, revision
FROM
chat_messages
WHERE
@@ -7531,6 +8009,7 @@ func (q *sqlQuerier) GetChatMessagesByChatID(ctx context.Context, arg GetChatMes
&i.Deleted,
&i.ProviderResponseID,
&i.APIKeyID,
+ &i.Revision,
); err != nil {
return nil, err
}
@@ -7547,7 +8026,7 @@ func (q *sqlQuerier) GetChatMessagesByChatID(ctx context.Context, arg GetChatMes
const getChatMessagesByChatIDAscPaginated = `-- name: GetChatMessagesByChatIDAscPaginated :many
SELECT
- id, chat_id, model_config_id, created_at, role, content, visibility, input_tokens, output_tokens, total_tokens, reasoning_tokens, cache_creation_tokens, cache_read_tokens, context_limit, compressed, created_by, content_version, total_cost_micros, runtime_ms, deleted, provider_response_id, api_key_id
+ id, chat_id, model_config_id, created_at, role, content, visibility, input_tokens, output_tokens, total_tokens, reasoning_tokens, cache_creation_tokens, cache_read_tokens, context_limit, compressed, created_by, content_version, total_cost_micros, runtime_ms, deleted, provider_response_id, api_key_id, revision
FROM
chat_messages
WHERE
@@ -7599,6 +8078,7 @@ func (q *sqlQuerier) GetChatMessagesByChatIDAscPaginated(ctx context.Context, ar
&i.Deleted,
&i.ProviderResponseID,
&i.APIKeyID,
+ &i.Revision,
); err != nil {
return nil, err
}
@@ -7615,7 +8095,7 @@ func (q *sqlQuerier) GetChatMessagesByChatIDAscPaginated(ctx context.Context, ar
const getChatMessagesByChatIDDescPaginated = `-- name: GetChatMessagesByChatIDDescPaginated :many
SELECT
- id, chat_id, model_config_id, created_at, role, content, visibility, input_tokens, output_tokens, total_tokens, reasoning_tokens, cache_creation_tokens, cache_read_tokens, context_limit, compressed, created_by, content_version, total_cost_micros, runtime_ms, deleted, provider_response_id, api_key_id
+ id, chat_id, model_config_id, created_at, role, content, visibility, input_tokens, output_tokens, total_tokens, reasoning_tokens, cache_creation_tokens, cache_read_tokens, context_limit, compressed, created_by, content_version, total_cost_micros, runtime_ms, deleted, provider_response_id, api_key_id, revision
FROM
chat_messages
WHERE
@@ -7680,6 +8160,72 @@ func (q *sqlQuerier) GetChatMessagesByChatIDDescPaginated(ctx context.Context, a
&i.Deleted,
&i.ProviderResponseID,
&i.APIKeyID,
+ &i.Revision,
+ ); err != nil {
+ return nil, err
+ }
+ items = append(items, i)
+ }
+ if err := rows.Close(); err != nil {
+ return nil, err
+ }
+ if err := rows.Err(); err != nil {
+ return nil, err
+ }
+ return items, nil
+}
+
+const getChatMessagesByRevisionForStream = `-- name: GetChatMessagesByRevisionForStream :many
+SELECT
+ id, chat_id, model_config_id, created_at, role, content, visibility, input_tokens, output_tokens, total_tokens, reasoning_tokens, cache_creation_tokens, cache_read_tokens, context_limit, compressed, created_by, content_version, total_cost_micros, runtime_ms, deleted, provider_response_id, api_key_id, revision
+FROM
+ chat_messages
+WHERE
+ chat_id = $1::uuid
+ AND revision > $2::bigint
+ AND visibility IN ('user', 'both')
+ORDER BY
+ created_at ASC, id ASC
+`
+
+type GetChatMessagesByRevisionForStreamParams struct {
+ ChatID uuid.UUID `db:"chat_id" json:"chat_id"`
+ AfterRevision int64 `db:"after_revision" json:"after_revision"`
+}
+
+func (q *sqlQuerier) GetChatMessagesByRevisionForStream(ctx context.Context, arg GetChatMessagesByRevisionForStreamParams) ([]ChatMessage, error) {
+ rows, err := q.db.QueryContext(ctx, getChatMessagesByRevisionForStream, arg.ChatID, arg.AfterRevision)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+ var items []ChatMessage
+ for rows.Next() {
+ var i ChatMessage
+ if err := rows.Scan(
+ &i.ID,
+ &i.ChatID,
+ &i.ModelConfigID,
+ &i.CreatedAt,
+ &i.Role,
+ &i.Content,
+ &i.Visibility,
+ &i.InputTokens,
+ &i.OutputTokens,
+ &i.TotalTokens,
+ &i.ReasoningTokens,
+ &i.CacheCreationTokens,
+ &i.CacheReadTokens,
+ &i.ContextLimit,
+ &i.Compressed,
+ &i.CreatedBy,
+ &i.ContentVersion,
+ &i.TotalCostMicros,
+ &i.RuntimeMs,
+ &i.Deleted,
+ &i.ProviderResponseID,
+ &i.APIKeyID,
+ &i.Revision,
); err != nil {
return nil, err
}
@@ -7712,7 +8258,7 @@ WITH latest_compressed_summary AS (
1
)
SELECT
- id, chat_id, model_config_id, created_at, role, content, visibility, input_tokens, output_tokens, total_tokens, reasoning_tokens, cache_creation_tokens, cache_read_tokens, context_limit, compressed, created_by, content_version, total_cost_micros, runtime_ms, deleted, provider_response_id, api_key_id
+ id, chat_id, model_config_id, created_at, role, content, visibility, input_tokens, output_tokens, total_tokens, reasoning_tokens, cache_creation_tokens, cache_read_tokens, context_limit, compressed, created_by, content_version, total_cost_micros, runtime_ms, deleted, provider_response_id, api_key_id, revision
FROM
chat_messages
WHERE
@@ -7785,6 +8331,7 @@ func (q *sqlQuerier) GetChatMessagesForPromptByChatID(ctx context.Context, chatI
&i.Deleted,
&i.ProviderResponseID,
&i.APIKeyID,
+ &i.Revision,
); err != nil {
return nil, err
}
@@ -7845,8 +8392,58 @@ func (q *sqlQuerier) GetChatModelConfigsForTelemetry(ctx context.Context) ([]Get
return items, nil
}
+const getChatQueuedMessageByID = `-- name: GetChatQueuedMessageByID :one
+SELECT id, chat_id, content, created_at, model_config_id, api_key_id, position, created_by FROM chat_queued_messages
+WHERE id = $1::bigint AND chat_id = $2::uuid
+`
+
+type GetChatQueuedMessageByIDParams struct {
+ ID int64 `db:"id" json:"id"`
+ ChatID uuid.UUID `db:"chat_id" json:"chat_id"`
+}
+
+func (q *sqlQuerier) GetChatQueuedMessageByID(ctx context.Context, arg GetChatQueuedMessageByIDParams) (ChatQueuedMessage, error) {
+ row := q.db.QueryRowContext(ctx, getChatQueuedMessageByID, arg.ID, arg.ChatID)
+ var i ChatQueuedMessage
+ err := row.Scan(
+ &i.ID,
+ &i.ChatID,
+ &i.Content,
+ &i.CreatedAt,
+ &i.ModelConfigID,
+ &i.APIKeyID,
+ &i.Position,
+ &i.CreatedBy,
+ )
+ return i, err
+}
+
+const getChatQueuedMessageHead = `-- name: GetChatQueuedMessageHead :one
+SELECT id, chat_id, content, created_at, model_config_id, api_key_id, position, created_by FROM chat_queued_messages
+WHERE chat_id = $1::uuid
+ORDER BY position ASC, id ASC
+LIMIT 1
+`
+
+// Returns the queue head (lowest position, then lowest id).
+func (q *sqlQuerier) GetChatQueuedMessageHead(ctx context.Context, chatID uuid.UUID) (ChatQueuedMessage, error) {
+ row := q.db.QueryRowContext(ctx, getChatQueuedMessageHead, chatID)
+ var i ChatQueuedMessage
+ err := row.Scan(
+ &i.ID,
+ &i.ChatID,
+ &i.Content,
+ &i.CreatedAt,
+ &i.ModelConfigID,
+ &i.APIKeyID,
+ &i.Position,
+ &i.CreatedBy,
+ )
+ return i, err
+}
+
const getChatQueuedMessages = `-- name: GetChatQueuedMessages :many
-SELECT id, chat_id, content, created_at, model_config_id, api_key_id FROM chat_queued_messages
+SELECT id, chat_id, content, created_at, model_config_id, api_key_id, position, created_by FROM chat_queued_messages
WHERE chat_id = $1
ORDER BY created_at ASC, id ASC
`
@@ -7867,6 +8464,105 @@ func (q *sqlQuerier) GetChatQueuedMessages(ctx context.Context, chatID uuid.UUID
&i.CreatedAt,
&i.ModelConfigID,
&i.APIKeyID,
+ &i.Position,
+ &i.CreatedBy,
+ ); err != nil {
+ return nil, err
+ }
+ items = append(items, i)
+ }
+ if err := rows.Close(); err != nil {
+ return nil, err
+ }
+ if err := rows.Err(); err != nil {
+ return nil, err
+ }
+ return items, nil
+}
+
+const getChatQueuedMessagesByPosition = `-- name: GetChatQueuedMessagesByPosition :many
+SELECT id, chat_id, content, created_at, model_config_id, api_key_id, position, created_by FROM chat_queued_messages
+WHERE chat_id = $1::uuid
+ORDER BY position ASC, id ASC
+`
+
+// Returns queued messages in state-machine order (position ASC, id ASC).
+func (q *sqlQuerier) GetChatQueuedMessagesByPosition(ctx context.Context, chatID uuid.UUID) ([]ChatQueuedMessage, error) {
+ rows, err := q.db.QueryContext(ctx, getChatQueuedMessagesByPosition, chatID)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+ var items []ChatQueuedMessage
+ for rows.Next() {
+ var i ChatQueuedMessage
+ if err := rows.Scan(
+ &i.ID,
+ &i.ChatID,
+ &i.Content,
+ &i.CreatedAt,
+ &i.ModelConfigID,
+ &i.APIKeyID,
+ &i.Position,
+ &i.CreatedBy,
+ ); err != nil {
+ return nil, err
+ }
+ items = append(items, i)
+ }
+ if err := rows.Close(); err != nil {
+ return nil, err
+ }
+ if err := rows.Err(); err != nil {
+ return nil, err
+ }
+ return items, nil
+}
+
+const getChatStreamSyncRows = `-- name: GetChatStreamSyncRows :many
+SELECT
+ id,
+ snapshot_version,
+ history_version,
+ queue_version,
+ retry_state_version,
+ generation_attempt,
+ status,
+ worker_id
+FROM chats
+WHERE id = ANY($1::uuid[])
+ORDER BY id ASC
+`
+
+type GetChatStreamSyncRowsRow struct {
+ ID uuid.UUID `db:"id" json:"id"`
+ SnapshotVersion int64 `db:"snapshot_version" json:"snapshot_version"`
+ HistoryVersion int64 `db:"history_version" json:"history_version"`
+ QueueVersion int64 `db:"queue_version" json:"queue_version"`
+ RetryStateVersion int64 `db:"retry_state_version" json:"retry_state_version"`
+ GenerationAttempt int64 `db:"generation_attempt" json:"generation_attempt"`
+ Status ChatStatus `db:"status" json:"status"`
+ WorkerID uuid.NullUUID `db:"worker_id" json:"worker_id"`
+}
+
+func (q *sqlQuerier) GetChatStreamSyncRows(ctx context.Context, ids []uuid.UUID) ([]GetChatStreamSyncRowsRow, error) {
+ rows, err := q.db.QueryContext(ctx, getChatStreamSyncRows, pq.Array(ids))
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+ var items []GetChatStreamSyncRowsRow
+ for rows.Next() {
+ var i GetChatStreamSyncRowsRow
+ if err := rows.Scan(
+ &i.ID,
+ &i.SnapshotVersion,
+ &i.HistoryVersion,
+ &i.QueueVersion,
+ &i.RetryStateVersion,
+ &i.GenerationAttempt,
+ &i.Status,
+ &i.WorkerID,
); err != nil {
return nil, err
}
@@ -8002,6 +8698,160 @@ func (q *sqlQuerier) GetChatUserPromptsByChatID(ctx context.Context, arg GetChat
return items, nil
}
+const getChatWorkerAcquisitionCandidates = `-- name: GetChatWorkerAcquisitionCandidates :many
+SELECT
+ chats_expanded.id, chats_expanded.owner_id, chats_expanded.workspace_id, chats_expanded.title, chats_expanded.status, chats_expanded.worker_id, chats_expanded.started_at, chats_expanded.heartbeat_at, chats_expanded.created_at, chats_expanded.updated_at, chats_expanded.parent_chat_id, chats_expanded.root_chat_id, chats_expanded.last_model_config_id, chats_expanded.archived, chats_expanded.last_error, chats_expanded.mode, chats_expanded.mcp_server_ids, chats_expanded.labels, chats_expanded.build_id, chats_expanded.agent_id, chats_expanded.pin_order, chats_expanded.last_read_message_id, chats_expanded.last_injected_context, chats_expanded.dynamic_tools, chats_expanded.organization_id, chats_expanded.plan_mode, chats_expanded.client_type, chats_expanded.last_turn_summary, chats_expanded.snapshot_version, chats_expanded.history_version, chats_expanded.queue_version, chats_expanded.generation_attempt, chats_expanded.retry_state, chats_expanded.retry_state_version, chats_expanded.runner_id, chats_expanded.requires_action_deadline_at, chats_expanded.user_acl, chats_expanded.group_acl, chats_expanded.owner_username, chats_expanded.owner_name,
+ chat_heartbeats.heartbeat_at AS current_heartbeat_at,
+ NOT EXISTS (
+ SELECT 1
+ FROM chat_heartbeats current_lease
+ WHERE current_lease.chat_id = chats_expanded.id
+ AND current_lease.runner_id = chats_expanded.runner_id
+ AND current_lease.heartbeat_at > NOW() - (INTERVAL '1 second' * $1::int)
+ ) AS heartbeat_stale
+FROM chats_expanded
+LEFT JOIN chat_heartbeats
+ ON chat_heartbeats.chat_id = chats_expanded.id
+ AND chat_heartbeats.runner_id = chats_expanded.runner_id
+WHERE
+ chats_expanded.status IN ('running'::chat_status, 'interrupting'::chat_status, 'requires_action'::chat_status)
+ AND chats_expanded.archived = false
+ AND (
+ chats_expanded.worker_id IS NULL
+ OR chats_expanded.runner_id IS NULL
+ OR NOT EXISTS (
+ SELECT 1
+ FROM chat_heartbeats current_lease
+ WHERE current_lease.chat_id = chats_expanded.id
+ AND current_lease.runner_id = chats_expanded.runner_id
+ AND current_lease.heartbeat_at > NOW() - (INTERVAL '1 second' * $1::int)
+ )
+ )
+ORDER BY chats_expanded.updated_at ASC, chats_expanded.id ASC
+LIMIT $2::int
+`
+
+type GetChatWorkerAcquisitionCandidatesParams struct {
+ StaleSeconds int32 `db:"stale_seconds" json:"stale_seconds"`
+ LimitCount int32 `db:"limit_count" json:"limit_count"`
+}
+
+type GetChatWorkerAcquisitionCandidatesRow 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"`
+ Title string `db:"title" json:"title"`
+ Status ChatStatus `db:"status" json:"status"`
+ WorkerID uuid.NullUUID `db:"worker_id" json:"worker_id"`
+ StartedAt sql.NullTime `db:"started_at" json:"started_at"`
+ HeartbeatAt sql.NullTime `db:"heartbeat_at" json:"heartbeat_at"`
+ CreatedAt time.Time `db:"created_at" json:"created_at"`
+ UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
+ 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"`
+ Archived bool `db:"archived" json:"archived"`
+ LastError pqtype.NullRawMessage `db:"last_error" json:"last_error"`
+ Mode NullChatMode `db:"mode" json:"mode"`
+ MCPServerIDs []uuid.UUID `db:"mcp_server_ids" json:"mcp_server_ids"`
+ Labels StringMap `db:"labels" json:"labels"`
+ BuildID uuid.NullUUID `db:"build_id" json:"build_id"`
+ AgentID uuid.NullUUID `db:"agent_id" json:"agent_id"`
+ PinOrder int32 `db:"pin_order" json:"pin_order"`
+ LastReadMessageID sql.NullInt64 `db:"last_read_message_id" json:"last_read_message_id"`
+ LastInjectedContext pqtype.NullRawMessage `db:"last_injected_context" json:"last_injected_context"`
+ DynamicTools pqtype.NullRawMessage `db:"dynamic_tools" json:"dynamic_tools"`
+ OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"`
+ PlanMode NullChatPlanMode `db:"plan_mode" json:"plan_mode"`
+ ClientType ChatClientType `db:"client_type" json:"client_type"`
+ LastTurnSummary sql.NullString `db:"last_turn_summary" json:"last_turn_summary"`
+ SnapshotVersion int64 `db:"snapshot_version" json:"snapshot_version"`
+ HistoryVersion int64 `db:"history_version" json:"history_version"`
+ QueueVersion int64 `db:"queue_version" json:"queue_version"`
+ GenerationAttempt int64 `db:"generation_attempt" json:"generation_attempt"`
+ RetryState pqtype.NullRawMessage `db:"retry_state" json:"retry_state"`
+ RetryStateVersion int64 `db:"retry_state_version" json:"retry_state_version"`
+ RunnerID uuid.NullUUID `db:"runner_id" json:"runner_id"`
+ RequiresActionDeadlineAt sql.NullTime `db:"requires_action_deadline_at" json:"requires_action_deadline_at"`
+ UserACL ChatACL `db:"user_acl" json:"user_acl"`
+ GroupACL ChatACL `db:"group_acl" json:"group_acl"`
+ OwnerUsername string `db:"owner_username" json:"owner_username"`
+ OwnerName string `db:"owner_name" json:"owner_name"`
+ CurrentHeartbeatAt sql.NullTime `db:"current_heartbeat_at" json:"current_heartbeat_at"`
+ HeartbeatStale bool `db:"heartbeat_stale" json:"heartbeat_stale"`
+}
+
+// Returns worker-runnable chats whose ownership is missing or whose
+// current runner heartbeat is stale. The runner_id IS NULL predicate is
+// a robustness extension for inconsistent rows where a worker_id exists
+// without a runner_id; normal missing ownership is worker_id IS NULL or
+// a missing or stale heartbeat row.
+func (q *sqlQuerier) GetChatWorkerAcquisitionCandidates(ctx context.Context, arg GetChatWorkerAcquisitionCandidatesParams) ([]GetChatWorkerAcquisitionCandidatesRow, error) {
+ rows, err := q.db.QueryContext(ctx, getChatWorkerAcquisitionCandidates, arg.StaleSeconds, arg.LimitCount)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+ var items []GetChatWorkerAcquisitionCandidatesRow
+ for rows.Next() {
+ var i GetChatWorkerAcquisitionCandidatesRow
+ if err := rows.Scan(
+ &i.ID,
+ &i.OwnerID,
+ &i.WorkspaceID,
+ &i.Title,
+ &i.Status,
+ &i.WorkerID,
+ &i.StartedAt,
+ &i.HeartbeatAt,
+ &i.CreatedAt,
+ &i.UpdatedAt,
+ &i.ParentChatID,
+ &i.RootChatID,
+ &i.LastModelConfigID,
+ &i.Archived,
+ &i.LastError,
+ &i.Mode,
+ pq.Array(&i.MCPServerIDs),
+ &i.Labels,
+ &i.BuildID,
+ &i.AgentID,
+ &i.PinOrder,
+ &i.LastReadMessageID,
+ &i.LastInjectedContext,
+ &i.DynamicTools,
+ &i.OrganizationID,
+ &i.PlanMode,
+ &i.ClientType,
+ &i.LastTurnSummary,
+ &i.SnapshotVersion,
+ &i.HistoryVersion,
+ &i.QueueVersion,
+ &i.GenerationAttempt,
+ &i.RetryState,
+ &i.RetryStateVersion,
+ &i.RunnerID,
+ &i.RequiresActionDeadlineAt,
+ &i.UserACL,
+ &i.GroupACL,
+ &i.OwnerUsername,
+ &i.OwnerName,
+ &i.CurrentHeartbeatAt,
+ &i.HeartbeatStale,
+ ); err != nil {
+ return nil, err
+ }
+ items = append(items, i)
+ }
+ if err := rows.Close(); err != nil {
+ return nil, err
+ }
+ if err := rows.Err(); err != nil {
+ return nil, err
+ }
+ return items, nil
+}
+
const getChats = `-- name: GetChats :many
WITH cursor_chat AS (
SELECT
@@ -8012,7 +8862,7 @@ WITH cursor_chat AS (
WHERE id = $5
)
SELECT
- chats_expanded.id, chats_expanded.owner_id, chats_expanded.workspace_id, chats_expanded.title, chats_expanded.status, chats_expanded.worker_id, chats_expanded.started_at, chats_expanded.heartbeat_at, chats_expanded.created_at, chats_expanded.updated_at, chats_expanded.parent_chat_id, chats_expanded.root_chat_id, chats_expanded.last_model_config_id, chats_expanded.archived, chats_expanded.last_error, chats_expanded.mode, chats_expanded.mcp_server_ids, chats_expanded.labels, chats_expanded.build_id, chats_expanded.agent_id, chats_expanded.pin_order, chats_expanded.last_read_message_id, chats_expanded.last_injected_context, chats_expanded.dynamic_tools, chats_expanded.organization_id, chats_expanded.plan_mode, chats_expanded.client_type, chats_expanded.last_turn_summary, chats_expanded.user_acl, chats_expanded.group_acl, chats_expanded.owner_username, chats_expanded.owner_name,
+ chats_expanded.id, chats_expanded.owner_id, chats_expanded.workspace_id, chats_expanded.title, chats_expanded.status, chats_expanded.worker_id, chats_expanded.started_at, chats_expanded.heartbeat_at, chats_expanded.created_at, chats_expanded.updated_at, chats_expanded.parent_chat_id, chats_expanded.root_chat_id, chats_expanded.last_model_config_id, chats_expanded.archived, chats_expanded.last_error, chats_expanded.mode, chats_expanded.mcp_server_ids, chats_expanded.labels, chats_expanded.build_id, chats_expanded.agent_id, chats_expanded.pin_order, chats_expanded.last_read_message_id, chats_expanded.last_injected_context, chats_expanded.dynamic_tools, chats_expanded.organization_id, chats_expanded.plan_mode, chats_expanded.client_type, chats_expanded.last_turn_summary, chats_expanded.snapshot_version, chats_expanded.history_version, chats_expanded.queue_version, chats_expanded.generation_attempt, chats_expanded.retry_state, chats_expanded.retry_state_version, chats_expanded.runner_id, chats_expanded.requires_action_deadline_at, chats_expanded.user_acl, chats_expanded.group_acl, chats_expanded.owner_username, chats_expanded.owner_name,
EXISTS (
SELECT 1 FROM chat_messages cm
WHERE cm.chat_id = chats_expanded.id
@@ -8244,6 +9094,14 @@ func (q *sqlQuerier) GetChats(ctx context.Context, arg GetChatsParams) ([]GetCha
&i.Chat.PlanMode,
&i.Chat.ClientType,
&i.Chat.LastTurnSummary,
+ &i.Chat.SnapshotVersion,
+ &i.Chat.HistoryVersion,
+ &i.Chat.QueueVersion,
+ &i.Chat.GenerationAttempt,
+ &i.Chat.RetryState,
+ &i.Chat.RetryStateVersion,
+ &i.Chat.RunnerID,
+ &i.Chat.RequiresActionDeadlineAt,
&i.Chat.UserACL,
&i.Chat.GroupACL,
&i.Chat.OwnerUsername,
@@ -8265,7 +9123,7 @@ func (q *sqlQuerier) GetChats(ctx context.Context, arg GetChatsParams) ([]GetCha
const getChatsByChatFileID = `-- name: GetChatsByChatFileID :many
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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, user_acl, group_acl, owner_username, owner_name
+ 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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, snapshot_version, history_version, queue_version, generation_attempt, retry_state, retry_state_version, runner_id, requires_action_deadline_at, user_acl, group_acl, owner_username, owner_name
FROM
chats_expanded
WHERE
@@ -8316,6 +9174,85 @@ func (q *sqlQuerier) GetChatsByChatFileID(ctx context.Context, fileID uuid.UUID)
&i.PlanMode,
&i.ClientType,
&i.LastTurnSummary,
+ &i.SnapshotVersion,
+ &i.HistoryVersion,
+ &i.QueueVersion,
+ &i.GenerationAttempt,
+ &i.RetryState,
+ &i.RetryStateVersion,
+ &i.RunnerID,
+ &i.RequiresActionDeadlineAt,
+ &i.UserACL,
+ &i.GroupACL,
+ &i.OwnerUsername,
+ &i.OwnerName,
+ ); err != nil {
+ return nil, err
+ }
+ items = append(items, i)
+ }
+ if err := rows.Close(); err != nil {
+ return nil, err
+ }
+ if err := rows.Err(); err != nil {
+ return nil, err
+ }
+ return items, nil
+}
+
+const getChatsByIDsForRunnerSync = `-- name: GetChatsByIDsForRunnerSync :many
+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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, snapshot_version, history_version, queue_version, generation_attempt, retry_state, retry_state_version, runner_id, requires_action_deadline_at, user_acl, group_acl, owner_username, owner_name
+FROM chats_expanded
+WHERE id = ANY($1::uuid[])
+ORDER BY id ASC
+`
+
+func (q *sqlQuerier) GetChatsByIDsForRunnerSync(ctx context.Context, ids []uuid.UUID) ([]Chat, error) {
+ rows, err := q.db.QueryContext(ctx, getChatsByIDsForRunnerSync, pq.Array(ids))
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+ var items []Chat
+ for rows.Next() {
+ var i Chat
+ if err := rows.Scan(
+ &i.ID,
+ &i.OwnerID,
+ &i.WorkspaceID,
+ &i.Title,
+ &i.Status,
+ &i.WorkerID,
+ &i.StartedAt,
+ &i.HeartbeatAt,
+ &i.CreatedAt,
+ &i.UpdatedAt,
+ &i.ParentChatID,
+ &i.RootChatID,
+ &i.LastModelConfigID,
+ &i.Archived,
+ &i.LastError,
+ &i.Mode,
+ pq.Array(&i.MCPServerIDs),
+ &i.Labels,
+ &i.BuildID,
+ &i.AgentID,
+ &i.PinOrder,
+ &i.LastReadMessageID,
+ &i.LastInjectedContext,
+ &i.DynamicTools,
+ &i.OrganizationID,
+ &i.PlanMode,
+ &i.ClientType,
+ &i.LastTurnSummary,
+ &i.SnapshotVersion,
+ &i.HistoryVersion,
+ &i.QueueVersion,
+ &i.GenerationAttempt,
+ &i.RetryState,
+ &i.RetryStateVersion,
+ &i.RunnerID,
+ &i.RequiresActionDeadlineAt,
&i.UserACL,
&i.GroupACL,
&i.OwnerUsername,
@@ -8335,7 +9272,7 @@ func (q *sqlQuerier) GetChatsByChatFileID(ctx context.Context, fileID uuid.UUID)
}
const getChatsByWorkspaceIDs = `-- name: GetChatsByWorkspaceIDs :many
-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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, user_acl, group_acl, owner_username, owner_name
+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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, snapshot_version, history_version, queue_version, generation_attempt, retry_state, retry_state_version, runner_id, requires_action_deadline_at, user_acl, group_acl, owner_username, owner_name
FROM chats_expanded
WHERE archived = false
AND workspace_id = ANY($1::uuid[])
@@ -8380,6 +9317,14 @@ func (q *sqlQuerier) GetChatsByWorkspaceIDs(ctx context.Context, ids []uuid.UUID
&i.PlanMode,
&i.ClientType,
&i.LastTurnSummary,
+ &i.SnapshotVersion,
+ &i.HistoryVersion,
+ &i.QueueVersion,
+ &i.GenerationAttempt,
+ &i.RetryState,
+ &i.RetryStateVersion,
+ &i.RunnerID,
+ &i.RequiresActionDeadlineAt,
&i.UserACL,
&i.GroupACL,
&i.OwnerUsername,
@@ -8468,7 +9413,7 @@ func (q *sqlQuerier) GetChatsUpdatedAfter(ctx context.Context, updatedAfter time
const getChildChatsByParentIDs = `-- name: GetChildChatsByParentIDs :many
SELECT
- chats_expanded.id, chats_expanded.owner_id, chats_expanded.workspace_id, chats_expanded.title, chats_expanded.status, chats_expanded.worker_id, chats_expanded.started_at, chats_expanded.heartbeat_at, chats_expanded.created_at, chats_expanded.updated_at, chats_expanded.parent_chat_id, chats_expanded.root_chat_id, chats_expanded.last_model_config_id, chats_expanded.archived, chats_expanded.last_error, chats_expanded.mode, chats_expanded.mcp_server_ids, chats_expanded.labels, chats_expanded.build_id, chats_expanded.agent_id, chats_expanded.pin_order, chats_expanded.last_read_message_id, chats_expanded.last_injected_context, chats_expanded.dynamic_tools, chats_expanded.organization_id, chats_expanded.plan_mode, chats_expanded.client_type, chats_expanded.last_turn_summary, chats_expanded.user_acl, chats_expanded.group_acl, chats_expanded.owner_username, chats_expanded.owner_name,
+ chats_expanded.id, chats_expanded.owner_id, chats_expanded.workspace_id, chats_expanded.title, chats_expanded.status, chats_expanded.worker_id, chats_expanded.started_at, chats_expanded.heartbeat_at, chats_expanded.created_at, chats_expanded.updated_at, chats_expanded.parent_chat_id, chats_expanded.root_chat_id, chats_expanded.last_model_config_id, chats_expanded.archived, chats_expanded.last_error, chats_expanded.mode, chats_expanded.mcp_server_ids, chats_expanded.labels, chats_expanded.build_id, chats_expanded.agent_id, chats_expanded.pin_order, chats_expanded.last_read_message_id, chats_expanded.last_injected_context, chats_expanded.dynamic_tools, chats_expanded.organization_id, chats_expanded.plan_mode, chats_expanded.client_type, chats_expanded.last_turn_summary, chats_expanded.snapshot_version, chats_expanded.history_version, chats_expanded.queue_version, chats_expanded.generation_attempt, chats_expanded.retry_state, chats_expanded.retry_state_version, chats_expanded.runner_id, chats_expanded.requires_action_deadline_at, chats_expanded.user_acl, chats_expanded.group_acl, chats_expanded.owner_username, chats_expanded.owner_name,
EXISTS (
SELECT 1 FROM chat_messages cm
WHERE cm.chat_id = chats_expanded.id
@@ -8541,6 +9486,14 @@ func (q *sqlQuerier) GetChildChatsByParentIDs(ctx context.Context, arg GetChildC
&i.Chat.PlanMode,
&i.Chat.ClientType,
&i.Chat.LastTurnSummary,
+ &i.Chat.SnapshotVersion,
+ &i.Chat.HistoryVersion,
+ &i.Chat.QueueVersion,
+ &i.Chat.GenerationAttempt,
+ &i.Chat.RetryState,
+ &i.Chat.RetryStateVersion,
+ &i.Chat.RunnerID,
+ &i.Chat.RequiresActionDeadlineAt,
&i.Chat.UserACL,
&i.Chat.GroupACL,
&i.Chat.OwnerUsername,
@@ -8560,9 +9513,23 @@ func (q *sqlQuerier) GetChildChatsByParentIDs(ctx context.Context, arg GetChildC
return items, nil
}
+const getDatabaseNow = `-- name: GetDatabaseNow :one
+SELECT NOW()::timestamptz AS now
+`
+
+// Returns the current database timestamp. Used so transitions that
+// record deadlines or heartbeats rely on a clock that is consistent
+// with the database rather than the caller's local clock.
+func (q *sqlQuerier) GetDatabaseNow(ctx context.Context) (time.Time, error) {
+ row := q.db.QueryRowContext(ctx, getDatabaseNow)
+ var now time.Time
+ err := row.Scan(&now)
+ return now, err
+}
+
const getLastChatMessageByRole = `-- name: GetLastChatMessageByRole :one
SELECT
- id, chat_id, model_config_id, created_at, role, content, visibility, input_tokens, output_tokens, total_tokens, reasoning_tokens, cache_creation_tokens, cache_read_tokens, context_limit, compressed, created_by, content_version, total_cost_micros, runtime_ms, deleted, provider_response_id, api_key_id
+ id, chat_id, model_config_id, created_at, role, content, visibility, input_tokens, output_tokens, total_tokens, reasoning_tokens, cache_creation_tokens, cache_read_tokens, context_limit, compressed, created_by, content_version, total_cost_micros, runtime_ms, deleted, provider_response_id, api_key_id, revision
FROM
chat_messages
WHERE
@@ -8606,13 +9573,14 @@ func (q *sqlQuerier) GetLastChatMessageByRole(ctx context.Context, arg GetLastCh
&i.Deleted,
&i.ProviderResponseID,
&i.APIKeyID,
+ &i.Revision,
)
return i, err
}
const getStaleChats = `-- name: GetStaleChats :many
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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, user_acl, group_acl, owner_username, owner_name
+ 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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, snapshot_version, history_version, queue_version, generation_attempt, retry_state, retry_state_version, runner_id, requires_action_deadline_at, user_acl, group_acl, owner_username, owner_name
FROM
chats_expanded
WHERE
@@ -8673,6 +9641,14 @@ func (q *sqlQuerier) GetStaleChats(ctx context.Context, staleThreshold time.Time
&i.PlanMode,
&i.ClientType,
&i.LastTurnSummary,
+ &i.SnapshotVersion,
+ &i.HistoryVersion,
+ &i.QueueVersion,
+ &i.GenerationAttempt,
+ &i.RetryState,
+ &i.RetryStateVersion,
+ &i.RunnerID,
+ &i.RequiresActionDeadlineAt,
&i.UserACL,
&i.GroupACL,
&i.OwnerUsername,
@@ -8753,6 +9729,21 @@ func (q *sqlQuerier) GetUserGroupSpendLimit(ctx context.Context, arg GetUserGrou
return limit_micros, err
}
+const incrementChatGenerationAttempt = `-- name: IncrementChatGenerationAttempt :one
+UPDATE chats
+SET generation_attempt = generation_attempt + 1, updated_at = NOW()
+WHERE id = $1::uuid
+RETURNING generation_attempt
+`
+
+// Increments generation_attempt and returns the resulting value.
+func (q *sqlQuerier) IncrementChatGenerationAttempt(ctx context.Context, id uuid.UUID) (int64, error) {
+ row := q.db.QueryRowContext(ctx, incrementChatGenerationAttempt, id)
+ var generation_attempt int64
+ err := row.Scan(&generation_attempt)
+ return generation_attempt, err
+}
+
const insertChat = `-- name: InsertChat :one
WITH inserted_chat AS (
INSERT INTO chats (
@@ -8790,7 +9781,7 @@ INSERT INTO chats (
$15::jsonb,
$16::chat_client_type
)
-RETURNING 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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, user_acl, group_acl
+RETURNING 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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, user_acl, group_acl, snapshot_version, history_version, queue_version, generation_attempt, retry_state, retry_state_version, runner_id, requires_action_deadline_at
),
chats_expanded AS (
SELECT
@@ -8822,6 +9813,14 @@ chats_expanded AS (
inserted_chat.plan_mode,
inserted_chat.client_type,
inserted_chat.last_turn_summary,
+ inserted_chat.snapshot_version,
+ inserted_chat.history_version,
+ inserted_chat.queue_version,
+ inserted_chat.generation_attempt,
+ inserted_chat.retry_state,
+ inserted_chat.retry_state_version,
+ inserted_chat.runner_id,
+ inserted_chat.requires_action_deadline_at,
COALESCE(root.user_acl, inserted_chat.user_acl) AS user_acl,
COALESCE(root.group_acl, inserted_chat.group_acl) AS group_acl,
owner.username AS owner_username,
@@ -8831,7 +9830,7 @@ chats_expanded AS (
LEFT JOIN chats root ON root.id = COALESCE(inserted_chat.root_chat_id, inserted_chat.parent_chat_id)
JOIN visible_users owner ON owner.id = inserted_chat.owner_id
)
-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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, user_acl, group_acl, owner_username, owner_name
+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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, snapshot_version, history_version, queue_version, generation_attempt, retry_state, retry_state_version, runner_id, requires_action_deadline_at, user_acl, group_acl, owner_username, owner_name
FROM chats_expanded
`
@@ -8903,6 +9902,14 @@ func (q *sqlQuerier) InsertChat(ctx context.Context, arg InsertChatParams) (Chat
&i.PlanMode,
&i.ClientType,
&i.LastTurnSummary,
+ &i.SnapshotVersion,
+ &i.HistoryVersion,
+ &i.QueueVersion,
+ &i.GenerationAttempt,
+ &i.RetryState,
+ &i.RetryStateVersion,
+ &i.RunnerID,
+ &i.RequiresActionDeadlineAt,
&i.UserACL,
&i.GroupACL,
&i.OwnerUsername,
@@ -8982,7 +9989,7 @@ SELECT
NULLIF(UNNEST($18::bigint[]), 0),
NULLIF(UNNEST($19::text[]), '')
RETURNING
- id, chat_id, model_config_id, created_at, role, content, visibility, input_tokens, output_tokens, total_tokens, reasoning_tokens, cache_creation_tokens, cache_read_tokens, context_limit, compressed, created_by, content_version, total_cost_micros, runtime_ms, deleted, provider_response_id, api_key_id
+ id, chat_id, model_config_id, created_at, role, content, visibility, input_tokens, output_tokens, total_tokens, reasoning_tokens, cache_creation_tokens, cache_read_tokens, context_limit, compressed, created_by, content_version, total_cost_micros, runtime_ms, deleted, provider_response_id, api_key_id, revision
`
type InsertChatMessagesParams struct {
@@ -9059,6 +10066,7 @@ func (q *sqlQuerier) InsertChatMessages(ctx context.Context, arg InsertChatMessa
&i.Deleted,
&i.ProviderResponseID,
&i.APIKeyID,
+ &i.Revision,
); err != nil {
return nil, err
}
@@ -9074,14 +10082,16 @@ func (q *sqlQuerier) InsertChatMessages(ctx context.Context, arg InsertChatMessa
}
const insertChatQueuedMessage = `-- name: InsertChatQueuedMessage :one
-INSERT INTO chat_queued_messages (chat_id, content, model_config_id, api_key_id)
-VALUES (
- $1,
- $2,
+INSERT INTO chat_queued_messages (chat_id, content, model_config_id, api_key_id, created_by)
+SELECT
+ $1::uuid,
+ $2::jsonb,
$3::uuid,
- $4::text
-)
-RETURNING id, chat_id, content, created_at, model_config_id, api_key_id
+ $4::text,
+ chats.owner_id
+FROM chats
+WHERE chats.id = $1::uuid
+RETURNING id, chat_id, content, created_at, model_config_id, api_key_id, position, created_by
`
type InsertChatQueuedMessageParams struct {
@@ -9091,6 +10101,9 @@ type InsertChatQueuedMessageParams struct {
APIKeyID sql.NullString `db:"api_key_id" json:"api_key_id"`
}
+// Legacy queue insertion path. When no caller-supplied creator exists,
+// preserve the created_by invariant by attributing the queued row to the
+// chat owner.
func (q *sqlQuerier) InsertChatQueuedMessage(ctx context.Context, arg InsertChatQueuedMessageParams) (ChatQueuedMessage, error) {
row := q.db.QueryRowContext(ctx, insertChatQueuedMessage,
arg.ChatID,
@@ -9106,10 +10119,83 @@ func (q *sqlQuerier) InsertChatQueuedMessage(ctx context.Context, arg InsertChat
&i.CreatedAt,
&i.ModelConfigID,
&i.APIKeyID,
+ &i.Position,
+ &i.CreatedBy,
)
return i, err
}
+const insertChatQueuedMessageWithCreator = `-- name: InsertChatQueuedMessageWithCreator :one
+INSERT INTO chat_queued_messages (chat_id, content, model_config_id, api_key_id, created_by)
+VALUES (
+ $1::uuid,
+ $2::jsonb,
+ $3::uuid,
+ $4::text,
+ $5::uuid
+)
+RETURNING id, chat_id, content, created_at, model_config_id, api_key_id, position, created_by
+`
+
+type InsertChatQueuedMessageWithCreatorParams struct {
+ ChatID uuid.UUID `db:"chat_id" json:"chat_id"`
+ Content json.RawMessage `db:"content" json:"content"`
+ ModelConfigID uuid.NullUUID `db:"model_config_id" json:"model_config_id"`
+ APIKeyID sql.NullString `db:"api_key_id" json:"api_key_id"`
+ CreatedBy uuid.UUID `db:"created_by" json:"created_by"`
+}
+
+// Inserts a queued message that carries a position (from the default
+// sequence) and an explicit created_by reference. Use this when the
+// queued-message creator differs from the chat owner.
+func (q *sqlQuerier) InsertChatQueuedMessageWithCreator(ctx context.Context, arg InsertChatQueuedMessageWithCreatorParams) (ChatQueuedMessage, error) {
+ row := q.db.QueryRowContext(ctx, insertChatQueuedMessageWithCreator,
+ arg.ChatID,
+ arg.Content,
+ arg.ModelConfigID,
+ arg.APIKeyID,
+ arg.CreatedBy,
+ )
+ var i ChatQueuedMessage
+ err := row.Scan(
+ &i.ID,
+ &i.ChatID,
+ &i.Content,
+ &i.CreatedAt,
+ &i.ModelConfigID,
+ &i.APIKeyID,
+ &i.Position,
+ &i.CreatedBy,
+ )
+ return i, err
+}
+
+const isChatHeartbeatStale = `-- name: IsChatHeartbeatStale :one
+SELECT NOT EXISTS (
+ SELECT 1 FROM chat_heartbeats
+ WHERE chat_id = $1::uuid
+ AND runner_id = $2::uuid
+ AND heartbeat_at > NOW() - (INTERVAL '1 second' * $3::int)
+) AS stale
+`
+
+type IsChatHeartbeatStaleParams struct {
+ ChatID uuid.UUID `db:"chat_id" json:"chat_id"`
+ RunnerID uuid.UUID `db:"runner_id" json:"runner_id"`
+ StaleSeconds int32 `db:"stale_seconds" json:"stale_seconds"`
+}
+
+// Returns true when there is no heartbeat row for (chat_id, runner_id)
+// or the existing row is older than @stale_seconds seconds by database
+// time. chatstate calls this in a single query so the staleness check
+// is atomic and does not depend on the caller's local clock.
+func (q *sqlQuerier) IsChatHeartbeatStale(ctx context.Context, arg IsChatHeartbeatStaleParams) (bool, error) {
+ row := q.db.QueryRowContext(ctx, isChatHeartbeatStale, arg.ChatID, arg.RunnerID, arg.StaleSeconds)
+ var stale bool
+ err := row.Scan(&stale)
+ return stale, err
+}
+
const linkChatFiles = `-- name: LinkChatFiles :one
WITH current AS (
SELECT COUNT(*) AS cnt
@@ -9261,6 +10347,128 @@ func (q *sqlQuerier) ListChatUsageLimitOverrides(ctx context.Context) ([]ListCha
return items, nil
}
+const lockChatAndBumpSnapshotVersion = `-- name: LockChatAndBumpSnapshotVersion :one
+
+WITH bumped_chat AS (
+ UPDATE chats
+ SET snapshot_version = snapshot_version + 1
+ WHERE id = (
+ SELECT id FROM chats
+ WHERE id = $1::uuid
+ FOR UPDATE
+ )
+ RETURNING 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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, user_acl, group_acl, snapshot_version, history_version, queue_version, generation_attempt, retry_state, retry_state_version, runner_id, requires_action_deadline_at
+),
+chats_expanded AS (
+ SELECT
+ bumped_chat.id,
+ bumped_chat.owner_id,
+ bumped_chat.workspace_id,
+ bumped_chat.title,
+ bumped_chat.status,
+ bumped_chat.worker_id,
+ bumped_chat.started_at,
+ bumped_chat.heartbeat_at,
+ bumped_chat.created_at,
+ bumped_chat.updated_at,
+ bumped_chat.parent_chat_id,
+ bumped_chat.root_chat_id,
+ bumped_chat.last_model_config_id,
+ bumped_chat.archived,
+ bumped_chat.last_error,
+ bumped_chat.mode,
+ bumped_chat.mcp_server_ids,
+ bumped_chat.labels,
+ bumped_chat.build_id,
+ bumped_chat.agent_id,
+ bumped_chat.pin_order,
+ bumped_chat.last_read_message_id,
+ bumped_chat.last_injected_context,
+ bumped_chat.dynamic_tools,
+ bumped_chat.organization_id,
+ bumped_chat.plan_mode,
+ bumped_chat.client_type,
+ bumped_chat.last_turn_summary,
+ bumped_chat.snapshot_version,
+ bumped_chat.history_version,
+ bumped_chat.queue_version,
+ bumped_chat.generation_attempt,
+ bumped_chat.retry_state,
+ bumped_chat.retry_state_version,
+ bumped_chat.runner_id,
+ bumped_chat.requires_action_deadline_at,
+ COALESCE(root.user_acl, bumped_chat.user_acl) AS user_acl,
+ COALESCE(root.group_acl, bumped_chat.group_acl) AS group_acl,
+ owner.username AS owner_username,
+ owner.name AS owner_name
+ FROM bumped_chat
+ LEFT JOIN chats root ON root.id = COALESCE(bumped_chat.root_chat_id, bumped_chat.parent_chat_id)
+ JOIN visible_users owner ON owner.id = bumped_chat.owner_id
+)
+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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, snapshot_version, history_version, queue_version, generation_attempt, retry_state, retry_state_version, runner_id, requires_action_deadline_at, user_acl, group_acl, owner_username, owner_name
+FROM chats_expanded
+`
+
+// =====================================================================
+// chatd core state machine queries.
+//
+// These are consumed by the coderd/x/chatd/chatstate package. They
+// are intentionally kept side-by-side with the legacy chatd queries
+// above so the existing runtime keeps working while the state machine
+// lands behind it.
+// =====================================================================
+// Locks the chat row with FOR UPDATE and atomically increments its
+// snapshot_version, returning the post-bump chat. This is the single
+// entry point ChatMachine.Update uses to acquire the row lock and
+// allocate a new snapshot version in one round trip.
+func (q *sqlQuerier) LockChatAndBumpSnapshotVersion(ctx context.Context, id uuid.UUID) (Chat, error) {
+ row := q.db.QueryRowContext(ctx, lockChatAndBumpSnapshotVersion, id)
+ var i Chat
+ err := row.Scan(
+ &i.ID,
+ &i.OwnerID,
+ &i.WorkspaceID,
+ &i.Title,
+ &i.Status,
+ &i.WorkerID,
+ &i.StartedAt,
+ &i.HeartbeatAt,
+ &i.CreatedAt,
+ &i.UpdatedAt,
+ &i.ParentChatID,
+ &i.RootChatID,
+ &i.LastModelConfigID,
+ &i.Archived,
+ &i.LastError,
+ &i.Mode,
+ pq.Array(&i.MCPServerIDs),
+ &i.Labels,
+ &i.BuildID,
+ &i.AgentID,
+ &i.PinOrder,
+ &i.LastReadMessageID,
+ &i.LastInjectedContext,
+ &i.DynamicTools,
+ &i.OrganizationID,
+ &i.PlanMode,
+ &i.ClientType,
+ &i.LastTurnSummary,
+ &i.SnapshotVersion,
+ &i.HistoryVersion,
+ &i.QueueVersion,
+ &i.GenerationAttempt,
+ &i.RetryState,
+ &i.RetryStateVersion,
+ &i.RunnerID,
+ &i.RequiresActionDeadlineAt,
+ &i.UserACL,
+ &i.GroupACL,
+ &i.OwnerUsername,
+ &i.OwnerName,
+ )
+ return i, err
+}
+
const pinChatByID = `-- name: PinChatByID :exec
WITH target_chat AS (
SELECT
@@ -9330,7 +10538,7 @@ WHERE id = (
ORDER BY cqm.created_at ASC, cqm.id ASC
LIMIT 1
)
-RETURNING id, chat_id, content, created_at, model_config_id, api_key_id
+RETURNING id, chat_id, content, created_at, model_config_id, api_key_id, position, created_by
`
func (q *sqlQuerier) PopNextQueuedMessage(ctx context.Context, chatID uuid.UUID) (ChatQueuedMessage, error) {
@@ -9343,6 +10551,8 @@ func (q *sqlQuerier) PopNextQueuedMessage(ctx context.Context, chatID uuid.UUID)
&i.CreatedAt,
&i.ModelConfigID,
&i.APIKeyID,
+ &i.Position,
+ &i.CreatedBy,
)
return i, err
}
@@ -9372,6 +10582,35 @@ func (q *sqlQuerier) ReorderChatQueuedMessageToFront(ctx context.Context, arg Re
return result.RowsAffected()
}
+const reorderChatQueuedMessageToHead = `-- name: ReorderChatQueuedMessageToHead :execrows
+UPDATE chat_queued_messages AS target
+SET position = COALESCE(
+ (SELECT MIN(position) FROM chat_queued_messages WHERE chat_id = $1::uuid),
+ 0
+) - 1
+WHERE target.id = $2::bigint
+ AND target.chat_id = $1::uuid
+ AND target.position > COALESCE(
+ (SELECT MIN(position) FROM chat_queued_messages WHERE chat_id = $1::uuid),
+ target.position
+ )
+`
+
+type ReorderChatQueuedMessageToHeadParams struct {
+ ChatID uuid.UUID `db:"chat_id" json:"chat_id"`
+ ID int64 `db:"id" json:"id"`
+}
+
+// Sets the target queued message's position to one less than the
+// current minimum position for that chat, moving it to the head.
+func (q *sqlQuerier) ReorderChatQueuedMessageToHead(ctx context.Context, arg ReorderChatQueuedMessageToHeadParams) (int64, error) {
+ result, err := q.db.ExecContext(ctx, reorderChatQueuedMessageToHead, arg.ChatID, arg.ID)
+ if err != nil {
+ return 0, err
+ }
+ return result.RowsAffected()
+}
+
const resolveUserChatSpendLimit = `-- name: ResolveUserChatSpendLimit :one
SELECT CASE
WHEN NOT cfg.enabled THEN -1
@@ -9481,7 +10720,7 @@ WITH updated_chats AS (
archived = false,
updated_at = NOW()
WHERE id = $1::uuid OR root_chat_id = $1::uuid
- RETURNING 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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, user_acl, group_acl
+ RETURNING 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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, user_acl, group_acl, snapshot_version, history_version, queue_version, generation_attempt, retry_state, retry_state_version, runner_id, requires_action_deadline_at
),
chats_expanded AS (
SELECT
@@ -9513,6 +10752,14 @@ chats_expanded AS (
updated_chats.plan_mode,
updated_chats.client_type,
updated_chats.last_turn_summary,
+ updated_chats.snapshot_version,
+ updated_chats.history_version,
+ updated_chats.queue_version,
+ updated_chats.generation_attempt,
+ updated_chats.retry_state,
+ updated_chats.retry_state_version,
+ updated_chats.runner_id,
+ updated_chats.requires_action_deadline_at,
COALESCE(root.user_acl, updated_chats.user_acl) AS user_acl,
COALESCE(root.group_acl, updated_chats.group_acl) AS group_acl,
owner.username AS owner_username,
@@ -9522,7 +10769,7 @@ chats_expanded AS (
LEFT JOIN chats root ON root.id = COALESCE(updated_chats.root_chat_id, updated_chats.parent_chat_id)
JOIN visible_users owner ON owner.id = updated_chats.owner_id
)
-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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, user_acl, group_acl, owner_username, owner_name
+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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, snapshot_version, history_version, queue_version, generation_attempt, retry_state, retry_state_version, runner_id, requires_action_deadline_at, user_acl, group_acl, owner_username, owner_name
FROM chats_expanded
ORDER BY (chats_expanded.id = $1::uuid) DESC, chats_expanded.created_at ASC, chats_expanded.id ASC
`
@@ -9569,6 +10816,14 @@ func (q *sqlQuerier) UnarchiveChatByID(ctx context.Context, id uuid.UUID) ([]Cha
&i.PlanMode,
&i.ClientType,
&i.LastTurnSummary,
+ &i.SnapshotVersion,
+ &i.HistoryVersion,
+ &i.QueueVersion,
+ &i.GenerationAttempt,
+ &i.RetryState,
+ &i.RetryStateVersion,
+ &i.RunnerID,
+ &i.RequiresActionDeadlineAt,
&i.UserACL,
&i.GroupACL,
&i.OwnerUsername,
@@ -9675,7 +10930,7 @@ UPDATE chats SET
updated_at = NOW()
WHERE
id = $3::uuid
-RETURNING 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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, user_acl, group_acl
+RETURNING 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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, user_acl, group_acl, snapshot_version, history_version, queue_version, generation_attempt, retry_state, retry_state_version, runner_id, requires_action_deadline_at
),
chats_expanded AS (
SELECT
@@ -9707,6 +10962,14 @@ chats_expanded AS (
updated_chat.plan_mode,
updated_chat.client_type,
updated_chat.last_turn_summary,
+ updated_chat.snapshot_version,
+ updated_chat.history_version,
+ updated_chat.queue_version,
+ updated_chat.generation_attempt,
+ updated_chat.retry_state,
+ updated_chat.retry_state_version,
+ updated_chat.runner_id,
+ updated_chat.requires_action_deadline_at,
COALESCE(root.user_acl, updated_chat.user_acl) AS user_acl,
COALESCE(root.group_acl, updated_chat.group_acl) AS group_acl,
owner.username AS owner_username,
@@ -9716,7 +10979,7 @@ chats_expanded AS (
LEFT JOIN chats root ON root.id = COALESCE(updated_chat.root_chat_id, updated_chat.parent_chat_id)
JOIN visible_users owner ON owner.id = updated_chat.owner_id
)
-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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, user_acl, group_acl, owner_username, owner_name
+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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, snapshot_version, history_version, queue_version, generation_attempt, retry_state, retry_state_version, runner_id, requires_action_deadline_at, user_acl, group_acl, owner_username, owner_name
FROM chats_expanded
`
@@ -9758,6 +11021,14 @@ func (q *sqlQuerier) UpdateChatBuildAgentBinding(ctx context.Context, arg Update
&i.PlanMode,
&i.ClientType,
&i.LastTurnSummary,
+ &i.SnapshotVersion,
+ &i.HistoryVersion,
+ &i.QueueVersion,
+ &i.GenerationAttempt,
+ &i.RetryState,
+ &i.RetryStateVersion,
+ &i.RunnerID,
+ &i.RequiresActionDeadlineAt,
&i.UserACL,
&i.GroupACL,
&i.OwnerUsername,
@@ -9775,7 +11046,7 @@ SET
updated_at = NOW()
WHERE
id = $2::uuid
-RETURNING 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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, user_acl, group_acl
+RETURNING 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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, user_acl, group_acl, snapshot_version, history_version, queue_version, generation_attempt, retry_state, retry_state_version, runner_id, requires_action_deadline_at
),
chats_expanded AS (
SELECT
@@ -9807,6 +11078,14 @@ chats_expanded AS (
updated_chat.plan_mode,
updated_chat.client_type,
updated_chat.last_turn_summary,
+ updated_chat.snapshot_version,
+ updated_chat.history_version,
+ updated_chat.queue_version,
+ updated_chat.generation_attempt,
+ updated_chat.retry_state,
+ updated_chat.retry_state_version,
+ updated_chat.runner_id,
+ updated_chat.requires_action_deadline_at,
COALESCE(root.user_acl, updated_chat.user_acl) AS user_acl,
COALESCE(root.group_acl, updated_chat.group_acl) AS group_acl,
owner.username AS owner_username,
@@ -9816,7 +11095,7 @@ chats_expanded AS (
LEFT JOIN chats root ON root.id = COALESCE(updated_chat.root_chat_id, updated_chat.parent_chat_id)
JOIN visible_users owner ON owner.id = updated_chat.owner_id
)
-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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, user_acl, group_acl, owner_username, owner_name
+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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, snapshot_version, history_version, queue_version, generation_attempt, retry_state, retry_state_version, runner_id, requires_action_deadline_at, user_acl, group_acl, owner_username, owner_name
FROM chats_expanded
`
@@ -9857,6 +11136,149 @@ func (q *sqlQuerier) UpdateChatByID(ctx context.Context, arg UpdateChatByIDParam
&i.PlanMode,
&i.ClientType,
&i.LastTurnSummary,
+ &i.SnapshotVersion,
+ &i.HistoryVersion,
+ &i.QueueVersion,
+ &i.GenerationAttempt,
+ &i.RetryState,
+ &i.RetryStateVersion,
+ &i.RunnerID,
+ &i.RequiresActionDeadlineAt,
+ &i.UserACL,
+ &i.GroupACL,
+ &i.OwnerUsername,
+ &i.OwnerName,
+ )
+ return i, err
+}
+
+const updateChatExecutionState = `-- name: UpdateChatExecutionState :one
+WITH updated_chat AS (
+ UPDATE chats
+ SET
+ status = $1::chat_status,
+ archived = $2::boolean,
+ worker_id = $3::uuid,
+ runner_id = $4::uuid,
+ last_error = $5::jsonb,
+ requires_action_deadline_at = $6::timestamptz,
+ pin_order = CASE WHEN $2::boolean THEN 0 ELSE pin_order END,
+ updated_at = NOW()
+ WHERE id = $7::uuid
+ RETURNING 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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, user_acl, group_acl, snapshot_version, history_version, queue_version, generation_attempt, retry_state, retry_state_version, runner_id, requires_action_deadline_at
+),
+chats_expanded AS (
+ SELECT
+ updated_chat.id,
+ updated_chat.owner_id,
+ updated_chat.workspace_id,
+ updated_chat.title,
+ updated_chat.status,
+ updated_chat.worker_id,
+ updated_chat.started_at,
+ updated_chat.heartbeat_at,
+ updated_chat.created_at,
+ updated_chat.updated_at,
+ updated_chat.parent_chat_id,
+ updated_chat.root_chat_id,
+ updated_chat.last_model_config_id,
+ updated_chat.archived,
+ updated_chat.last_error,
+ updated_chat.mode,
+ updated_chat.mcp_server_ids,
+ updated_chat.labels,
+ updated_chat.build_id,
+ updated_chat.agent_id,
+ updated_chat.pin_order,
+ updated_chat.last_read_message_id,
+ updated_chat.last_injected_context,
+ updated_chat.dynamic_tools,
+ updated_chat.organization_id,
+ updated_chat.plan_mode,
+ updated_chat.client_type,
+ updated_chat.last_turn_summary,
+ updated_chat.snapshot_version,
+ updated_chat.history_version,
+ updated_chat.queue_version,
+ updated_chat.generation_attempt,
+ updated_chat.retry_state,
+ updated_chat.retry_state_version,
+ updated_chat.runner_id,
+ updated_chat.requires_action_deadline_at,
+ COALESCE(root.user_acl, updated_chat.user_acl) AS user_acl,
+ COALESCE(root.group_acl, updated_chat.group_acl) AS group_acl,
+ owner.username AS owner_username,
+ owner.name AS owner_name
+ FROM updated_chat
+ LEFT JOIN chats root ON root.id = COALESCE(updated_chat.root_chat_id, updated_chat.parent_chat_id)
+ JOIN visible_users owner ON owner.id = updated_chat.owner_id
+)
+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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, snapshot_version, history_version, queue_version, generation_attempt, retry_state, retry_state_version, runner_id, requires_action_deadline_at, user_acl, group_acl, owner_username, owner_name
+FROM chats_expanded
+`
+
+type UpdateChatExecutionStateParams struct {
+ Status ChatStatus `db:"status" json:"status"`
+ Archived bool `db:"archived" json:"archived"`
+ WorkerID uuid.NullUUID `db:"worker_id" json:"worker_id"`
+ RunnerID uuid.NullUUID `db:"runner_id" json:"runner_id"`
+ LastError pqtype.NullRawMessage `db:"last_error" json:"last_error"`
+ RequiresActionDeadlineAt sql.NullTime `db:"requires_action_deadline_at" json:"requires_action_deadline_at"`
+ ID uuid.UUID `db:"id" json:"id"`
+}
+
+// Atomically updates the execution-state-managed fields on a chat:
+// status, archived, last_error, ownership identifiers, and the
+// requires-action deadline. Callers compose this with transition
+// mutations inside a single ChatMachine.Update transaction.
+func (q *sqlQuerier) UpdateChatExecutionState(ctx context.Context, arg UpdateChatExecutionStateParams) (Chat, error) {
+ row := q.db.QueryRowContext(ctx, updateChatExecutionState,
+ arg.Status,
+ arg.Archived,
+ arg.WorkerID,
+ arg.RunnerID,
+ arg.LastError,
+ arg.RequiresActionDeadlineAt,
+ arg.ID,
+ )
+ var i Chat
+ err := row.Scan(
+ &i.ID,
+ &i.OwnerID,
+ &i.WorkspaceID,
+ &i.Title,
+ &i.Status,
+ &i.WorkerID,
+ &i.StartedAt,
+ &i.HeartbeatAt,
+ &i.CreatedAt,
+ &i.UpdatedAt,
+ &i.ParentChatID,
+ &i.RootChatID,
+ &i.LastModelConfigID,
+ &i.Archived,
+ &i.LastError,
+ &i.Mode,
+ pq.Array(&i.MCPServerIDs),
+ &i.Labels,
+ &i.BuildID,
+ &i.AgentID,
+ &i.PinOrder,
+ &i.LastReadMessageID,
+ &i.LastInjectedContext,
+ &i.DynamicTools,
+ &i.OrganizationID,
+ &i.PlanMode,
+ &i.ClientType,
+ &i.LastTurnSummary,
+ &i.SnapshotVersion,
+ &i.HistoryVersion,
+ &i.QueueVersion,
+ &i.GenerationAttempt,
+ &i.RetryState,
+ &i.RetryStateVersion,
+ &i.RunnerID,
+ &i.RequiresActionDeadlineAt,
&i.UserACL,
&i.GroupACL,
&i.OwnerUsername,
@@ -9919,7 +11341,7 @@ SET
updated_at = NOW()
WHERE
id = $2::uuid
-RETURNING 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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, user_acl, group_acl
+RETURNING 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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, user_acl, group_acl, snapshot_version, history_version, queue_version, generation_attempt, retry_state, retry_state_version, runner_id, requires_action_deadline_at
),
chats_expanded AS (
SELECT
@@ -9951,6 +11373,14 @@ chats_expanded AS (
updated_chat.plan_mode,
updated_chat.client_type,
updated_chat.last_turn_summary,
+ updated_chat.snapshot_version,
+ updated_chat.history_version,
+ updated_chat.queue_version,
+ updated_chat.generation_attempt,
+ updated_chat.retry_state,
+ updated_chat.retry_state_version,
+ updated_chat.runner_id,
+ updated_chat.requires_action_deadline_at,
COALESCE(root.user_acl, updated_chat.user_acl) AS user_acl,
COALESCE(root.group_acl, updated_chat.group_acl) AS group_acl,
owner.username AS owner_username,
@@ -9960,7 +11390,7 @@ chats_expanded AS (
LEFT JOIN chats root ON root.id = COALESCE(updated_chat.root_chat_id, updated_chat.parent_chat_id)
JOIN visible_users owner ON owner.id = updated_chat.owner_id
)
-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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, user_acl, group_acl, owner_username, owner_name
+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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, snapshot_version, history_version, queue_version, generation_attempt, retry_state, retry_state_version, runner_id, requires_action_deadline_at, user_acl, group_acl, owner_username, owner_name
FROM chats_expanded
`
@@ -10001,6 +11431,14 @@ func (q *sqlQuerier) UpdateChatLabelsByID(ctx context.Context, arg UpdateChatLab
&i.PlanMode,
&i.ClientType,
&i.LastTurnSummary,
+ &i.SnapshotVersion,
+ &i.HistoryVersion,
+ &i.QueueVersion,
+ &i.GenerationAttempt,
+ &i.RetryState,
+ &i.RetryStateVersion,
+ &i.RunnerID,
+ &i.RequiresActionDeadlineAt,
&i.UserACL,
&i.GroupACL,
&i.OwnerUsername,
@@ -10015,7 +11453,7 @@ UPDATE chats SET
last_injected_context = $1::jsonb
WHERE
id = $2::uuid
-RETURNING 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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, user_acl, group_acl
+RETURNING 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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, user_acl, group_acl, snapshot_version, history_version, queue_version, generation_attempt, retry_state, retry_state_version, runner_id, requires_action_deadline_at
),
chats_expanded AS (
SELECT
@@ -10047,6 +11485,14 @@ chats_expanded AS (
updated_chat.plan_mode,
updated_chat.client_type,
updated_chat.last_turn_summary,
+ updated_chat.snapshot_version,
+ updated_chat.history_version,
+ updated_chat.queue_version,
+ updated_chat.generation_attempt,
+ updated_chat.retry_state,
+ updated_chat.retry_state_version,
+ updated_chat.runner_id,
+ updated_chat.requires_action_deadline_at,
COALESCE(root.user_acl, updated_chat.user_acl) AS user_acl,
COALESCE(root.group_acl, updated_chat.group_acl) AS group_acl,
owner.username AS owner_username,
@@ -10056,7 +11502,7 @@ chats_expanded AS (
LEFT JOIN chats root ON root.id = COALESCE(updated_chat.root_chat_id, updated_chat.parent_chat_id)
JOIN visible_users owner ON owner.id = updated_chat.owner_id
)
-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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, user_acl, group_acl, owner_username, owner_name
+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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, snapshot_version, history_version, queue_version, generation_attempt, retry_state, retry_state_version, runner_id, requires_action_deadline_at, user_acl, group_acl, owner_username, owner_name
FROM chats_expanded
`
@@ -10101,6 +11547,14 @@ func (q *sqlQuerier) UpdateChatLastInjectedContext(ctx context.Context, arg Upda
&i.PlanMode,
&i.ClientType,
&i.LastTurnSummary,
+ &i.SnapshotVersion,
+ &i.HistoryVersion,
+ &i.QueueVersion,
+ &i.GenerationAttempt,
+ &i.RetryState,
+ &i.RetryStateVersion,
+ &i.RunnerID,
+ &i.RequiresActionDeadlineAt,
&i.UserACL,
&i.GroupACL,
&i.OwnerUsername,
@@ -10118,7 +11572,7 @@ SET
last_model_config_id = $1::uuid
WHERE
id = $2::uuid
-RETURNING 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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, user_acl, group_acl
+RETURNING 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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, user_acl, group_acl, snapshot_version, history_version, queue_version, generation_attempt, retry_state, retry_state_version, runner_id, requires_action_deadline_at
),
chats_expanded AS (
SELECT
@@ -10150,6 +11604,14 @@ chats_expanded AS (
updated_chat.plan_mode,
updated_chat.client_type,
updated_chat.last_turn_summary,
+ updated_chat.snapshot_version,
+ updated_chat.history_version,
+ updated_chat.queue_version,
+ updated_chat.generation_attempt,
+ updated_chat.retry_state,
+ updated_chat.retry_state_version,
+ updated_chat.runner_id,
+ updated_chat.requires_action_deadline_at,
COALESCE(root.user_acl, updated_chat.user_acl) AS user_acl,
COALESCE(root.group_acl, updated_chat.group_acl) AS group_acl,
owner.username AS owner_username,
@@ -10159,7 +11621,7 @@ chats_expanded AS (
LEFT JOIN chats root ON root.id = COALESCE(updated_chat.root_chat_id, updated_chat.parent_chat_id)
JOIN visible_users owner ON owner.id = updated_chat.owner_id
)
-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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, user_acl, group_acl, owner_username, owner_name
+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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, snapshot_version, history_version, queue_version, generation_attempt, retry_state, retry_state_version, runner_id, requires_action_deadline_at, user_acl, group_acl, owner_username, owner_name
FROM chats_expanded
`
@@ -10200,6 +11662,14 @@ func (q *sqlQuerier) UpdateChatLastModelConfigByID(ctx context.Context, arg Upda
&i.PlanMode,
&i.ClientType,
&i.LastTurnSummary,
+ &i.SnapshotVersion,
+ &i.HistoryVersion,
+ &i.QueueVersion,
+ &i.GenerationAttempt,
+ &i.RetryState,
+ &i.RetryStateVersion,
+ &i.RunnerID,
+ &i.RequiresActionDeadlineAt,
&i.UserACL,
&i.GroupACL,
&i.OwnerUsername,
@@ -10234,26 +11704,24 @@ SET
), '')
WHERE
id = $2::uuid
- AND updated_at = $3::timestamptz
+ AND history_version = $3::bigint
`
type UpdateChatLastTurnSummaryParams struct {
- LastTurnSummary sql.NullString `db:"last_turn_summary" json:"last_turn_summary"`
- ID uuid.UUID `db:"id" json:"id"`
- ExpectedUpdatedAt time.Time `db:"expected_updated_at" json:"expected_updated_at"`
+ LastTurnSummary sql.NullString `db:"last_turn_summary" json:"last_turn_summary"`
+ ID uuid.UUID `db:"id" json:"id"`
+ ExpectedHistoryVersion int64 `db:"expected_history_version" json:"expected_history_version"`
}
// Updates the cached last completed turn summary for sidebar display.
// Empty or whitespace-only summaries are stored as NULL here so direct
// query callers cannot accidentally persist blank sidebar text.
-// This intentionally preserves updated_at. The staleness guard relies on
-// every new-turn query, such as UpdateChatStatus and AcquireChats, bumping
-// updated_at. Future chat-field updates that do not bump updated_at can let
-// stale summaries persist. If this query ever bumps updated_at, later
-// goroutine summary writes will be rejected as stale.
+// This intentionally preserves updated_at. The staleness guard uses
+// history_version so worker lifecycle transitions that do not change the
+// active message history cannot reject final turn summary writes.
// Two summary workers using the same freshness marker are last-write-wins.
func (q *sqlQuerier) UpdateChatLastTurnSummary(ctx context.Context, arg UpdateChatLastTurnSummaryParams) (int64, error) {
- result, err := q.db.ExecContext(ctx, updateChatLastTurnSummary, arg.LastTurnSummary, arg.ID, arg.ExpectedUpdatedAt)
+ result, err := q.db.ExecContext(ctx, updateChatLastTurnSummary, arg.LastTurnSummary, arg.ID, arg.ExpectedHistoryVersion)
if err != nil {
return 0, err
}
@@ -10269,7 +11737,7 @@ SET
updated_at = NOW()
WHERE
id = $2::uuid
-RETURNING 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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, user_acl, group_acl
+RETURNING 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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, user_acl, group_acl, snapshot_version, history_version, queue_version, generation_attempt, retry_state, retry_state_version, runner_id, requires_action_deadline_at
),
chats_expanded AS (
SELECT
@@ -10301,6 +11769,14 @@ chats_expanded AS (
updated_chat.plan_mode,
updated_chat.client_type,
updated_chat.last_turn_summary,
+ updated_chat.snapshot_version,
+ updated_chat.history_version,
+ updated_chat.queue_version,
+ updated_chat.generation_attempt,
+ updated_chat.retry_state,
+ updated_chat.retry_state_version,
+ updated_chat.runner_id,
+ updated_chat.requires_action_deadline_at,
COALESCE(root.user_acl, updated_chat.user_acl) AS user_acl,
COALESCE(root.group_acl, updated_chat.group_acl) AS group_acl,
owner.username AS owner_username,
@@ -10310,7 +11786,7 @@ chats_expanded AS (
LEFT JOIN chats root ON root.id = COALESCE(updated_chat.root_chat_id, updated_chat.parent_chat_id)
JOIN visible_users owner ON owner.id = updated_chat.owner_id
)
-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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, user_acl, group_acl, owner_username, owner_name
+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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, snapshot_version, history_version, queue_version, generation_attempt, retry_state, retry_state_version, runner_id, requires_action_deadline_at, user_acl, group_acl, owner_username, owner_name
FROM chats_expanded
`
@@ -10351,6 +11827,14 @@ func (q *sqlQuerier) UpdateChatMCPServerIDs(ctx context.Context, arg UpdateChatM
&i.PlanMode,
&i.ClientType,
&i.LastTurnSummary,
+ &i.SnapshotVersion,
+ &i.HistoryVersion,
+ &i.QueueVersion,
+ &i.GenerationAttempt,
+ &i.RetryState,
+ &i.RetryStateVersion,
+ &i.RunnerID,
+ &i.RequiresActionDeadlineAt,
&i.UserACL,
&i.GroupACL,
&i.OwnerUsername,
@@ -10368,7 +11852,7 @@ SET
WHERE
id = $3::bigint
RETURNING
- id, chat_id, model_config_id, created_at, role, content, visibility, input_tokens, output_tokens, total_tokens, reasoning_tokens, cache_creation_tokens, cache_read_tokens, context_limit, compressed, created_by, content_version, total_cost_micros, runtime_ms, deleted, provider_response_id, api_key_id
+ id, chat_id, model_config_id, created_at, role, content, visibility, input_tokens, output_tokens, total_tokens, reasoning_tokens, cache_creation_tokens, cache_read_tokens, context_limit, compressed, created_by, content_version, total_cost_micros, runtime_ms, deleted, provider_response_id, api_key_id, revision
`
type UpdateChatMessageByIDParams struct {
@@ -10403,6 +11887,7 @@ func (q *sqlQuerier) UpdateChatMessageByID(ctx context.Context, arg UpdateChatMe
&i.Deleted,
&i.ProviderResponseID,
&i.APIKeyID,
+ &i.Revision,
)
return i, err
}
@@ -10487,7 +11972,7 @@ SET
plan_mode = $1::chat_plan_mode
WHERE
id = $2::uuid
-RETURNING 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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, user_acl, group_acl
+RETURNING 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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, user_acl, group_acl, snapshot_version, history_version, queue_version, generation_attempt, retry_state, retry_state_version, runner_id, requires_action_deadline_at
),
chats_expanded AS (
SELECT
@@ -10519,6 +12004,14 @@ chats_expanded AS (
updated_chat.plan_mode,
updated_chat.client_type,
updated_chat.last_turn_summary,
+ updated_chat.snapshot_version,
+ updated_chat.history_version,
+ updated_chat.queue_version,
+ updated_chat.generation_attempt,
+ updated_chat.retry_state,
+ updated_chat.retry_state_version,
+ updated_chat.runner_id,
+ updated_chat.requires_action_deadline_at,
COALESCE(root.user_acl, updated_chat.user_acl) AS user_acl,
COALESCE(root.group_acl, updated_chat.group_acl) AS group_acl,
owner.username AS owner_username,
@@ -10528,7 +12021,7 @@ chats_expanded AS (
LEFT JOIN chats root ON root.id = COALESCE(updated_chat.root_chat_id, updated_chat.parent_chat_id)
JOIN visible_users owner ON owner.id = updated_chat.owner_id
)
-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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, user_acl, group_acl, owner_username, owner_name
+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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, snapshot_version, history_version, queue_version, generation_attempt, retry_state, retry_state_version, runner_id, requires_action_deadline_at, user_acl, group_acl, owner_username, owner_name
FROM chats_expanded
`
@@ -10569,6 +12062,14 @@ func (q *sqlQuerier) UpdateChatPlanModeByID(ctx context.Context, arg UpdateChatP
&i.PlanMode,
&i.ClientType,
&i.LastTurnSummary,
+ &i.SnapshotVersion,
+ &i.HistoryVersion,
+ &i.QueueVersion,
+ &i.GenerationAttempt,
+ &i.RetryState,
+ &i.RetryStateVersion,
+ &i.RunnerID,
+ &i.RequiresActionDeadlineAt,
&i.UserACL,
&i.GroupACL,
&i.OwnerUsername,
@@ -10577,20 +12078,14 @@ func (q *sqlQuerier) UpdateChatPlanModeByID(ctx context.Context, arg UpdateChatP
return i, err
}
-const updateChatStatus = `-- name: UpdateChatStatus :one
+const updateChatRetryState = `-- name: UpdateChatRetryState :one
WITH updated_chat AS (
-UPDATE
- chats
-SET
- status = $1::chat_status,
- worker_id = $2::uuid,
- started_at = $3::timestamptz,
- heartbeat_at = $4::timestamptz,
- last_error = $5::jsonb,
- updated_at = NOW()
-WHERE
- id = $6::uuid
-RETURNING 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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, user_acl, group_acl
+ UPDATE chats
+ SET
+ retry_state = $1::jsonb,
+ updated_at = NOW()
+ WHERE id = $2::uuid
+ RETURNING 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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, user_acl, group_acl, snapshot_version, history_version, queue_version, generation_attempt, retry_state, retry_state_version, runner_id, requires_action_deadline_at
),
chats_expanded AS (
SELECT
@@ -10622,6 +12117,134 @@ chats_expanded AS (
updated_chat.plan_mode,
updated_chat.client_type,
updated_chat.last_turn_summary,
+ updated_chat.snapshot_version,
+ updated_chat.history_version,
+ updated_chat.queue_version,
+ updated_chat.generation_attempt,
+ updated_chat.retry_state,
+ updated_chat.retry_state_version,
+ updated_chat.runner_id,
+ updated_chat.requires_action_deadline_at,
+ COALESCE(root.user_acl, updated_chat.user_acl) AS user_acl,
+ COALESCE(root.group_acl, updated_chat.group_acl) AS group_acl,
+ owner.username AS owner_username,
+ owner.name AS owner_name
+ FROM updated_chat
+ LEFT JOIN chats root ON root.id = COALESCE(updated_chat.root_chat_id, updated_chat.parent_chat_id)
+ JOIN visible_users owner ON owner.id = updated_chat.owner_id
+)
+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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, snapshot_version, history_version, queue_version, generation_attempt, retry_state, retry_state_version, runner_id, requires_action_deadline_at, user_acl, group_acl, owner_username, owner_name
+FROM chats_expanded
+`
+
+type UpdateChatRetryStateParams struct {
+ RetryState json.RawMessage `db:"retry_state" json:"retry_state"`
+ ID uuid.UUID `db:"id" json:"id"`
+}
+
+// Stores the client-visible retry payload. retry_state_version is
+// assigned by trigger from the current snapshot_version.
+func (q *sqlQuerier) UpdateChatRetryState(ctx context.Context, arg UpdateChatRetryStateParams) (Chat, error) {
+ row := q.db.QueryRowContext(ctx, updateChatRetryState, arg.RetryState, arg.ID)
+ var i Chat
+ err := row.Scan(
+ &i.ID,
+ &i.OwnerID,
+ &i.WorkspaceID,
+ &i.Title,
+ &i.Status,
+ &i.WorkerID,
+ &i.StartedAt,
+ &i.HeartbeatAt,
+ &i.CreatedAt,
+ &i.UpdatedAt,
+ &i.ParentChatID,
+ &i.RootChatID,
+ &i.LastModelConfigID,
+ &i.Archived,
+ &i.LastError,
+ &i.Mode,
+ pq.Array(&i.MCPServerIDs),
+ &i.Labels,
+ &i.BuildID,
+ &i.AgentID,
+ &i.PinOrder,
+ &i.LastReadMessageID,
+ &i.LastInjectedContext,
+ &i.DynamicTools,
+ &i.OrganizationID,
+ &i.PlanMode,
+ &i.ClientType,
+ &i.LastTurnSummary,
+ &i.SnapshotVersion,
+ &i.HistoryVersion,
+ &i.QueueVersion,
+ &i.GenerationAttempt,
+ &i.RetryState,
+ &i.RetryStateVersion,
+ &i.RunnerID,
+ &i.RequiresActionDeadlineAt,
+ &i.UserACL,
+ &i.GroupACL,
+ &i.OwnerUsername,
+ &i.OwnerName,
+ )
+ return i, err
+}
+
+const updateChatStatus = `-- name: UpdateChatStatus :one
+WITH updated_chat AS (
+UPDATE
+ chats
+SET
+ status = $1::chat_status,
+ worker_id = $2::uuid,
+ started_at = $3::timestamptz,
+ heartbeat_at = $4::timestamptz,
+ last_error = $5::jsonb,
+ updated_at = NOW()
+WHERE
+ id = $6::uuid
+RETURNING 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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, user_acl, group_acl, snapshot_version, history_version, queue_version, generation_attempt, retry_state, retry_state_version, runner_id, requires_action_deadline_at
+),
+chats_expanded AS (
+ SELECT
+ updated_chat.id,
+ updated_chat.owner_id,
+ updated_chat.workspace_id,
+ updated_chat.title,
+ updated_chat.status,
+ updated_chat.worker_id,
+ updated_chat.started_at,
+ updated_chat.heartbeat_at,
+ updated_chat.created_at,
+ updated_chat.updated_at,
+ updated_chat.parent_chat_id,
+ updated_chat.root_chat_id,
+ updated_chat.last_model_config_id,
+ updated_chat.archived,
+ updated_chat.last_error,
+ updated_chat.mode,
+ updated_chat.mcp_server_ids,
+ updated_chat.labels,
+ updated_chat.build_id,
+ updated_chat.agent_id,
+ updated_chat.pin_order,
+ updated_chat.last_read_message_id,
+ updated_chat.last_injected_context,
+ updated_chat.dynamic_tools,
+ updated_chat.organization_id,
+ updated_chat.plan_mode,
+ updated_chat.client_type,
+ updated_chat.last_turn_summary,
+ updated_chat.snapshot_version,
+ updated_chat.history_version,
+ updated_chat.queue_version,
+ updated_chat.generation_attempt,
+ updated_chat.retry_state,
+ updated_chat.retry_state_version,
+ updated_chat.runner_id,
+ updated_chat.requires_action_deadline_at,
COALESCE(root.user_acl, updated_chat.user_acl) AS user_acl,
COALESCE(root.group_acl, updated_chat.group_acl) AS group_acl,
owner.username AS owner_username,
@@ -10631,7 +12254,7 @@ chats_expanded AS (
LEFT JOIN chats root ON root.id = COALESCE(updated_chat.root_chat_id, updated_chat.parent_chat_id)
JOIN visible_users owner ON owner.id = updated_chat.owner_id
)
-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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, user_acl, group_acl, owner_username, owner_name
+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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, snapshot_version, history_version, queue_version, generation_attempt, retry_state, retry_state_version, runner_id, requires_action_deadline_at, user_acl, group_acl, owner_username, owner_name
FROM chats_expanded
`
@@ -10683,6 +12306,14 @@ func (q *sqlQuerier) UpdateChatStatus(ctx context.Context, arg UpdateChatStatusP
&i.PlanMode,
&i.ClientType,
&i.LastTurnSummary,
+ &i.SnapshotVersion,
+ &i.HistoryVersion,
+ &i.QueueVersion,
+ &i.GenerationAttempt,
+ &i.RetryState,
+ &i.RetryStateVersion,
+ &i.RunnerID,
+ &i.RequiresActionDeadlineAt,
&i.UserACL,
&i.GroupACL,
&i.OwnerUsername,
@@ -10704,7 +12335,7 @@ SET
updated_at = $6::timestamptz
WHERE
id = $7::uuid
-RETURNING 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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, user_acl, group_acl
+RETURNING 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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, user_acl, group_acl, snapshot_version, history_version, queue_version, generation_attempt, retry_state, retry_state_version, runner_id, requires_action_deadline_at
),
chats_expanded AS (
SELECT
@@ -10736,6 +12367,14 @@ chats_expanded AS (
updated_chat.plan_mode,
updated_chat.client_type,
updated_chat.last_turn_summary,
+ updated_chat.snapshot_version,
+ updated_chat.history_version,
+ updated_chat.queue_version,
+ updated_chat.generation_attempt,
+ updated_chat.retry_state,
+ updated_chat.retry_state_version,
+ updated_chat.runner_id,
+ updated_chat.requires_action_deadline_at,
COALESCE(root.user_acl, updated_chat.user_acl) AS user_acl,
COALESCE(root.group_acl, updated_chat.group_acl) AS group_acl,
owner.username AS owner_username,
@@ -10745,7 +12384,7 @@ chats_expanded AS (
LEFT JOIN chats root ON root.id = COALESCE(updated_chat.root_chat_id, updated_chat.parent_chat_id)
JOIN visible_users owner ON owner.id = updated_chat.owner_id
)
-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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, user_acl, group_acl, owner_username, owner_name
+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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, snapshot_version, history_version, queue_version, generation_attempt, retry_state, retry_state_version, runner_id, requires_action_deadline_at, user_acl, group_acl, owner_username, owner_name
FROM chats_expanded
`
@@ -10799,6 +12438,14 @@ func (q *sqlQuerier) UpdateChatStatusPreserveUpdatedAt(ctx context.Context, arg
&i.PlanMode,
&i.ClientType,
&i.LastTurnSummary,
+ &i.SnapshotVersion,
+ &i.HistoryVersion,
+ &i.QueueVersion,
+ &i.GenerationAttempt,
+ &i.RetryState,
+ &i.RetryStateVersion,
+ &i.RunnerID,
+ &i.RequiresActionDeadlineAt,
&i.UserACL,
&i.GroupACL,
&i.OwnerUsername,
@@ -10818,7 +12465,7 @@ SET
title = $1::text
WHERE
id = $2::uuid
-RETURNING 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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, user_acl, group_acl
+RETURNING 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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, user_acl, group_acl, snapshot_version, history_version, queue_version, generation_attempt, retry_state, retry_state_version, runner_id, requires_action_deadline_at
),
chats_expanded AS (
SELECT
@@ -10850,6 +12497,14 @@ chats_expanded AS (
updated_chat.plan_mode,
updated_chat.client_type,
updated_chat.last_turn_summary,
+ updated_chat.snapshot_version,
+ updated_chat.history_version,
+ updated_chat.queue_version,
+ updated_chat.generation_attempt,
+ updated_chat.retry_state,
+ updated_chat.retry_state_version,
+ updated_chat.runner_id,
+ updated_chat.requires_action_deadline_at,
COALESCE(root.user_acl, updated_chat.user_acl) AS user_acl,
COALESCE(root.group_acl, updated_chat.group_acl) AS group_acl,
owner.username AS owner_username,
@@ -10859,7 +12514,7 @@ chats_expanded AS (
LEFT JOIN chats root ON root.id = COALESCE(updated_chat.root_chat_id, updated_chat.parent_chat_id)
JOIN visible_users owner ON owner.id = updated_chat.owner_id
)
-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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, user_acl, group_acl, owner_username, owner_name
+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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, snapshot_version, history_version, queue_version, generation_attempt, retry_state, retry_state_version, runner_id, requires_action_deadline_at, user_acl, group_acl, owner_username, owner_name
FROM chats_expanded
`
@@ -10900,6 +12555,14 @@ func (q *sqlQuerier) UpdateChatTitleByID(ctx context.Context, arg UpdateChatTitl
&i.PlanMode,
&i.ClientType,
&i.LastTurnSummary,
+ &i.SnapshotVersion,
+ &i.HistoryVersion,
+ &i.QueueVersion,
+ &i.GenerationAttempt,
+ &i.RetryState,
+ &i.RetryStateVersion,
+ &i.RunnerID,
+ &i.RequiresActionDeadlineAt,
&i.UserACL,
&i.GroupACL,
&i.OwnerUsername,
@@ -10916,7 +12579,7 @@ UPDATE chats SET
agent_id = $3::uuid,
updated_at = NOW()
WHERE id = $4::uuid
-RETURNING 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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, user_acl, group_acl
+RETURNING 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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, user_acl, group_acl, snapshot_version, history_version, queue_version, generation_attempt, retry_state, retry_state_version, runner_id, requires_action_deadline_at
),
chats_expanded AS (
SELECT
@@ -10948,6 +12611,14 @@ chats_expanded AS (
updated_chat.plan_mode,
updated_chat.client_type,
updated_chat.last_turn_summary,
+ updated_chat.snapshot_version,
+ updated_chat.history_version,
+ updated_chat.queue_version,
+ updated_chat.generation_attempt,
+ updated_chat.retry_state,
+ updated_chat.retry_state_version,
+ updated_chat.runner_id,
+ updated_chat.requires_action_deadline_at,
COALESCE(root.user_acl, updated_chat.user_acl) AS user_acl,
COALESCE(root.group_acl, updated_chat.group_acl) AS group_acl,
owner.username AS owner_username,
@@ -10957,7 +12628,7 @@ chats_expanded AS (
LEFT JOIN chats root ON root.id = COALESCE(updated_chat.root_chat_id, updated_chat.parent_chat_id)
JOIN visible_users owner ON owner.id = updated_chat.owner_id
)
-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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, user_acl, group_acl, owner_username, owner_name
+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, mode, mcp_server_ids, labels, build_id, agent_id, pin_order, last_read_message_id, last_injected_context, dynamic_tools, organization_id, plan_mode, client_type, last_turn_summary, snapshot_version, history_version, queue_version, generation_attempt, retry_state, retry_state_version, runner_id, requires_action_deadline_at, user_acl, group_acl, owner_username, owner_name
FROM chats_expanded
`
@@ -11005,6 +12676,14 @@ func (q *sqlQuerier) UpdateChatWorkspaceBinding(ctx context.Context, arg UpdateC
&i.PlanMode,
&i.ClientType,
&i.LastTurnSummary,
+ &i.SnapshotVersion,
+ &i.HistoryVersion,
+ &i.QueueVersion,
+ &i.GenerationAttempt,
+ &i.RetryState,
+ &i.RetryStateVersion,
+ &i.RunnerID,
+ &i.RequiresActionDeadlineAt,
&i.UserACL,
&i.GroupACL,
&i.OwnerUsername,
@@ -11232,6 +12911,44 @@ func (q *sqlQuerier) UpsertChatDiffStatusReference(ctx context.Context, arg Upse
return i, err
}
+const upsertChatHeartbeat = `-- name: UpsertChatHeartbeat :exec
+INSERT INTO chat_heartbeats (chat_id, runner_id, heartbeat_at)
+VALUES ($1::uuid, $2::uuid, NOW())
+ON CONFLICT (chat_id, runner_id) DO UPDATE
+SET heartbeat_at = EXCLUDED.heartbeat_at
+`
+
+type UpsertChatHeartbeatParams struct {
+ ChatID uuid.UUID `db:"chat_id" json:"chat_id"`
+ RunnerID uuid.UUID `db:"runner_id" json:"runner_id"`
+}
+
+// Upserts a heartbeat row for the (chat_id, runner_id) lease. Uses
+// database time so callers do not depend on a local clock.
+func (q *sqlQuerier) UpsertChatHeartbeat(ctx context.Context, arg UpsertChatHeartbeatParams) error {
+ _, err := q.db.ExecContext(ctx, upsertChatHeartbeat, arg.ChatID, arg.RunnerID)
+ return err
+}
+
+const upsertChatHeartbeats = `-- name: UpsertChatHeartbeats :exec
+INSERT INTO chat_heartbeats (chat_id, runner_id, heartbeat_at)
+SELECT chat_ids.chat_id, runner_ids.runner_id, NOW()
+FROM unnest($1::uuid[]) WITH ORDINALITY AS chat_ids(chat_id, ord)
+JOIN unnest($2::uuid[]) WITH ORDINALITY AS runner_ids(runner_id, ord) USING (ord)
+ON CONFLICT (chat_id, runner_id) DO UPDATE
+SET heartbeat_at = EXCLUDED.heartbeat_at
+`
+
+type UpsertChatHeartbeatsParams struct {
+ ChatIds []uuid.UUID `db:"chat_ids" json:"chat_ids"`
+ RunnerIds []uuid.UUID `db:"runner_ids" json:"runner_ids"`
+}
+
+func (q *sqlQuerier) UpsertChatHeartbeats(ctx context.Context, arg UpsertChatHeartbeatsParams) error {
+ _, err := q.db.ExecContext(ctx, upsertChatHeartbeats, pq.Array(arg.ChatIds), pq.Array(arg.RunnerIds))
+ return err
+}
+
const upsertChatUsageLimitConfig = `-- name: UpsertChatUsageLimitConfig :one
INSERT INTO chat_usage_limit_config (singleton, enabled, default_limit_micros, period, updated_at)
VALUES (TRUE, $1::boolean, $2::bigint, $3::text, NOW())
diff --git a/coderd/database/unique_constraint.go b/coderd/database/unique_constraint.go
index fd11ab2e06..347435f66a 100644
--- a/coderd/database/unique_constraint.go
+++ b/coderd/database/unique_constraint.go
@@ -26,6 +26,7 @@ const (
UniqueChatDiffStatusesPkey UniqueConstraint = "chat_diff_statuses_pkey" // ALTER TABLE ONLY chat_diff_statuses ADD CONSTRAINT chat_diff_statuses_pkey PRIMARY KEY (chat_id);
UniqueChatFileLinksChatIDFileIDKey UniqueConstraint = "chat_file_links_chat_id_file_id_key" // ALTER TABLE ONLY chat_file_links ADD CONSTRAINT chat_file_links_chat_id_file_id_key UNIQUE (chat_id, file_id);
UniqueChatFilesPkey UniqueConstraint = "chat_files_pkey" // ALTER TABLE ONLY chat_files ADD CONSTRAINT chat_files_pkey PRIMARY KEY (id);
+ UniqueChatHeartbeatsPkey UniqueConstraint = "chat_heartbeats_pkey" // ALTER TABLE ONLY chat_heartbeats ADD CONSTRAINT chat_heartbeats_pkey PRIMARY KEY (chat_id, runner_id);
UniqueChatMessagesPkey UniqueConstraint = "chat_messages_pkey" // ALTER TABLE ONLY chat_messages ADD CONSTRAINT chat_messages_pkey PRIMARY KEY (id);
UniqueChatModelConfigsPkey UniqueConstraint = "chat_model_configs_pkey" // ALTER TABLE ONLY chat_model_configs ADD CONSTRAINT chat_model_configs_pkey PRIMARY KEY (id);
UniqueChatQueuedMessagesPkey UniqueConstraint = "chat_queued_messages_pkey" // ALTER TABLE ONLY chat_queued_messages ADD CONSTRAINT chat_queued_messages_pkey PRIMARY KEY (id);
diff --git a/coderd/x/chatd/chatd.go b/coderd/x/chatd/chatd.go
index 5a5ba7fb60..d569d993fb 100644
--- a/coderd/x/chatd/chatd.go
+++ b/coderd/x/chatd/chatd.go
@@ -9803,9 +9803,8 @@ func (p *Server) updateLastTurnSummary(
defer cancel()
affected, err := p.db.UpdateChatLastTurnSummary(updateCtx, database.UpdateChatLastTurnSummaryParams{
- ID: chat.ID,
- ExpectedUpdatedAt: expectedUpdatedAt,
- LastTurnSummary: lastTurnSummary,
+ ID: chat.ID,
+ LastTurnSummary: lastTurnSummary,
})
if err != nil {
logger.Warn(updateCtx, "failed to update chat turn summary",
diff --git a/docs/admin/security/audit-logs.md b/docs/admin/security/audit-logs.md
index 45022ff253..fa7c688402 100644
--- a/docs/admin/security/audit-logs.md
+++ b/docs/admin/security/audit-logs.md
@@ -24,7 +24,7 @@ We track the following resources:
| Group
create, write, delete |
| Field | Tracked |
| | avatar_url | true |
| chat_spend_limit_micros | true |
| display_name | true |
| id | true |
| members | true |
| name | true |
| organization_id | false |
| quota_allowance | true |
| source | false |
|
| AuditableGroupAiBudget
write, delete | | Field | Tracked |
| | created_at | false |
| group_id | false |
| group_name | false |
| spend_limit | true |
| spend_limit_micros | false |
| updated_at | false |
|
| AuditableOrganizationMember
| | Field | Tracked |
| | created_at | true |
| organization_id | false |
| roles | true |
| updated_at | true |
| user_id | true |
| username | true |
|
-| Chat
create, write | | Field | Tracked |
| | agent_id | false |
| archived | true |
| build_id | false |
| client_type | false |
| created_at | false |
| dynamic_tools | false |
| group_acl | true |
| heartbeat_at | false |
| id | true |
| labels | true |
| last_error | false |
| last_injected_context | false |
| last_model_config_id | false |
| last_read_message_id | false |
| last_turn_summary | false |
| mcp_server_ids | true |
| mode | true |
| organization_id | false |
| owner_id | true |
| owner_name | false |
| owner_username | false |
| parent_chat_id | false |
| pin_order | true |
| plan_mode | false |
| root_chat_id | false |
| started_at | false |
| status | false |
| title | true |
| updated_at | false |
| user_acl | true |
| worker_id | false |
| workspace_id | true |
|
+| Chat
create, write | | Field | Tracked |
| | agent_id | false |
| archived | true |
| build_id | false |
| client_type | false |
| created_at | false |
| dynamic_tools | false |
| generation_attempt | false |
| group_acl | true |
| heartbeat_at | false |
| history_version | false |
| id | true |
| labels | true |
| last_error | false |
| last_injected_context | false |
| last_model_config_id | false |
| last_read_message_id | false |
| last_turn_summary | false |
| mcp_server_ids | true |
| mode | true |
| organization_id | false |
| owner_id | true |
| owner_name | false |
| owner_username | false |
| parent_chat_id | false |
| pin_order | true |
| plan_mode | false |
| queue_version | false |
| requires_action_deadline_at | false |
| retry_state | false |
| retry_state_version | false |
| root_chat_id | false |
| runner_id | false |
| snapshot_version | false |
| started_at | false |
| status | false |
| title | true |
| updated_at | false |
| user_acl | true |
| worker_id | false |
| workspace_id | true |
|
| CustomRole
| | Field | Tracked |
| | created_at | false |
| display_name | true |
| id | false |
| is_system | false |
| member_permissions | true |
| name | true |
| org_permissions | true |
| organization_id | false |
| site_permissions | true |
| updated_at | false |
| user_permissions | true |
|
| GitSSHKey
create | | Field | Tracked |
| | created_at | false |
| private_key | true |
| private_key_key_id | false |
| public_key | true |
| updated_at | false |
| user_id | true |
|
| GroupSyncSettings
| | Field | Tracked |
| | auto_create_missing_groups | true |
| field | true |
| legacy_group_name_mapping | false |
| mapping | true |
| regex_filter | true |
|