feat: expose busy_behavior on chat message API (#24054)

The backend (`chatd.go`) already fully implements both `"queue"` and
`"interrupt"` busy behaviors for `SendMessage`, and the `message_agent`
subagent tool already leverages both internally. However the HTTP API
hardcoded `"queue"` and the SDK had no way for callers to request
interrupt-on-send.

This adds a `ChatBusyBehavior` enum type to the SDK and an optional
`busy_behavior` field on `CreateChatMessageRequest`. The HTTP handler
validates the field and passes it through to `chatd.SendMessage`.
Default remains `"queue"` for full backward compatibility.

<details><summary>Implementation notes</summary>

- `codersdk/chats.go`: New `ChatBusyBehavior` type with
`ChatBusyBehaviorQueue` and `ChatBusyBehaviorInterrupt` constants. Added
`BusyBehavior` field to `CreateChatMessageRequest` with `enums` tag for
codegen.
- `coderd/exp_chats.go`: `postChatMessages` now reads
`req.BusyBehavior`, maps SDK constants to
`chatd.SendMessageBusyBehavior*`, returns 400 on invalid values.
- `site/src/api/typesGenerated.ts`: Auto-generated via `make gen`.
- No frontend behavior changes — the field is available but unused by
the UI.

</details>

> [!NOTE]
> Generated by Coder Agents
This commit is contained in:
Kyle Carberry
2026-04-06 16:20:14 -04:00
committed by GitHub
parent d2950e7615
commit 648787e739
3 changed files with 40 additions and 4 deletions
+15 -1
View File
@@ -1830,6 +1830,20 @@ func (api *API) postChatMessages(rw http.ResponseWriter, r *http.Request) {
}
}
busyBehavior := chatd.SendMessageBusyBehaviorQueue
switch req.BusyBehavior {
case codersdk.ChatBusyBehaviorInterrupt:
busyBehavior = chatd.SendMessageBusyBehaviorInterrupt
case codersdk.ChatBusyBehaviorQueue, "":
// Default to queue.
default:
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
Message: "Invalid busy_behavior value.",
Detail: `Must be "queue" or "interrupt".`,
})
return
}
sendResult, sendErr := api.chatDaemon.SendMessage(
ctx,
chatd.SendMessageOptions{
@@ -1837,7 +1851,7 @@ func (api *API) postChatMessages(rw http.ResponseWriter, r *http.Request) {
CreatedBy: apiKey.UserID,
Content: contentBlocks,
ModelConfigID: req.ModelConfigID,
BusyBehavior: chatd.SendMessageBusyBehaviorQueue,
BusyBehavior: busyBehavior,
MCPServerIDs: req.MCPServerIDs,
},
)
+19 -3
View File
@@ -369,11 +369,27 @@ type UpdateChatRequest struct {
Labels *map[string]string `json:"labels,omitempty"`
}
// ChatBusyBehavior controls what happens when a user sends a message
// while the chat is already processing.
type ChatBusyBehavior string
const (
// ChatBusyBehaviorQueue queues the message for processing after
// the current run finishes.
ChatBusyBehaviorQueue ChatBusyBehavior = "queue"
// ChatBusyBehaviorInterrupt queues the message and interrupts
// the active run. The partial assistant response is persisted
// before the queued message is promoted, preserving correct
// conversation order.
ChatBusyBehaviorInterrupt ChatBusyBehavior = "interrupt"
)
// CreateChatMessageRequest is the request to add a message to a chat.
type CreateChatMessageRequest struct {
Content []ChatInputPart `json:"content"`
ModelConfigID *uuid.UUID `json:"model_config_id,omitempty" format:"uuid"`
MCPServerIDs *[]uuid.UUID `json:"mcp_server_ids,omitempty" format:"uuid"`
Content []ChatInputPart `json:"content"`
ModelConfigID *uuid.UUID `json:"model_config_id,omitempty" format:"uuid"`
MCPServerIDs *[]uuid.UUID `json:"mcp_server_ids,omitempty" format:"uuid"`
BusyBehavior ChatBusyBehavior `json:"busy_behavior,omitempty" enums:"queue,interrupt"`
}
// EditChatMessageRequest is the request to edit a user message in a chat.
+6
View File
@@ -1215,6 +1215,11 @@ export interface Chat {
readonly last_injected_context?: readonly ChatMessagePart[];
}
// From codersdk/chats.go
export type ChatBusyBehavior = "interrupt" | "queue";
export const ChatBusyBehaviors: ChatBusyBehavior[] = ["interrupt", "queue"];
// From codersdk/chats.go
/**
* ChatCompactionThresholdKeyPrefix scopes per-model chat compaction
@@ -2338,6 +2343,7 @@ export interface CreateChatMessageRequest {
readonly content: readonly ChatInputPart[];
readonly model_config_id?: string;
readonly mcp_server_ids?: string[];
readonly busy_behavior?: ChatBusyBehavior;
}
// From codersdk/chats.go