feat(db): add created_by column to chat_messages table (#22940)

Adds a `created_by` column (nullable UUID) to the `chat_messages` table
to track which user created each message. Only user-sent messages
populate this field; assistant, tool, system, and summary messages leave
it null.

The column is threaded through the full stack: SQL migration, query
updates, generated Go/TypeScript types, db2sdk conversion, chatd
(including subagent paths), and API handlers. All API handlers that
insert user messages now pass the authenticated user's ID as
`created_by`.

No foreign key constraint was added, matching the existing pattern used
by `chat_model_configs.created_by`.
This commit is contained in:
Kyle Carberry
2026-03-11 10:00:38 -04:00
committed by GitHub
parent c7c789f9e4
commit bb59477648
12 changed files with 66 additions and 15 deletions
+16 -1
View File
@@ -192,6 +192,7 @@ const (
// SendMessageOptions controls user message insertion with busy-state behavior.
type SendMessageOptions struct {
ChatID uuid.UUID
CreatedBy uuid.UUID
Content []fantasy.Content
ContentFileIDs map[int]uuid.UUID
ModelConfigID *uuid.UUID
@@ -209,6 +210,7 @@ type SendMessageResult struct {
// EditMessageOptions controls in-place user message edits.
type EditMessageOptions struct {
ChatID uuid.UUID
CreatedBy uuid.UUID
EditedMessageID int64
Content []fantasy.Content
ContentFileIDs map[int]uuid.UUID
@@ -223,6 +225,7 @@ type EditMessageResult struct {
// PromoteQueuedOptions controls queued-message promotion.
type PromoteQueuedOptions struct {
ChatID uuid.UUID
CreatedBy uuid.UUID
QueuedMessageID int64
ModelConfigID *uuid.UUID
}
@@ -266,7 +269,8 @@ func (p *Server) CreateChat(ctx context.Context, opts CreateOptions) (database.C
return xerrors.Errorf("marshal system prompt: %w", err)
}
_, err = tx.InsertChatMessage(ctx, database.InsertChatMessageParams{
ChatID: insertedChat.ID,
ChatID: insertedChat.ID,
CreatedBy: uuid.NullUUID{},
ModelConfigID: uuid.NullUUID{
UUID: opts.ModelConfigID,
Valid: true,
@@ -303,6 +307,7 @@ func (p *Server) CreateChat(ctx context.Context, opts CreateOptions) (database.C
},
Role: "user",
Content: userContent,
CreatedBy: uuid.NullUUID{UUID: opts.OwnerID, Valid: opts.OwnerID != uuid.Nil},
Visibility: database.ChatMessageVisibilityBoth,
InputTokens: sql.NullInt64{},
OutputTokens: sql.NullInt64{},
@@ -421,6 +426,7 @@ func (p *Server) SendMessage(
lockedChat,
modelConfigID,
content,
opts.CreatedBy,
)
if err != nil {
return err
@@ -736,6 +742,7 @@ func (p *Server) PromoteQueued(
RawMessage: targetContent,
Valid: len(targetContent) > 0,
},
opts.CreatedBy,
)
if err != nil {
return err
@@ -881,12 +888,14 @@ func insertUserMessageAndSetPending(
lockedChat database.Chat,
modelConfigID uuid.UUID,
content pqtype.NullRawMessage,
createdBy uuid.UUID,
) (database.ChatMessage, database.Chat, error) {
message, err := insertChatMessageWithStore(ctx, store, database.InsertChatMessageParams{
ChatID: lockedChat.ID,
ModelConfigID: uuid.NullUUID{UUID: modelConfigID, Valid: true},
Role: "user",
Content: content,
CreatedBy: uuid.NullUUID{UUID: createdBy, Valid: createdBy != uuid.Nil},
Visibility: database.ChatMessageVisibilityBoth,
InputTokens: sql.NullInt64{},
OutputTokens: sql.NullInt64{},
@@ -1948,6 +1957,7 @@ func (p *Server) processChat(ctx context.Context, chat database.Chat) {
RawMessage: nextQueued.Content,
Valid: len(nextQueued.Content) > 0,
},
CreatedBy: uuid.NullUUID{UUID: chat.OwnerID, Valid: chat.OwnerID != uuid.Nil},
Visibility: database.ChatMessageVisibilityBoth,
InputTokens: sql.NullInt64{},
OutputTokens: sql.NullInt64{},
@@ -2296,6 +2306,7 @@ func (p *Server) runChat(
hasUsage := step.Usage != (fantasy.Usage{})
assistantMessage, insertErr := tx.InsertChatMessage(persistCtx, database.InsertChatMessageParams{
ChatID: chat.ID,
CreatedBy: uuid.NullUUID{},
ModelConfigID: uuid.NullUUID{UUID: modelConfig.ID, Valid: true},
Role: string(fantasy.MessageRoleAssistant),
Content: assistantContent,
@@ -2329,6 +2340,7 @@ func (p *Server) runChat(
toolMessage, insertErr := tx.InsertChatMessage(persistCtx, database.InsertChatMessageParams{
ChatID: chat.ID,
CreatedBy: uuid.NullUUID{},
ModelConfigID: uuid.NullUUID{UUID: modelConfig.ID, Valid: true},
Role: string(fantasy.MessageRoleTool),
Content: resultContent,
@@ -2613,6 +2625,7 @@ func (p *Server) persistChatContextSummary(
txErr := p.db.InTx(func(tx database.Store) error {
_, txErr := tx.InsertChatMessage(ctx, database.InsertChatMessageParams{
ChatID: chatID,
CreatedBy: uuid.NullUUID{},
ModelConfigID: uuid.NullUUID{UUID: modelConfigID, Valid: true},
Role: string(fantasy.MessageRoleUser),
Content: pqtype.NullRawMessage{
@@ -2635,6 +2648,7 @@ func (p *Server) persistChatContextSummary(
assistantMessage, txErr := tx.InsertChatMessage(ctx, database.InsertChatMessageParams{
ChatID: chatID,
CreatedBy: uuid.NullUUID{},
ModelConfigID: uuid.NullUUID{UUID: modelConfigID, Valid: true},
Role: string(fantasy.MessageRoleAssistant),
Content: assistantContent,
@@ -2658,6 +2672,7 @@ func (p *Server) persistChatContextSummary(
toolMessage, txErr := tx.InsertChatMessage(ctx, database.InsertChatMessageParams{
ChatID: chatID,
CreatedBy: uuid.NullUUID{},
ModelConfigID: uuid.NullUUID{UUID: modelConfigID, Valid: true},
Role: string(fantasy.MessageRoleTool),
Content: toolResult,