feat: add deployment-wide template allowlist for chats (#23262)

- Stores a deployment-wide agents template allowlist in `site_configs`
(`agents_template_allowlist`)
- Adds `GET/PUT /api/experimental/chats/config/template-allowlist`
endpoints
- Filters `list_templates`, `read_template`, and `create_workspace` chat
tools by allowlist, if defined (empty=all allowed)
- Add "Templates" admin settings tab in Agents UI ([what it looks
like](https://624de63c6aacee003aa84340-sitjilsyrr.chromatic.com/?path=/story/pages-agentspage-agentsettingspageview--template-allowlist))

> 🤖 This PR was created with the help of Coder Agents, and has been
reviewed by my human. 🧑‍💻
This commit is contained in:
Cian Johnston
2026-03-25 15:19:17 +00:00
committed by GitHub
parent c0ab22dc88
commit 796872f4de
23 changed files with 1045 additions and 10 deletions
+34
View File
@@ -425,6 +425,13 @@ func ParseChatWorkspaceTTL(s string) (time.Duration, error) {
return d, nil
}
// ChatTemplateAllowlist is the request and response body for the
// chat template allowlist configuration endpoint. An empty list
// means all templates are allowed.
type ChatTemplateAllowlist struct {
TemplateIDs []string `json:"template_ids"`
}
// ChatProviderConfigSource describes how a provider entry is sourced.
type ChatProviderConfigSource string
@@ -1444,6 +1451,33 @@ func (c *ExperimentalClient) UpdateChatWorkspaceTTL(ctx context.Context, req Upd
return nil
}
// GetChatTemplateAllowlist returns the deployment-wide chat template allowlist.
func (c *ExperimentalClient) GetChatTemplateAllowlist(ctx context.Context) (ChatTemplateAllowlist, error) {
res, err := c.Request(ctx, http.MethodGet, "/api/experimental/chats/config/template-allowlist", nil)
if err != nil {
return ChatTemplateAllowlist{}, err
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return ChatTemplateAllowlist{}, ReadBodyAsError(res)
}
var resp ChatTemplateAllowlist
return resp, json.NewDecoder(res.Body).Decode(&resp)
}
// UpdateChatTemplateAllowlist updates the deployment-wide chat template allowlist.
func (c *ExperimentalClient) UpdateChatTemplateAllowlist(ctx context.Context, req ChatTemplateAllowlist) error {
res, err := c.Request(ctx, http.MethodPut, "/api/experimental/chats/config/template-allowlist", req)
if err != nil {
return err
}
defer res.Body.Close()
if res.StatusCode != http.StatusNoContent {
return ReadBodyAsError(res)
}
return nil
}
// UpdateUserChatCustomPrompt updates the user's custom chat prompt.
func (c *ExperimentalClient) UpdateUserChatCustomPrompt(ctx context.Context, req UserChatCustomPrompt) (UserChatCustomPrompt, error) {
res, err := c.Request(ctx, http.MethodPut, "/api/experimental/chats/config/user-prompt", req)