feat: separate default and additional Coder Agents system prompts (#23616)

Admins can now control whether the built-in Coder Agents default system
prompt is prepended to their custom instructions, rather than having the
custom prompt silently replace the default.

**Changes:**
- New `include_default_system_prompt` boolean toggle (defaults to `true`
for existing deployments) stored as a site config key — no migration
needed.
- GET `/api/experimental/chats/config/system-prompt` returns the toggle
state, the custom prompt, and a preview of the built-in default.
- PUT persists both the toggle and custom prompt atomically in a single
transaction.
- `resolvedChatSystemPrompt()` composes `[default?, custom?]` joined by
`\n\n`, falling back to the built-in default on DB errors.
- Settings UI adds a Switch toggle with conditional helper text and a
"Preview" button that shows the built-in default prompt via the existing
`TextPreviewDialog`.
- Comprehensive test coverage: 15 subtests covering toggle behavior,
prompt composition matrix, auth boundaries, and integration with chat
creation.
This commit is contained in:
Michael Suchacz
2026-03-26 13:32:41 +01:00
committed by GitHub
parent d175e799da
commit 4f063cdc47
16 changed files with 895 additions and 68 deletions
+18 -9
View File
@@ -388,10 +388,19 @@ type ChatModelsResponse struct {
Providers []ChatModelProvider `json:"providers"`
}
// ChatSystemPrompt is the request and response body for the chat
// system prompt configuration endpoint.
type ChatSystemPrompt struct {
SystemPrompt string `json:"system_prompt"`
// ChatSystemPromptResponse is the response body for the chat system prompt
// configuration endpoint.
type ChatSystemPromptResponse struct {
SystemPrompt string `json:"system_prompt"`
IncludeDefaultSystemPrompt bool `json:"include_default_system_prompt"`
DefaultSystemPrompt string `json:"default_system_prompt"`
}
// UpdateChatSystemPromptRequest is the request body for updating the chat
// system prompt configuration.
type UpdateChatSystemPromptRequest struct {
SystemPrompt string `json:"system_prompt"`
IncludeDefaultSystemPrompt *bool `json:"include_default_system_prompt,omitempty"`
}
// UserChatCustomPrompt is the request and response body for the
@@ -1407,21 +1416,21 @@ func (c *ExperimentalClient) GetChatCostUsers(ctx context.Context, opts ChatCost
}
// GetChatSystemPrompt returns the deployment-wide chat system prompt.
func (c *ExperimentalClient) GetChatSystemPrompt(ctx context.Context) (ChatSystemPrompt, error) {
func (c *ExperimentalClient) GetChatSystemPrompt(ctx context.Context) (ChatSystemPromptResponse, error) {
res, err := c.Request(ctx, http.MethodGet, "/api/experimental/chats/config/system-prompt", nil)
if err != nil {
return ChatSystemPrompt{}, err
return ChatSystemPromptResponse{}, err
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return ChatSystemPrompt{}, ReadBodyAsError(res)
return ChatSystemPromptResponse{}, ReadBodyAsError(res)
}
var resp ChatSystemPrompt
var resp ChatSystemPromptResponse
return resp, json.NewDecoder(res.Body).Decode(&resp)
}
// UpdateChatSystemPrompt updates the deployment-wide chat system prompt.
func (c *ExperimentalClient) UpdateChatSystemPrompt(ctx context.Context, req ChatSystemPrompt) error {
func (c *ExperimentalClient) UpdateChatSystemPrompt(ctx context.Context, req UpdateChatSystemPromptRequest) error {
res, err := c.Request(ctx, http.MethodPut, "/api/experimental/chats/config/system-prompt", req)
if err != nil {
return err