diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 94c41bf60e..72317ba0ff 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -154,970 +154,6 @@ const docTemplate = `{ } } }, - "/api/experimental/chats": { - "get": { - "description": "Experimental: this endpoint is subject to change.", - "produces": [ - "application/json" - ], - "tags": [ - "Chats" - ], - "summary": "List chats", - "operationId": "list-chats", - "parameters": [ - { - "type": "string", - "description": "Search query. Supports ` + "`" + `title:\u003csubstring\u003e` + "`" + ` (case-insensitive, quote multi-word values), ` + "`" + `archived:bool` + "`" + `, ` + "`" + `has_unread:bool` + "`" + `, ` + "`" + `pr_status:\u003cdraft\\|open\\|merged\\|closed\u003e` + "`" + ` as repeated or comma-separated values, ` + "`" + `source:\u003ccreated_by_me\\|shared_with_me\u003e` + "`" + `, ` + "`" + `diff_url:\u003curl\u003e` + "`" + ` (quote values containing colons), ` + "`" + `pr:\u003cnumber\u003e` + "`" + ` (exact PR number match), ` + "`" + `repo:\u003cowner/repo\u003e` + "`" + ` (case-insensitive substring match against git remote origin or URL), ` + "`" + `pr_title:\u003ctext\u003e` + "`" + ` (case-insensitive PR title substring), ` + "`" + `search:\u003ctext\u003e` + "`" + ` (full-text search across chat titles, PR titles, PR numbers, and message bodies; message bodies match English word stems, e.g. ` + "`" + `refactor` + "`" + ` matches ` + "`" + `refactoring` + "`" + `, and ignore English stopwords; titles and PR titles match whole words case-insensitively without stemming; quote multi-word values; cannot be combined with title, pr_title, or pr; a value that tokenizes to no searchable words, e.g. punctuation only, returns an empty list). Bare terms are not supported; use ` + "`" + `title:\u003cvalue\u003e` + "`" + ` or ` + "`" + `search:\u003cvalue\u003e` + "`" + `.", - "name": "q", - "in": "query" - }, - { - "type": "string", - "description": "Filter by label as key:value. Repeat for multiple (AND logic).", - "name": "label", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/codersdk.Chat" - } - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ] - }, - "post": { - "description": "Experimental: this endpoint is subject to change.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Chats" - ], - "summary": "Create chat", - "operationId": "create-chat", - "parameters": [ - { - "description": "Create chat request", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.CreateChatRequest" - } - } - ], - "responses": { - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/codersdk.Chat" - } - }, - "413": { - "description": "Request body exceeds 256 KiB", - "schema": { - "$ref": "#/definitions/codersdk.Response" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ] - } - }, - "/api/experimental/chats/config/retention-days": { - "get": { - "produces": [ - "application/json" - ], - "tags": [ - "Chats" - ], - "summary": "Get chat retention days", - "operationId": "get-chat-retention-days", - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.ChatRetentionDaysResponse" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ], - "x-apidocgen": { - "skip": true - } - }, - "put": { - "consumes": [ - "application/json" - ], - "tags": [ - "Chats" - ], - "summary": "Update chat retention days", - "operationId": "update-chat-retention-days", - "parameters": [ - { - "description": "Request body", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.UpdateChatRetentionDaysRequest" - } - } - ], - "responses": { - "204": { - "description": "No Content" - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ], - "x-apidocgen": { - "skip": true - } - } - }, - "/api/experimental/chats/files": { - "post": { - "description": "Experimental: this endpoint is subject to change.", - "consumes": [ - "image/png", - "image/jpeg", - "image/gif", - "image/webp", - "text/plain", - "text/markdown", - "text/csv", - "application/json", - "application/pdf" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Chats" - ], - "summary": "Upload chat file", - "operationId": "upload-chat-file", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Organization ID", - "name": "organization", - "in": "query", - "required": true - } - ], - "responses": { - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/codersdk.UploadChatFileResponse" - } - }, - "413": { - "description": "Request body exceeds 10 MiB", - "schema": { - "$ref": "#/definitions/codersdk.Response" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ] - } - }, - "/api/experimental/chats/files/{file}": { - "get": { - "description": "Experimental: this endpoint is subject to change.", - "produces": [ - "image/png", - "image/jpeg", - "image/gif", - "image/webp", - "text/plain", - "text/markdown", - "text/csv", - "application/json", - "application/pdf" - ], - "tags": [ - "Chats" - ], - "summary": "Get chat file", - "operationId": "get-chat-file", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "File ID", - "name": "file", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK" - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ] - } - }, - "/api/experimental/chats/files/{file}/download": { - "get": { - "description": "Experimental: this endpoint is subject to change.", - "produces": [ - "image/png", - "image/jpeg", - "image/gif", - "image/webp", - "text/plain", - "text/markdown", - "text/csv", - "application/json", - "application/pdf" - ], - "tags": [ - "Chats" - ], - "summary": "Download chat file with signed token", - "operationId": "download-chat-file", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "File ID", - "name": "file", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Signed download token", - "name": "token", - "in": "query", - "required": true - } - ], - "responses": { - "200": { - "description": "OK" - } - }, - "x-apidocgen": { - "skip": true - } - } - }, - "/api/experimental/chats/files/{file}/download-url": { - "post": { - "description": "Experimental: this endpoint is subject to change.", - "produces": [ - "application/json" - ], - "tags": [ - "Chats" - ], - "summary": "Create chat file download URL", - "operationId": "create-chat-file-download-url", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "File ID", - "name": "file", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.ChatFileDownloadURLResponse" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ], - "x-apidocgen": { - "skip": true - } - } - }, - "/api/experimental/chats/watch": { - "get": { - "description": "Experimental: this endpoint is subject to change.", - "produces": [ - "application/json" - ], - "tags": [ - "Chats" - ], - "summary": "Watch chat events for a user via WebSockets", - "operationId": "watch-chat-events-for-a-user-via-websockets", - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.ChatWatchEvent" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ] - } - }, - "/api/experimental/chats/{chat}": { - "get": { - "description": "Experimental: this endpoint is subject to change.", - "produces": [ - "application/json" - ], - "tags": [ - "Chats" - ], - "summary": "Get chat by ID", - "operationId": "get-chat-by-id", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Chat ID", - "name": "chat", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.Chat" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ] - }, - "patch": { - "description": "Experimental: this endpoint is subject to change.", - "consumes": [ - "application/json" - ], - "tags": [ - "Chats" - ], - "summary": "Update chat", - "operationId": "update-chat", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Chat ID", - "name": "chat", - "in": "path", - "required": true - }, - { - "description": "Update chat request", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.UpdateChatRequest" - } - } - ], - "responses": { - "204": { - "description": "No Content" - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ] - } - }, - "/api/experimental/chats/{chat}/acl": { - "get": { - "description": "Experimental: this endpoint is subject to change.", - "produces": [ - "application/json" - ], - "tags": [ - "Chats" - ], - "summary": "Get chat ACLs", - "operationId": "get-chat-acls", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Chat ID", - "name": "chat", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.ChatACL" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ], - "x-apidocgen": { - "skip": true - } - }, - "patch": { - "description": "Experimental: this endpoint is subject to change.", - "consumes": [ - "application/json" - ], - "tags": [ - "Chats" - ], - "summary": "Update chat ACL", - "operationId": "update-chat-acl", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Chat ID", - "name": "chat", - "in": "path", - "required": true - }, - { - "description": "Update chat ACL request", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.UpdateChatACL" - } - } - ], - "responses": { - "204": { - "description": "No Content" - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ], - "x-apidocgen": { - "skip": true - } - } - }, - "/api/experimental/chats/{chat}/compact": { - "post": { - "description": "Experimental: this endpoint is subject to change.\nRequests a manual context compaction on an idle or errored\nchat, clearing any stored error. The compaction runs\nasynchronously through the chat worker and bypasses the\nautomatic usage threshold.", - "produces": [ - "application/json" - ], - "tags": [ - "Chats" - ], - "summary": "Compact chat", - "operationId": "compact-chat", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Chat ID", - "name": "chat", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.Chat" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ], - "x-apidocgen": { - "skip": true - } - } - }, - "/api/experimental/chats/{chat}/context": { - "put": { - "description": "Experimental: this endpoint is subject to change.", - "produces": [ - "application/json" - ], - "tags": [ - "Chats" - ], - "summary": "Refresh chat context", - "operationId": "refresh-chat-context", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Chat ID", - "name": "chat", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.Chat" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ] - } - }, - "/api/experimental/chats/{chat}/cost": { - "get": { - "description": "Experimental: this endpoint is subject to change.\n\nCost covers the whole chat tree: the root chat plus every\nsubagent chat beneath it. Requesting cost for a subagent chat\nreturns that same total.\n\nCost is derived from AI Gateway data, which is subject to its\nown retention period, 60 days by default, configured\nindependently of chat retention. Spend for requests older than\nthat period is no longer reported, so a chat whose requests\nhave all been purged reports zero cost.", - "produces": [ - "application/json" - ], - "tags": [ - "Chats" - ], - "summary": "Get chat cost", - "operationId": "get-chat-cost", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Chat ID", - "name": "chat", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.ChatCost" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ] - } - }, - "/api/experimental/chats/{chat}/diff": { - "get": { - "description": "Experimental: this endpoint is subject to change.", - "produces": [ - "application/json" - ], - "tags": [ - "Chats" - ], - "summary": "Get chat diff contents", - "operationId": "get-chat-diff-contents", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Chat ID", - "name": "chat", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.ChatDiffContents" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ] - } - }, - "/api/experimental/chats/{chat}/interrupt": { - "post": { - "description": "Experimental: this endpoint is subject to change.", - "produces": [ - "application/json" - ], - "tags": [ - "Chats" - ], - "summary": "Interrupt chat", - "operationId": "interrupt-chat", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Chat ID", - "name": "chat", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.Chat" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ] - } - }, - "/api/experimental/chats/{chat}/messages": { - "get": { - "description": "Experimental: this endpoint is subject to change.", - "produces": [ - "application/json" - ], - "tags": [ - "Chats" - ], - "summary": "List chat messages", - "operationId": "list-chat-messages", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Chat ID", - "name": "chat", - "in": "path", - "required": true - }, - { - "type": "integer", - "description": "Return messages with id \u003c before_id", - "name": "before_id", - "in": "query" - }, - { - "type": "integer", - "description": "Return messages with id \u003e after_id", - "name": "after_id", - "in": "query" - }, - { - "type": "integer", - "description": "Page size, 1 to 200. Defaults to 50.", - "name": "limit", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.ChatMessagesResponse" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ] - }, - "post": { - "description": "Experimental: this endpoint is subject to change.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Chats" - ], - "summary": "Send chat message", - "operationId": "send-chat-message", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Chat ID", - "name": "chat", - "in": "path", - "required": true - }, - { - "description": "Create chat message request", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.CreateChatMessageRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.CreateChatMessageResponse" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ] - } - }, - "/api/experimental/chats/{chat}/messages/{message}": { - "patch": { - "description": "Experimental: this endpoint is subject to change.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Chats" - ], - "summary": "Edit chat message", - "operationId": "edit-chat-message", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Chat ID", - "name": "chat", - "in": "path", - "required": true - }, - { - "type": "integer", - "description": "Message ID", - "name": "message", - "in": "path", - "required": true - }, - { - "description": "Edit chat message request", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.EditChatMessageRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.EditChatMessageResponse" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ] - } - }, - "/api/experimental/chats/{chat}/prompts": { - "get": { - "description": "Experimental: this endpoint is subject to change.\n\nReturns the user-authored prompts in a chat, newest first,\nwith each prompt's text parts concatenated in the order they\nwere authored. Used by the composer to power the up/down\narrow prompt-history cycle without paging through every\nmessage in the chat.", - "produces": [ - "application/json" - ], - "tags": [ - "Chats" - ], - "summary": "List chat user prompts", - "operationId": "list-chat-user-prompts", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Chat ID", - "name": "chat", - "in": "path", - "required": true - }, - { - "type": "integer", - "description": "Page size, 0 to 2000. 0 (the default) means the server-side default of 500.", - "name": "limit", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.ChatPromptsResponse" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ] - } - }, - "/api/experimental/chats/{chat}/reconcile-invalid": { - "post": { - "description": "Experimental: this endpoint is subject to change.", - "produces": [ - "application/json" - ], - "tags": [ - "Chats" - ], - "summary": "Reconcile invalid chat state", - "operationId": "reconcile-invalid-chat-state", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Chat ID", - "name": "chat", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.Chat" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ] - } - }, - "/api/experimental/chats/{chat}/stream": { - "get": { - "description": "Experimental: this endpoint is subject to change.", - "produces": [ - "application/json" - ], - "tags": [ - "Chats" - ], - "summary": "Stream chat events via WebSockets", - "operationId": "stream-chat-events-via-websockets", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Chat ID", - "name": "chat", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.ChatStreamEvent" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ] - } - }, "/api/experimental/chats/{chat}/stream/desktop": { "get": { "description": "Raw binary WebSocket stream of the chat workspace desktop.\nExperimental: this endpoint is subject to change.", @@ -1151,117 +187,6 @@ const docTemplate = `{ ] } }, - "/api/experimental/chats/{chat}/stream/git": { - "get": { - "description": "Experimental: this endpoint is subject to change.", - "produces": [ - "application/json" - ], - "tags": [ - "Chats" - ], - "summary": "Watch chat workspace git state via WebSockets", - "operationId": "watch-chat-workspace-git-state-via-websockets", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Chat ID", - "name": "chat", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.WorkspaceAgentGitServerMessage" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ] - } - }, - "/api/experimental/chats/{chat}/stream/parts": { - "get": { - "description": "Experimental: this endpoint is subject to change.", - "produces": [ - "application/json" - ], - "tags": [ - "Chats" - ], - "summary": "Stream chat parts via WebSockets", - "operationId": "stream-chat-parts-via-websockets", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Chat ID", - "name": "chat", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.ChatStreamEvent" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ], - "x-apidocgen": { - "skip": true - } - } - }, - "/api/experimental/chats/{chat}/title/propose": { - "post": { - "description": "Experimental: this endpoint is subject to change.", - "produces": [ - "application/json" - ], - "tags": [ - "Chats" - ], - "summary": "Propose chat title", - "operationId": "propose-chat-title", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Chat ID", - "name": "chat", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.ProposeChatTitleResponse" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ] - } - }, "/api/experimental/mcp/servers/{mcpServer}/oauth2/callback": { "get": { "produces": [ @@ -1321,927 +246,6 @@ const docTemplate = `{ } } }, - "/api/experimental/mcp/servers/{mcpServer}/oauth2/disconnect": { - "delete": { - "produces": [ - "application/json" - ], - "tags": [ - "MCP" - ], - "summary": "Disconnect MCP server OAuth2 token", - "operationId": "disconnect-mcp-server-oauth2-token", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "MCP server config ID", - "name": "mcpServer", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.MCPServerOAuth2DisconnectResponse" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ], - "x-apidocgen": { - "skip": true - } - } - }, - "/api/experimental/organizations/{organization}/chats/model-overrides": { - "get": { - "produces": [ - "application/json" - ], - "tags": [ - "Chats" - ], - "summary": "List organization chat model overrides", - "operationId": "list-organization-chat-model-overrides", - "parameters": [ - { - "type": "string", - "description": "Organization name or ID", - "name": "organization", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.ChatModelOverridesResponse" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ], - "x-apidocgen": { - "skip": true - } - } - }, - "/api/experimental/organizations/{organization}/chats/model-overrides/{context}": { - "put": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Chats" - ], - "summary": "Update organization chat model override", - "operationId": "update-organization-chat-model-override", - "parameters": [ - { - "type": "string", - "description": "Organization name or ID", - "name": "organization", - "in": "path", - "required": true - }, - { - "enum": [ - "general", - "explore", - "title_generation", - "compaction", - "advisor" - ], - "type": "string", - "description": "Override context", - "name": "context", - "in": "path", - "required": true - }, - { - "description": "Model override", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.UpdateChatModelOverrideRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.ChatModelOverrideResponse" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ], - "x-apidocgen": { - "skip": true - } - } - }, - "/api/experimental/organizations/{organization}/chats/models": { - "get": { - "produces": [ - "application/json" - ], - "tags": [ - "Chats" - ], - "summary": "List AI models and provider descriptors in an organization", - "operationId": "list-ai-models-by-organization", - "parameters": [ - { - "type": "string", - "description": "Organization name or ID", - "name": "organization", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.OrganizationChatModelsResponse" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ], - "x-apidocgen": { - "skip": true - } - }, - "post": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Chats" - ], - "summary": "Create an AI model in an organization", - "operationId": "create-ai-model", - "parameters": [ - { - "type": "string", - "description": "Organization name or ID", - "name": "organization", - "in": "path", - "required": true - }, - { - "description": "Model", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.CreateChatModelRequest" - } - } - ], - "responses": { - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/codersdk.ChatModel" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ], - "x-apidocgen": { - "skip": true - } - } - }, - "/api/experimental/organizations/{organization}/chats/models/{model}": { - "get": { - "produces": [ - "application/json" - ], - "tags": [ - "Chats" - ], - "summary": "Get an AI model", - "operationId": "get-ai-model", - "parameters": [ - { - "type": "string", - "description": "Organization name or ID", - "name": "organization", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Model ID", - "name": "model", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.ChatModel" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ], - "x-apidocgen": { - "skip": true - } - }, - "delete": { - "tags": [ - "Chats" - ], - "summary": "Delete an AI model", - "operationId": "delete-ai-model", - "parameters": [ - { - "type": "string", - "description": "Organization name or ID", - "name": "organization", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Model ID", - "name": "model", - "in": "path", - "required": true - } - ], - "responses": { - "204": { - "description": "No Content" - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ], - "x-apidocgen": { - "skip": true - } - }, - "patch": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Chats" - ], - "summary": "Update an AI model", - "operationId": "update-ai-model", - "parameters": [ - { - "type": "string", - "description": "Organization name or ID", - "name": "organization", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Model ID", - "name": "model", - "in": "path", - "required": true - }, - { - "description": "Model updates", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.UpdateChatModelRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.ChatModel" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ], - "x-apidocgen": { - "skip": true - } - } - }, - "/api/experimental/organizations/{organization}/chats/models/{model}/acl": { - "get": { - "produces": [ - "application/json" - ], - "tags": [ - "Chats" - ], - "summary": "Get an AI model ACL", - "operationId": "get-ai-model-acl", - "parameters": [ - { - "type": "string", - "description": "Organization name or ID", - "name": "organization", - "in": "path", - "required": true - }, - { - "type": "string", - "format": "uuid", - "description": "Model ID", - "name": "model", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.ChatModelACL" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ], - "x-apidocgen": { - "skip": true - } - }, - "patch": { - "consumes": [ - "application/json" - ], - "tags": [ - "Chats" - ], - "summary": "Update an AI model ACL", - "operationId": "update-ai-model-acl", - "parameters": [ - { - "type": "string", - "description": "Organization name or ID", - "name": "organization", - "in": "path", - "required": true - }, - { - "type": "string", - "format": "uuid", - "description": "Model ID", - "name": "model", - "in": "path", - "required": true - }, - { - "description": "Sparse model ACL update", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.UpdateChatModelACLRequest" - } - } - ], - "responses": { - "204": { - "description": "No Content" - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ], - "x-apidocgen": { - "skip": true - } - } - }, - "/api/experimental/organizations/{organization}/mcp-servers": { - "get": { - "produces": [ - "application/json" - ], - "tags": [ - "MCP" - ], - "summary": "List MCP server configs", - "operationId": "list-mcp-server-configs", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Organization ID", - "name": "organization", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/codersdk.MCPServerConfig" - } - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ], - "x-apidocgen": { - "skip": true - } - }, - "post": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "MCP" - ], - "summary": "Create MCP server config", - "operationId": "create-mcp-server-config", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Organization ID", - "name": "organization", - "in": "path", - "required": true - }, - { - "description": "Create MCP server config request", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.CreateMCPServerConfigRequest" - } - } - ], - "responses": { - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/codersdk.MCPServerConfig" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ], - "x-apidocgen": { - "skip": true - } - } - }, - "/api/experimental/organizations/{organization}/mcp-servers/{mcpserverconfig}": { - "get": { - "produces": [ - "application/json" - ], - "tags": [ - "MCP" - ], - "summary": "Get MCP server config", - "operationId": "get-mcp-server-config", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Organization ID", - "name": "organization", - "in": "path", - "required": true - }, - { - "type": "string", - "format": "uuid", - "description": "MCP server config ID", - "name": "mcpserverconfig", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.MCPServerConfig" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ], - "x-apidocgen": { - "skip": true - } - }, - "delete": { - "tags": [ - "MCP" - ], - "summary": "Delete MCP server config", - "operationId": "delete-mcp-server-config", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Organization ID", - "name": "organization", - "in": "path", - "required": true - }, - { - "type": "string", - "format": "uuid", - "description": "MCP server config ID", - "name": "mcpserverconfig", - "in": "path", - "required": true - } - ], - "responses": { - "204": { - "description": "No Content" - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ], - "x-apidocgen": { - "skip": true - } - }, - "patch": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "MCP" - ], - "summary": "Update MCP server config", - "operationId": "update-mcp-server-config", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Organization ID", - "name": "organization", - "in": "path", - "required": true - }, - { - "type": "string", - "format": "uuid", - "description": "MCP server config ID", - "name": "mcpserverconfig", - "in": "path", - "required": true - }, - { - "description": "Update MCP server config request", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.UpdateMCPServerConfigRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.MCPServerConfig" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ], - "x-apidocgen": { - "skip": true - } - } - }, - "/api/experimental/organizations/{organization}/mcp-servers/{mcpserverconfig}/acl": { - "get": { - "produces": [ - "application/json" - ], - "tags": [ - "MCP" - ], - "summary": "Get MCP server config ACL", - "operationId": "get-mcp-server-config-acl", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Organization ID", - "name": "organization", - "in": "path", - "required": true - }, - { - "type": "string", - "format": "uuid", - "description": "MCP server config ID", - "name": "mcpserverconfig", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.MCPServerConfigACL" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ], - "x-apidocgen": { - "skip": true - } - }, - "patch": { - "consumes": [ - "application/json" - ], - "tags": [ - "MCP" - ], - "summary": "Update MCP server config ACL", - "operationId": "update-mcp-server-config-acl", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Organization ID", - "name": "organization", - "in": "path", - "required": true - }, - { - "type": "string", - "format": "uuid", - "description": "MCP server config ID", - "name": "mcpserverconfig", - "in": "path", - "required": true - }, - { - "description": "Update MCP server config ACL request", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.UpdateMCPServerConfigACLRequest" - } - } - ], - "responses": { - "204": { - "description": "No Content" - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ], - "x-apidocgen": { - "skip": true - } - } - }, - "/api/experimental/organizations/{organization}/mcp-servers/{mcpserverconfig}/oauth2/connect": { - "get": { - "tags": [ - "MCP" - ], - "summary": "Initiate MCP server OAuth2 connect", - "operationId": "initiate-mcp-server-oauth2-connect", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Organization ID", - "name": "organization", - "in": "path", - "required": true - }, - { - "type": "string", - "format": "uuid", - "description": "MCP server config ID", - "name": "mcpserverconfig", - "in": "path", - "required": true - } - ], - "responses": { - "307": { - "description": "Temporary Redirect" - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ], - "x-apidocgen": { - "skip": true - } - } - }, - "/api/experimental/organizations/{organization}/members/{user}/chats/model-overrides": { - "get": { - "produces": [ - "application/json" - ], - "tags": [ - "Chats" - ], - "summary": "Get organization member chat model overrides", - "operationId": "get-organization-member-chat-model-overrides", - "parameters": [ - { - "type": "string", - "description": "Organization name or ID", - "name": "organization", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "User name, ID, or me", - "name": "user", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.UserChatPersonalModelOverridesResponse" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ], - "x-apidocgen": { - "skip": true - } - } - }, - "/api/experimental/organizations/{organization}/members/{user}/chats/model-overrides/{context}": { - "put": { - "consumes": [ - "application/json" - ], - "tags": [ - "Chats" - ], - "summary": "Update organization member chat model override", - "operationId": "update-organization-member-chat-model-override", - "parameters": [ - { - "type": "string", - "description": "Organization name or ID", - "name": "organization", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "User name, ID, or me", - "name": "user", - "in": "path", - "required": true - }, - { - "enum": [ - "root", - "general", - "explore" - ], - "type": "string", - "description": "Override context", - "name": "context", - "in": "path", - "required": true - }, - { - "description": "Personal model override", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.UpdateUserChatPersonalModelOverrideRequest" - } - } - ], - "responses": { - "204": { - "description": "No Content" - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ], - "x-apidocgen": { - "skip": true - } - } - }, "/api/experimental/users/{user}/skills": { "get": { "produces": [ @@ -3359,6 +1363,1884 @@ const docTemplate = `{ } } }, + "/api/v2/chats": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Chats" + ], + "summary": "List chats", + "operationId": "list-chats", + "parameters": [ + { + "type": "string", + "description": "Search query. Supports ` + "`" + `title:\u003csubstring\u003e` + "`" + ` (case-insensitive, quote multi-word values), ` + "`" + `archived:bool` + "`" + `, ` + "`" + `has_unread:bool` + "`" + `, ` + "`" + `pr_status:\u003cdraft\\|open\\|merged\\|closed\u003e` + "`" + ` as repeated or comma-separated values, ` + "`" + `source:\u003ccreated_by_me\\|shared_with_me\u003e` + "`" + `, ` + "`" + `diff_url:\u003curl\u003e` + "`" + ` (quote values containing colons), ` + "`" + `pr:\u003cnumber\u003e` + "`" + ` (exact PR number match), ` + "`" + `repo:\u003cowner/repo\u003e` + "`" + ` (case-insensitive substring match against git remote origin or URL), ` + "`" + `pr_title:\u003ctext\u003e` + "`" + ` (case-insensitive PR title substring), ` + "`" + `search:\u003ctext\u003e` + "`" + ` (full-text search across chat titles, PR titles, PR numbers, and message bodies; message bodies match English word stems, e.g. ` + "`" + `refactor` + "`" + ` matches ` + "`" + `refactoring` + "`" + `, and ignore English stopwords; titles and PR titles match whole words case-insensitively without stemming; quote multi-word values; cannot be combined with title, pr_title, or pr; a value that tokenizes to no searchable words, e.g. punctuation only, returns an empty list). Bare terms are not supported; use ` + "`" + `title:\u003cvalue\u003e` + "`" + ` or ` + "`" + `search:\u003cvalue\u003e` + "`" + `.", + "name": "q", + "in": "query" + }, + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "multi", + "description": "Filter by label as key:value. Repeat for multiple (AND logic).", + "name": "label", + "in": "query" + }, + { + "type": "string", + "format": "uuid", + "description": "After ID", + "name": "after_id", + "in": "query" + }, + { + "type": "integer", + "description": "Page limit", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "description": "Page offset", + "name": "offset", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.Chat" + } + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + }, + "post": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Chats" + ], + "summary": "Create chat", + "operationId": "create-chat", + "parameters": [ + { + "description": "Create chat request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.CreateChatRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/codersdk.Chat" + } + }, + "413": { + "description": "Request body exceeds 256 KiB", + "schema": { + "$ref": "#/definitions/codersdk.Response" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/chats/by-workspace": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Chats" + ], + "summary": "List chats by workspace", + "operationId": "list-chats-by-workspace", + "parameters": [ + { + "type": "string", + "description": "Comma-separated workspace IDs", + "name": "workspace_ids", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/coderd.chatsByWorkspaceResponse" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/chats/config/auto-archive-days": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Chats" + ], + "summary": "Get chat auto archive days", + "operationId": "get-chat-auto-archive-days", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.ChatAutoArchiveDaysResponse" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + }, + "put": { + "consumes": [ + "application/json" + ], + "tags": [ + "Chats" + ], + "summary": "Update chat auto archive days", + "operationId": "update-chat-auto-archive-days", + "parameters": [ + { + "description": "Request body", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateChatAutoArchiveDaysRequest" + } + } + ], + "responses": { + "204": { + "description": "No Content" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/chats/config/debug-logging": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Chats" + ], + "summary": "Get chat debug logging setting", + "operationId": "get-chat-debug-logging-setting", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.ChatDebugLoggingAdminSettings" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + }, + "put": { + "consumes": [ + "application/json" + ], + "tags": [ + "Chats" + ], + "summary": "Update chat debug logging setting", + "operationId": "update-chat-debug-logging-setting", + "parameters": [ + { + "description": "Request body", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateChatDebugLoggingAllowUsersRequest" + } + } + ], + "responses": { + "204": { + "description": "No Content" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/chats/config/debug-retention-days": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Chats" + ], + "summary": "Get chat debug retention days", + "operationId": "get-chat-debug-retention-days", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.ChatDebugRetentionDaysResponse" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + }, + "put": { + "consumes": [ + "application/json" + ], + "tags": [ + "Chats" + ], + "summary": "Update chat debug retention days", + "operationId": "update-chat-debug-retention-days", + "parameters": [ + { + "description": "Request body", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateChatDebugRetentionDaysRequest" + } + } + ], + "responses": { + "204": { + "description": "No Content" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/chats/config/personal-model-overrides": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Chats" + ], + "summary": "Get chat personal model override settings", + "operationId": "get-chat-personal-model-override-settings", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.ChatPersonalModelOverridesAdminSettings" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + }, + "put": { + "consumes": [ + "application/json" + ], + "tags": [ + "Chats" + ], + "summary": "Update chat personal model override settings", + "operationId": "update-chat-personal-model-override-settings", + "parameters": [ + { + "description": "Request body", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateChatPersonalModelOverridesAdminSettingsRequest" + } + } + ], + "responses": { + "204": { + "description": "No Content" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/chats/config/plan-mode-instructions": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Chats" + ], + "summary": "Get chat plan mode instructions", + "operationId": "get-chat-plan-mode-instructions", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.ChatPlanModeInstructionsResponse" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + }, + "put": { + "consumes": [ + "application/json" + ], + "tags": [ + "Chats" + ], + "summary": "Update chat plan mode instructions", + "operationId": "update-chat-plan-mode-instructions", + "parameters": [ + { + "description": "Request body", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateChatPlanModeInstructionsRequest" + } + } + ], + "responses": { + "204": { + "description": "No Content" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/chats/config/retention-days": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Chats" + ], + "summary": "Get chat retention days", + "operationId": "get-chat-retention-days", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.ChatRetentionDaysResponse" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + }, + "put": { + "consumes": [ + "application/json" + ], + "tags": [ + "Chats" + ], + "summary": "Update chat retention days", + "operationId": "update-chat-retention-days", + "parameters": [ + { + "description": "Request body", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateChatRetentionDaysRequest" + } + } + ], + "responses": { + "204": { + "description": "No Content" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + } + }, + "/api/v2/chats/config/system-prompt": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Chats" + ], + "summary": "Get chat system prompt", + "operationId": "get-chat-system-prompt", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.ChatSystemPromptResponse" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + }, + "put": { + "consumes": [ + "application/json" + ], + "tags": [ + "Chats" + ], + "summary": "Update chat system prompt", + "operationId": "update-chat-system-prompt", + "parameters": [ + { + "description": "Request body", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateChatSystemPromptRequest" + } + } + ], + "responses": { + "204": { + "description": "No Content" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/chats/config/user-compaction-thresholds": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Chats" + ], + "summary": "Get user chat compaction thresholds", + "operationId": "get-user-chat-compaction-thresholds", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.UserChatCompactionThresholds" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + } + }, + "/api/v2/chats/config/user-compaction-thresholds/{modelConfig}": { + "put": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Chats" + ], + "summary": "Update user chat compaction threshold", + "operationId": "update-user-chat-compaction-threshold", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Model config ID", + "name": "modelConfig", + "in": "path", + "required": true + }, + { + "description": "Request body", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateUserChatCompactionThresholdRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.UserChatCompactionThreshold" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + }, + "delete": { + "tags": [ + "Chats" + ], + "summary": "Delete user chat compaction threshold", + "operationId": "delete-user-chat-compaction-threshold", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Model config ID", + "name": "modelConfig", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + } + }, + "/api/v2/chats/config/user-debug-logging": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Chats" + ], + "summary": "Get user chat debug logging setting", + "operationId": "get-user-chat-debug-logging-setting", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.UserChatDebugLoggingSettings" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + }, + "put": { + "consumes": [ + "application/json" + ], + "tags": [ + "Chats" + ], + "summary": "Update user chat debug logging setting", + "operationId": "update-user-chat-debug-logging-setting", + "parameters": [ + { + "description": "Request body", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateUserChatDebugLoggingRequest" + } + } + ], + "responses": { + "204": { + "description": "No Content" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/chats/config/user-prompt": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Chats" + ], + "summary": "Get user chat custom prompt", + "operationId": "get-user-chat-custom-prompt", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.UserChatCustomPrompt" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + }, + "put": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Chats" + ], + "summary": "Update user chat custom prompt", + "operationId": "update-user-chat-custom-prompt", + "parameters": [ + { + "description": "Request body", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UserChatCustomPrompt" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.UserChatCustomPrompt" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/chats/config/workspace-ttl": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Chats" + ], + "summary": "Get chat workspace time to live", + "operationId": "get-chat-workspace-time-to-live", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.ChatWorkspaceTTLResponse" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + }, + "put": { + "consumes": [ + "application/json" + ], + "tags": [ + "Chats" + ], + "summary": "Update chat workspace time to live", + "operationId": "update-chat-workspace-time-to-live", + "parameters": [ + { + "description": "Request body", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateChatWorkspaceTTLRequest" + } + } + ], + "responses": { + "204": { + "description": "No Content" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/chats/files": { + "post": { + "consumes": [ + "image/png", + "image/jpeg", + "image/gif", + "image/webp", + "text/plain", + "text/markdown", + "text/csv", + "application/json", + "application/pdf" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Chats" + ], + "summary": "Upload chat file", + "operationId": "upload-chat-file", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Organization ID", + "name": "organization", + "in": "query", + "required": true + }, + { + "type": "string", + "example": "attachment; filename=\"image.png\"", + "description": "Attachment disposition carrying the file name", + "name": "Content-Disposition", + "in": "header", + "required": true + }, + { + "description": "Raw file binary data", + "name": "request", + "in": "body", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/codersdk.UploadChatFileResponse" + } + }, + "413": { + "description": "Request body exceeds 10 MiB", + "schema": { + "$ref": "#/definitions/codersdk.Response" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "rawBodyFile": "image.png" + } + } + }, + "/api/v2/chats/files/{file}": { + "get": { + "produces": [ + "image/png", + "image/jpeg", + "image/gif", + "image/webp", + "text/plain", + "text/markdown", + "text/csv", + "application/json", + "application/pdf" + ], + "tags": [ + "Chats" + ], + "summary": "Get chat file", + "operationId": "get-chat-file", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "File ID", + "name": "file", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/chats/files/{file}/download": { + "get": { + "produces": [ + "image/png", + "image/jpeg", + "image/gif", + "image/webp", + "text/plain", + "text/markdown", + "text/csv", + "application/json", + "application/pdf" + ], + "tags": [ + "Chats" + ], + "summary": "Download chat file with signed token", + "operationId": "download-chat-file-with-signed-token", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "File ID", + "name": "file", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Signed download token", + "name": "token", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "x-apidocgen": { + "skip": true + } + } + }, + "/api/v2/chats/files/{file}/download-url": { + "post": { + "produces": [ + "application/json" + ], + "tags": [ + "Chats" + ], + "summary": "Create chat file download URL", + "operationId": "create-chat-file-download-url", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "File ID", + "name": "file", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.ChatFileDownloadURLResponse" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + } + }, + "/api/v2/chats/watch": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Chats" + ], + "summary": "Watch chat events for a user via WebSockets", + "operationId": "watch-chat-events-for-a-user-via-websockets", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.ChatWatchEvent" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/chats/{chat}": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Chats" + ], + "summary": "Get chat by ID", + "operationId": "get-chat-by-id", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.Chat" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + }, + "patch": { + "consumes": [ + "application/json" + ], + "tags": [ + "Chats" + ], + "summary": "Update chat", + "operationId": "update-chat", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true + }, + { + "description": "Update chat request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateChatRequest" + } + } + ], + "responses": { + "204": { + "description": "No Content" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/chats/{chat}/acl": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Chats" + ], + "summary": "Get chat ACLs", + "operationId": "get-chat-acls", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.ChatACL" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + }, + "patch": { + "consumes": [ + "application/json" + ], + "tags": [ + "Chats" + ], + "summary": "Update chat ACL", + "operationId": "update-chat-acl", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true + }, + { + "description": "Update chat ACL request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateChatACL" + } + } + ], + "responses": { + "204": { + "description": "No Content" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + } + }, + "/api/v2/chats/{chat}/compact": { + "post": { + "description": "Requests a manual context compaction on an idle or errored\nchat, clearing any stored error. The compaction runs\nasynchronously through the chat worker and bypasses the\nautomatic usage threshold.", + "produces": [ + "application/json" + ], + "tags": [ + "Chats" + ], + "summary": "Compact chat", + "operationId": "compact-chat", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.Chat" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + } + }, + "/api/v2/chats/{chat}/context": { + "put": { + "produces": [ + "application/json" + ], + "tags": [ + "Chats" + ], + "summary": "Refresh chat context", + "operationId": "refresh-chat-context", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.Chat" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/chats/{chat}/cost": { + "get": { + "description": "Cost covers the whole chat tree: the root chat plus every\nsubagent chat beneath it. Requesting cost for a subagent chat\nreturns that same total.\n\nCost is derived from AI Gateway data, which is subject to its\nown retention period, 60 days by default, configured\nindependently of chat retention. Spend for requests older than\nthat period is no longer reported, so a chat whose requests\nhave all been purged reports zero cost.", + "produces": [ + "application/json" + ], + "tags": [ + "Chats" + ], + "summary": "Get chat cost", + "operationId": "get-chat-cost", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.ChatCost" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/chats/{chat}/diff": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Chats" + ], + "summary": "Get chat diff contents", + "operationId": "get-chat-diff-contents", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.ChatDiffContents" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/chats/{chat}/interrupt": { + "post": { + "produces": [ + "application/json" + ], + "tags": [ + "Chats" + ], + "summary": "Interrupt chat", + "operationId": "interrupt-chat", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.Chat" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/chats/{chat}/messages": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Chats" + ], + "summary": "List chat messages", + "operationId": "list-chat-messages", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "Return messages with id \u003c before_id", + "name": "before_id", + "in": "query" + }, + { + "type": "integer", + "description": "Return messages with id \u003e after_id", + "name": "after_id", + "in": "query" + }, + { + "type": "integer", + "description": "Page size, 1 to 200. Defaults to 50.", + "name": "limit", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.ChatMessagesResponse" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + }, + "post": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Chats" + ], + "summary": "Send chat message", + "operationId": "send-chat-message", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true + }, + { + "description": "Create chat message request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.CreateChatMessageRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.CreateChatMessageResponse" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/chats/{chat}/messages/{message}": { + "patch": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Chats" + ], + "summary": "Edit chat message", + "operationId": "edit-chat-message", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "Message ID", + "name": "message", + "in": "path", + "required": true + }, + { + "description": "Edit chat message request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.EditChatMessageRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.EditChatMessageResponse" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/chats/{chat}/prompts": { + "get": { + "description": "Returns the user-authored prompts in a chat, newest first,\nwith each prompt's text parts concatenated in the order they\nwere authored. Used by the composer to power the up/down\narrow prompt-history cycle without paging through every\nmessage in the chat.", + "produces": [ + "application/json" + ], + "tags": [ + "Chats" + ], + "summary": "List chat user prompts", + "operationId": "list-chat-user-prompts", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "Page size, 0 to 2000. 0 (the default) means the server-side default of 500.", + "name": "limit", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.ChatPromptsResponse" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/chats/{chat}/queue/{queuedMessage}": { + "delete": { + "tags": [ + "Chats" + ], + "summary": "Delete chat queued message", + "operationId": "delete-chat-queued-message", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "Queued message ID", + "name": "queuedMessage", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/chats/{chat}/queue/{queuedMessage}/promote": { + "post": { + "produces": [ + "application/json" + ], + "tags": [ + "Chats" + ], + "summary": "Promote chat queued message", + "operationId": "promote-chat-queued-message", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "Queued message ID", + "name": "queuedMessage", + "in": "path", + "required": true + } + ], + "responses": { + "202": { + "description": "Accepted", + "schema": { + "$ref": "#/definitions/codersdk.Response" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/chats/{chat}/reconcile-invalid": { + "post": { + "produces": [ + "application/json" + ], + "tags": [ + "Chats" + ], + "summary": "Reconcile invalid chat state", + "operationId": "reconcile-invalid-chat-state", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.Chat" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/chats/{chat}/stream": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Chats" + ], + "summary": "Stream chat events via WebSockets", + "operationId": "stream-chat-events-via-websockets", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "Skip snapshot messages with id at or before this cursor", + "name": "after_id", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.ChatStreamEvent" + } + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/chats/{chat}/stream/git": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Chats" + ], + "summary": "Watch chat workspace git state via WebSockets", + "operationId": "watch-chat-workspace-git-state-via-websockets", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.WorkspaceAgentGitServerMessage" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/chats/{chat}/stream/parts": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Chats" + ], + "summary": "Stream chat parts via WebSockets", + "operationId": "stream-chat-parts-via-websockets", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.ChatStreamEvent" + } + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + } + }, + "/api/v2/chats/{chat}/title/propose": { + "post": { + "produces": [ + "application/json" + ], + "tags": [ + "Chats" + ], + "summary": "Propose chat title", + "operationId": "propose-chat-title", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.ProposeChatTitleResponse" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/chats/{chat}/tool-results": { + "post": { + "consumes": [ + "application/json" + ], + "tags": [ + "Chats" + ], + "summary": "Submit chat tool results", + "operationId": "submit-chat-tool-results", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true + }, + { + "description": "Request body", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.SubmitToolResultsRequest" + } + } + ], + "responses": { + "204": { + "description": "No Content" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, "/api/v2/connectionlog": { "get": { "produces": [ @@ -5155,6 +5037,44 @@ const docTemplate = `{ ] } }, + "/api/v2/mcp/servers/{mcpServer}/oauth2/disconnect": { + "delete": { + "produces": [ + "application/json" + ], + "tags": [ + "MCP" + ], + "summary": "Disconnect MCP server OAuth2 token", + "operationId": "disconnect-mcp-server-oauth2-token", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "MCP server config ID", + "name": "mcpServer", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.MCPServerOAuth2DisconnectResponse" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + } + }, "/api/v2/notifications/custom": { "post": { "consumes": [ @@ -6152,6 +6072,421 @@ const docTemplate = `{ ] } }, + "/api/v2/organizations/{organization}/chats/model-overrides": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Chats" + ], + "summary": "List organization chat model overrides", + "operationId": "list-organization-chat-model-overrides", + "parameters": [ + { + "type": "string", + "description": "Organization name or ID", + "name": "organization", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.ChatModelOverridesResponse" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + } + }, + "/api/v2/organizations/{organization}/chats/model-overrides/{context}": { + "put": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Chats" + ], + "summary": "Update organization chat model override", + "operationId": "update-organization-chat-model-override", + "parameters": [ + { + "type": "string", + "description": "Organization name or ID", + "name": "organization", + "in": "path", + "required": true + }, + { + "enum": [ + "general", + "explore", + "title_generation", + "compaction", + "advisor" + ], + "type": "string", + "description": "Override context", + "name": "context", + "in": "path", + "required": true + }, + { + "description": "Model override", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateChatModelOverrideRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.ChatModelOverrideResponse" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + } + }, + "/api/v2/organizations/{organization}/chats/models": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Chats" + ], + "summary": "List AI models and provider descriptors in an organization", + "operationId": "list-ai-models-and-provider-descriptors-in-an-organization", + "parameters": [ + { + "type": "string", + "description": "Organization name or ID", + "name": "organization", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.OrganizationChatModelsResponse" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + }, + "post": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Chats" + ], + "summary": "Create an AI model in an organization", + "operationId": "create-an-ai-model-in-an-organization", + "parameters": [ + { + "type": "string", + "description": "Organization name or ID", + "name": "organization", + "in": "path", + "required": true + }, + { + "description": "Model", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.CreateChatModelRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/codersdk.ChatModel" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + } + }, + "/api/v2/organizations/{organization}/chats/models/{model}": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Chats" + ], + "summary": "Get an AI model", + "operationId": "get-an-ai-model", + "parameters": [ + { + "type": "string", + "description": "Organization name or ID", + "name": "organization", + "in": "path", + "required": true + }, + { + "type": "string", + "format": "uuid", + "description": "Model ID", + "name": "model", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.ChatModel" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + }, + "delete": { + "tags": [ + "Chats" + ], + "summary": "Delete an AI model", + "operationId": "delete-an-ai-model", + "parameters": [ + { + "type": "string", + "description": "Organization name or ID", + "name": "organization", + "in": "path", + "required": true + }, + { + "type": "string", + "format": "uuid", + "description": "Model ID", + "name": "model", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + }, + "patch": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Chats" + ], + "summary": "Update an AI model", + "operationId": "update-an-ai-model", + "parameters": [ + { + "type": "string", + "description": "Organization name or ID", + "name": "organization", + "in": "path", + "required": true + }, + { + "type": "string", + "format": "uuid", + "description": "Model ID", + "name": "model", + "in": "path", + "required": true + }, + { + "description": "Model updates", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateChatModelRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.ChatModel" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + } + }, + "/api/v2/organizations/{organization}/chats/models/{model}/acl": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Chats" + ], + "summary": "Get an AI model ACL", + "operationId": "get-an-ai-model-acl", + "parameters": [ + { + "type": "string", + "description": "Organization name or ID", + "name": "organization", + "in": "path", + "required": true + }, + { + "type": "string", + "format": "uuid", + "description": "Model ID", + "name": "model", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.ChatModelACL" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + }, + "patch": { + "consumes": [ + "application/json" + ], + "tags": [ + "Chats" + ], + "summary": "Update an AI model ACL", + "operationId": "update-an-ai-model-acl", + "parameters": [ + { + "type": "string", + "description": "Organization name or ID", + "name": "organization", + "in": "path", + "required": true + }, + { + "type": "string", + "format": "uuid", + "description": "Model ID", + "name": "model", + "in": "path", + "required": true + }, + { + "description": "Sparse model ACL update", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateChatModelACLRequest" + } + } + ], + "responses": { + "204": { + "description": "No Content" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + } + }, "/api/v2/organizations/{organization}/groups": { "get": { "produces": [ @@ -6436,6 +6771,363 @@ const docTemplate = `{ ] } }, + "/api/v2/organizations/{organization}/mcp-servers": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "MCP" + ], + "summary": "List MCP server configs", + "operationId": "list-mcp-server-configs", + "parameters": [ + { + "type": "string", + "description": "Organization name or ID", + "name": "organization", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.MCPServerConfig" + } + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + }, + "post": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "MCP" + ], + "summary": "Create MCP server config", + "operationId": "create-mcp-server-config", + "parameters": [ + { + "type": "string", + "description": "Organization name or ID", + "name": "organization", + "in": "path", + "required": true + }, + { + "description": "Create MCP server config request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.CreateMCPServerConfigRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/codersdk.MCPServerConfig" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + } + }, + "/api/v2/organizations/{organization}/mcp-servers/{mcpserverconfig}": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "MCP" + ], + "summary": "Get MCP server config", + "operationId": "get-mcp-server-config", + "parameters": [ + { + "type": "string", + "description": "Organization name or ID", + "name": "organization", + "in": "path", + "required": true + }, + { + "type": "string", + "format": "uuid", + "description": "MCP server config ID", + "name": "mcpserverconfig", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.MCPServerConfig" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + }, + "delete": { + "tags": [ + "MCP" + ], + "summary": "Delete MCP server config", + "operationId": "delete-mcp-server-config", + "parameters": [ + { + "type": "string", + "description": "Organization name or ID", + "name": "organization", + "in": "path", + "required": true + }, + { + "type": "string", + "format": "uuid", + "description": "MCP server config ID", + "name": "mcpserverconfig", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + }, + "patch": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "MCP" + ], + "summary": "Update MCP server config", + "operationId": "update-mcp-server-config", + "parameters": [ + { + "type": "string", + "description": "Organization name or ID", + "name": "organization", + "in": "path", + "required": true + }, + { + "type": "string", + "format": "uuid", + "description": "MCP server config ID", + "name": "mcpserverconfig", + "in": "path", + "required": true + }, + { + "description": "Update MCP server config request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateMCPServerConfigRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.MCPServerConfig" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + } + }, + "/api/v2/organizations/{organization}/mcp-servers/{mcpserverconfig}/acl": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "MCP" + ], + "summary": "Get MCP server config ACL", + "operationId": "get-mcp-server-config-acl", + "parameters": [ + { + "type": "string", + "description": "Organization name or ID", + "name": "organization", + "in": "path", + "required": true + }, + { + "type": "string", + "format": "uuid", + "description": "MCP server config ID", + "name": "mcpserverconfig", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.MCPServerConfigACL" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + }, + "patch": { + "consumes": [ + "application/json" + ], + "tags": [ + "MCP" + ], + "summary": "Update MCP server config ACL", + "operationId": "update-mcp-server-config-acl", + "parameters": [ + { + "type": "string", + "description": "Organization name or ID", + "name": "organization", + "in": "path", + "required": true + }, + { + "type": "string", + "format": "uuid", + "description": "MCP server config ID", + "name": "mcpserverconfig", + "in": "path", + "required": true + }, + { + "description": "Update MCP server config ACL request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateMCPServerConfigACLRequest" + } + } + ], + "responses": { + "204": { + "description": "No Content" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + } + }, + "/api/v2/organizations/{organization}/mcp-servers/{mcpserverconfig}/oauth2/connect": { + "get": { + "tags": [ + "MCP" + ], + "summary": "Initiate MCP server OAuth2 connect", + "operationId": "initiate-mcp-server-oauth2-connect", + "parameters": [ + { + "type": "string", + "description": "Organization name or ID", + "name": "organization", + "in": "path", + "required": true + }, + { + "type": "string", + "format": "uuid", + "description": "MCP server config ID", + "name": "mcpserverconfig", + "in": "path", + "required": true + } + ], + "responses": { + "307": { + "description": "Temporary Redirect" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + } + }, "/api/v2/organizations/{organization}/members": { "get": { "produces": [ @@ -6766,6 +7458,112 @@ const docTemplate = `{ ] } }, + "/api/v2/organizations/{organization}/members/{user}/chats/model-overrides": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Chats" + ], + "summary": "Get organization member chat model overrides", + "operationId": "get-organization-member-chat-model-overrides", + "parameters": [ + { + "type": "string", + "description": "Organization name or ID", + "name": "organization", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "User name, ID, or me", + "name": "user", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.UserChatPersonalModelOverridesResponse" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + } + }, + "/api/v2/organizations/{organization}/members/{user}/chats/model-overrides/{context}": { + "put": { + "consumes": [ + "application/json" + ], + "tags": [ + "Chats" + ], + "summary": "Update organization member chat model override", + "operationId": "update-organization-member-chat-model-override", + "parameters": [ + { + "type": "string", + "description": "Organization name or ID", + "name": "organization", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "User name, ID, or me", + "name": "user", + "in": "path", + "required": true + }, + { + "enum": [ + "root", + "general", + "explore" + ], + "type": "string", + "description": "Override context", + "name": "context", + "in": "path", + "required": true + }, + { + "description": "Personal model override", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateUserChatPersonalModelOverrideRequest" + } + } + ], + "responses": { + "204": { + "description": "No Content" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + } + }, "/api/v2/organizations/{organization}/members/{user}/roles": { "put": { "consumes": [ @@ -10857,6 +11655,131 @@ const docTemplate = `{ ] } }, + "/api/v2/users/{user}/ai-provider-keys": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Chats" + ], + "summary": "List user AI provider key configurations", + "operationId": "list-user-ai-provider-key-configurations", + "parameters": [ + { + "type": "string", + "description": "User ID, username, or me", + "name": "user", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.UserAIProviderKeyConfig" + } + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/users/{user}/ai-provider-keys/{aiProvider}": { + "put": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Chats" + ], + "summary": "Update user AI provider key", + "operationId": "update-user-ai-provider-key", + "parameters": [ + { + "type": "string", + "description": "User ID, username, or me", + "name": "user", + "in": "path", + "required": true + }, + { + "type": "string", + "format": "uuid", + "description": "AI provider ID", + "name": "aiProvider", + "in": "path", + "required": true + }, + { + "description": "Request body", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.CreateUserAIProviderKeyRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.UserAIProviderKeyConfig" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + }, + "delete": { + "tags": [ + "Chats" + ], + "summary": "Delete user AI provider key", + "operationId": "delete-user-ai-provider-key", + "parameters": [ + { + "type": "string", + "description": "User ID, username, or me", + "name": "user", + "in": "path", + "required": true + }, + { + "type": "string", + "format": "uuid", + "description": "AI provider ID", + "name": "aiProvider", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, "/api/v2/users/{user}/ai/budget/override": { "get": { "produces": [ @@ -16138,6 +17061,12 @@ const docTemplate = `{ "ReinitializeReasonPrebuildClaimed" ] }, + "coderd.chatsByWorkspaceResponse": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, "coderd.cspViolation": { "type": "object", "properties": { @@ -16929,6 +17858,33 @@ const docTemplate = `{ } } }, + "codersdk.AIProviderSummary": { + "type": "object", + "properties": { + "deleted": { + "type": "boolean" + }, + "display_name": { + "type": "string" + }, + "enabled": { + "type": "boolean" + }, + "icon": { + "type": "string" + }, + "id": { + "type": "string", + "format": "uuid" + }, + "name": { + "type": "string" + }, + "type": { + "$ref": "#/definitions/codersdk.AIProviderType" + } + } + }, "codersdk.AIProviderType": { "type": "string", "enum": [ @@ -18353,6 +19309,14 @@ const docTemplate = `{ } } }, + "codersdk.ChatAutoArchiveDaysResponse": { + "type": "object", + "properties": { + "auto_archive_days": { + "type": "integer" + } + } + }, "codersdk.ChatBusyBehavior": { "type": "string", "enum": [ @@ -18531,6 +19495,25 @@ const docTemplate = `{ } } }, + "codersdk.ChatDebugLoggingAdminSettings": { + "type": "object", + "properties": { + "allow_users": { + "type": "boolean" + }, + "forced_by_deployment": { + "type": "boolean" + } + } + }, + "codersdk.ChatDebugRetentionDaysResponse": { + "type": "object", + "properties": { + "debug_retention_days": { + "type": "integer" + } + } + }, "codersdk.ChatDiffContents": { "type": "object", "properties": { @@ -19755,6 +20738,14 @@ const docTemplate = `{ "ChatPersonalModelOverrideModeModel" ] }, + "codersdk.ChatPersonalModelOverridesAdminSettings": { + "type": "object", + "properties": { + "allow_users": { + "type": "boolean" + } + } + }, "codersdk.ChatPlanMode": { "type": "string", "enum": [ @@ -19764,6 +20755,14 @@ const docTemplate = `{ "ChatPlanModePlan" ] }, + "codersdk.ChatPlanModeInstructionsResponse": { + "type": "object", + "properties": { + "plan_mode_instructions": { + "type": "string" + } + } + }, "codersdk.ChatPrompt": { "type": "object", "properties": { @@ -20000,6 +20999,20 @@ const docTemplate = `{ } } }, + "codersdk.ChatSystemPromptResponse": { + "type": "object", + "properties": { + "default_system_prompt": { + "type": "string" + }, + "include_default_system_prompt": { + "type": "boolean" + }, + "system_prompt": { + "type": "string" + } + } + }, "codersdk.ChatUnsupportedProvider": { "type": "object", "properties": { @@ -20087,6 +21100,15 @@ const docTemplate = `{ "ChatWatchEventKindContextDirty" ] }, + "codersdk.ChatWorkspaceTTLResponse": { + "type": "object", + "properties": { + "workspace_ttl_ms": { + "description": "WorkspaceTTLMillis is the workspace TTL in milliseconds.\nZero means disabled; the template's own autostop setting applies.", + "type": "integer" + } + } + }, "codersdk.ClusterConfig": { "type": "object", "properties": { @@ -21091,6 +22113,14 @@ const docTemplate = `{ } } }, + "codersdk.CreateUserAIProviderKeyRequest": { + "type": "object", + "properties": { + "api_key": { + "type": "string" + } + } + }, "codersdk.CreateUserRequestWithOrgs": { "type": "object", "required": [ @@ -26441,6 +27471,17 @@ const docTemplate = `{ } } }, + "codersdk.SubmitToolResultsRequest": { + "type": "object", + "properties": { + "results": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.ToolResult" + } + } + } + }, "codersdk.SupportConfig": { "type": "object", "properties": { @@ -27685,6 +28726,23 @@ const docTemplate = `{ } } }, + "codersdk.ToolResult": { + "type": "object", + "properties": { + "is_error": { + "type": "boolean" + }, + "output": { + "type": "array", + "items": { + "type": "integer" + } + }, + "tool_call_id": { + "type": "string" + } + } + }, "codersdk.TraceConfig": { "type": "object", "properties": { @@ -27795,6 +28853,30 @@ const docTemplate = `{ } } }, + "codersdk.UpdateChatAutoArchiveDaysRequest": { + "type": "object", + "properties": { + "auto_archive_days": { + "type": "integer" + } + } + }, + "codersdk.UpdateChatDebugLoggingAllowUsersRequest": { + "type": "object", + "properties": { + "allow_users": { + "type": "boolean" + } + } + }, + "codersdk.UpdateChatDebugRetentionDaysRequest": { + "type": "object", + "properties": { + "debug_retention_days": { + "type": "integer" + } + } + }, "codersdk.UpdateChatModelACLRequest": { "type": "object", "properties": { @@ -27853,6 +28935,22 @@ const docTemplate = `{ } } }, + "codersdk.UpdateChatPersonalModelOverridesAdminSettingsRequest": { + "type": "object", + "properties": { + "allow_users": { + "type": "boolean" + } + } + }, + "codersdk.UpdateChatPlanModeInstructionsRequest": { + "type": "object", + "properties": { + "plan_mode_instructions": { + "type": "string" + } + } + }, "codersdk.UpdateChatRequest": { "type": "object", "properties": { @@ -27894,6 +28992,26 @@ const docTemplate = `{ } } }, + "codersdk.UpdateChatSystemPromptRequest": { + "type": "object", + "properties": { + "include_default_system_prompt": { + "type": "boolean" + }, + "system_prompt": { + "type": "string" + } + } + }, + "codersdk.UpdateChatWorkspaceTTLRequest": { + "type": "object", + "properties": { + "workspace_ttl_ms": { + "description": "WorkspaceTTLMillis is the workspace TTL in milliseconds.\nZero means disabled; the template's own autostop setting applies.", + "type": "integer" + } + } + }, "codersdk.UpdateCheckResponse": { "type": "object", "properties": { @@ -28241,6 +29359,24 @@ const docTemplate = `{ } } }, + "codersdk.UpdateUserChatCompactionThresholdRequest": { + "type": "object", + "properties": { + "threshold_percent": { + "type": "integer", + "maximum": 100, + "minimum": 0 + } + } + }, + "codersdk.UpdateUserChatDebugLoggingRequest": { + "type": "object", + "properties": { + "debug_logging_enabled": { + "type": "boolean" + } + } + }, "codersdk.UpdateUserChatPersonalModelOverrideRequest": { "type": "object", "properties": { @@ -28688,6 +29824,23 @@ const docTemplate = `{ } } }, + "codersdk.UserAIProviderKeyConfig": { + "type": "object", + "properties": { + "byok_enabled": { + "type": "boolean" + }, + "has_provider_api_key": { + "type": "boolean" + }, + "has_user_api_key": { + "type": "boolean" + }, + "provider": { + "$ref": "#/definitions/codersdk.AIProviderSummary" + } + } + }, "codersdk.UserAISpendStatus": { "type": "object", "properties": { @@ -28808,6 +29961,51 @@ const docTemplate = `{ } } }, + "codersdk.UserChatCompactionThreshold": { + "type": "object", + "properties": { + "model_config_id": { + "type": "string", + "format": "uuid" + }, + "threshold_percent": { + "type": "integer" + } + } + }, + "codersdk.UserChatCompactionThresholds": { + "type": "object", + "properties": { + "thresholds": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.UserChatCompactionThreshold" + } + } + } + }, + "codersdk.UserChatCustomPrompt": { + "type": "object", + "properties": { + "custom_prompt": { + "type": "string" + } + } + }, + "codersdk.UserChatDebugLoggingSettings": { + "type": "object", + "properties": { + "debug_logging_enabled": { + "type": "boolean" + }, + "forced_by_deployment": { + "type": "boolean" + }, + "user_toggle_allowed": { + "type": "boolean" + } + } + }, "codersdk.UserChatPersonalModelOverridesResponse": { "type": "object", "properties": { diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 4ce1e67d1e..9ca41846ae 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -127,872 +127,6 @@ } } }, - "/api/experimental/chats": { - "get": { - "description": "Experimental: this endpoint is subject to change.", - "produces": ["application/json"], - "tags": ["Chats"], - "summary": "List chats", - "operationId": "list-chats", - "parameters": [ - { - "type": "string", - "description": "Search query. Supports `title:\u003csubstring\u003e` (case-insensitive, quote multi-word values), `archived:bool`, `has_unread:bool`, `pr_status:\u003cdraft\\|open\\|merged\\|closed\u003e` as repeated or comma-separated values, `source:\u003ccreated_by_me\\|shared_with_me\u003e`, `diff_url:\u003curl\u003e` (quote values containing colons), `pr:\u003cnumber\u003e` (exact PR number match), `repo:\u003cowner/repo\u003e` (case-insensitive substring match against git remote origin or URL), `pr_title:\u003ctext\u003e` (case-insensitive PR title substring), `search:\u003ctext\u003e` (full-text search across chat titles, PR titles, PR numbers, and message bodies; message bodies match English word stems, e.g. `refactor` matches `refactoring`, and ignore English stopwords; titles and PR titles match whole words case-insensitively without stemming; quote multi-word values; cannot be combined with title, pr_title, or pr; a value that tokenizes to no searchable words, e.g. punctuation only, returns an empty list). Bare terms are not supported; use `title:\u003cvalue\u003e` or `search:\u003cvalue\u003e`.", - "name": "q", - "in": "query" - }, - { - "type": "string", - "description": "Filter by label as key:value. Repeat for multiple (AND logic).", - "name": "label", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/codersdk.Chat" - } - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ] - }, - "post": { - "description": "Experimental: this endpoint is subject to change.", - "consumes": ["application/json"], - "produces": ["application/json"], - "tags": ["Chats"], - "summary": "Create chat", - "operationId": "create-chat", - "parameters": [ - { - "description": "Create chat request", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.CreateChatRequest" - } - } - ], - "responses": { - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/codersdk.Chat" - } - }, - "413": { - "description": "Request body exceeds 256 KiB", - "schema": { - "$ref": "#/definitions/codersdk.Response" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ] - } - }, - "/api/experimental/chats/config/retention-days": { - "get": { - "produces": ["application/json"], - "tags": ["Chats"], - "summary": "Get chat retention days", - "operationId": "get-chat-retention-days", - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.ChatRetentionDaysResponse" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ], - "x-apidocgen": { - "skip": true - } - }, - "put": { - "consumes": ["application/json"], - "tags": ["Chats"], - "summary": "Update chat retention days", - "operationId": "update-chat-retention-days", - "parameters": [ - { - "description": "Request body", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.UpdateChatRetentionDaysRequest" - } - } - ], - "responses": { - "204": { - "description": "No Content" - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ], - "x-apidocgen": { - "skip": true - } - } - }, - "/api/experimental/chats/files": { - "post": { - "description": "Experimental: this endpoint is subject to change.", - "consumes": [ - "image/png", - "image/jpeg", - "image/gif", - "image/webp", - "text/plain", - "text/markdown", - "text/csv", - "application/json", - "application/pdf" - ], - "produces": ["application/json"], - "tags": ["Chats"], - "summary": "Upload chat file", - "operationId": "upload-chat-file", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Organization ID", - "name": "organization", - "in": "query", - "required": true - } - ], - "responses": { - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/codersdk.UploadChatFileResponse" - } - }, - "413": { - "description": "Request body exceeds 10 MiB", - "schema": { - "$ref": "#/definitions/codersdk.Response" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ] - } - }, - "/api/experimental/chats/files/{file}": { - "get": { - "description": "Experimental: this endpoint is subject to change.", - "produces": [ - "image/png", - "image/jpeg", - "image/gif", - "image/webp", - "text/plain", - "text/markdown", - "text/csv", - "application/json", - "application/pdf" - ], - "tags": ["Chats"], - "summary": "Get chat file", - "operationId": "get-chat-file", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "File ID", - "name": "file", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK" - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ] - } - }, - "/api/experimental/chats/files/{file}/download": { - "get": { - "description": "Experimental: this endpoint is subject to change.", - "produces": [ - "image/png", - "image/jpeg", - "image/gif", - "image/webp", - "text/plain", - "text/markdown", - "text/csv", - "application/json", - "application/pdf" - ], - "tags": ["Chats"], - "summary": "Download chat file with signed token", - "operationId": "download-chat-file", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "File ID", - "name": "file", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Signed download token", - "name": "token", - "in": "query", - "required": true - } - ], - "responses": { - "200": { - "description": "OK" - } - }, - "x-apidocgen": { - "skip": true - } - } - }, - "/api/experimental/chats/files/{file}/download-url": { - "post": { - "description": "Experimental: this endpoint is subject to change.", - "produces": ["application/json"], - "tags": ["Chats"], - "summary": "Create chat file download URL", - "operationId": "create-chat-file-download-url", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "File ID", - "name": "file", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.ChatFileDownloadURLResponse" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ], - "x-apidocgen": { - "skip": true - } - } - }, - "/api/experimental/chats/watch": { - "get": { - "description": "Experimental: this endpoint is subject to change.", - "produces": ["application/json"], - "tags": ["Chats"], - "summary": "Watch chat events for a user via WebSockets", - "operationId": "watch-chat-events-for-a-user-via-websockets", - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.ChatWatchEvent" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ] - } - }, - "/api/experimental/chats/{chat}": { - "get": { - "description": "Experimental: this endpoint is subject to change.", - "produces": ["application/json"], - "tags": ["Chats"], - "summary": "Get chat by ID", - "operationId": "get-chat-by-id", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Chat ID", - "name": "chat", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.Chat" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ] - }, - "patch": { - "description": "Experimental: this endpoint is subject to change.", - "consumes": ["application/json"], - "tags": ["Chats"], - "summary": "Update chat", - "operationId": "update-chat", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Chat ID", - "name": "chat", - "in": "path", - "required": true - }, - { - "description": "Update chat request", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.UpdateChatRequest" - } - } - ], - "responses": { - "204": { - "description": "No Content" - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ] - } - }, - "/api/experimental/chats/{chat}/acl": { - "get": { - "description": "Experimental: this endpoint is subject to change.", - "produces": ["application/json"], - "tags": ["Chats"], - "summary": "Get chat ACLs", - "operationId": "get-chat-acls", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Chat ID", - "name": "chat", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.ChatACL" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ], - "x-apidocgen": { - "skip": true - } - }, - "patch": { - "description": "Experimental: this endpoint is subject to change.", - "consumes": ["application/json"], - "tags": ["Chats"], - "summary": "Update chat ACL", - "operationId": "update-chat-acl", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Chat ID", - "name": "chat", - "in": "path", - "required": true - }, - { - "description": "Update chat ACL request", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.UpdateChatACL" - } - } - ], - "responses": { - "204": { - "description": "No Content" - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ], - "x-apidocgen": { - "skip": true - } - } - }, - "/api/experimental/chats/{chat}/compact": { - "post": { - "description": "Experimental: this endpoint is subject to change.\nRequests a manual context compaction on an idle or errored\nchat, clearing any stored error. The compaction runs\nasynchronously through the chat worker and bypasses the\nautomatic usage threshold.", - "produces": ["application/json"], - "tags": ["Chats"], - "summary": "Compact chat", - "operationId": "compact-chat", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Chat ID", - "name": "chat", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.Chat" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ], - "x-apidocgen": { - "skip": true - } - } - }, - "/api/experimental/chats/{chat}/context": { - "put": { - "description": "Experimental: this endpoint is subject to change.", - "produces": ["application/json"], - "tags": ["Chats"], - "summary": "Refresh chat context", - "operationId": "refresh-chat-context", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Chat ID", - "name": "chat", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.Chat" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ] - } - }, - "/api/experimental/chats/{chat}/cost": { - "get": { - "description": "Experimental: this endpoint is subject to change.\n\nCost covers the whole chat tree: the root chat plus every\nsubagent chat beneath it. Requesting cost for a subagent chat\nreturns that same total.\n\nCost is derived from AI Gateway data, which is subject to its\nown retention period, 60 days by default, configured\nindependently of chat retention. Spend for requests older than\nthat period is no longer reported, so a chat whose requests\nhave all been purged reports zero cost.", - "produces": ["application/json"], - "tags": ["Chats"], - "summary": "Get chat cost", - "operationId": "get-chat-cost", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Chat ID", - "name": "chat", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.ChatCost" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ] - } - }, - "/api/experimental/chats/{chat}/diff": { - "get": { - "description": "Experimental: this endpoint is subject to change.", - "produces": ["application/json"], - "tags": ["Chats"], - "summary": "Get chat diff contents", - "operationId": "get-chat-diff-contents", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Chat ID", - "name": "chat", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.ChatDiffContents" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ] - } - }, - "/api/experimental/chats/{chat}/interrupt": { - "post": { - "description": "Experimental: this endpoint is subject to change.", - "produces": ["application/json"], - "tags": ["Chats"], - "summary": "Interrupt chat", - "operationId": "interrupt-chat", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Chat ID", - "name": "chat", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.Chat" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ] - } - }, - "/api/experimental/chats/{chat}/messages": { - "get": { - "description": "Experimental: this endpoint is subject to change.", - "produces": ["application/json"], - "tags": ["Chats"], - "summary": "List chat messages", - "operationId": "list-chat-messages", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Chat ID", - "name": "chat", - "in": "path", - "required": true - }, - { - "type": "integer", - "description": "Return messages with id \u003c before_id", - "name": "before_id", - "in": "query" - }, - { - "type": "integer", - "description": "Return messages with id \u003e after_id", - "name": "after_id", - "in": "query" - }, - { - "type": "integer", - "description": "Page size, 1 to 200. Defaults to 50.", - "name": "limit", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.ChatMessagesResponse" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ] - }, - "post": { - "description": "Experimental: this endpoint is subject to change.", - "consumes": ["application/json"], - "produces": ["application/json"], - "tags": ["Chats"], - "summary": "Send chat message", - "operationId": "send-chat-message", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Chat ID", - "name": "chat", - "in": "path", - "required": true - }, - { - "description": "Create chat message request", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.CreateChatMessageRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.CreateChatMessageResponse" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ] - } - }, - "/api/experimental/chats/{chat}/messages/{message}": { - "patch": { - "description": "Experimental: this endpoint is subject to change.", - "consumes": ["application/json"], - "produces": ["application/json"], - "tags": ["Chats"], - "summary": "Edit chat message", - "operationId": "edit-chat-message", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Chat ID", - "name": "chat", - "in": "path", - "required": true - }, - { - "type": "integer", - "description": "Message ID", - "name": "message", - "in": "path", - "required": true - }, - { - "description": "Edit chat message request", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.EditChatMessageRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.EditChatMessageResponse" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ] - } - }, - "/api/experimental/chats/{chat}/prompts": { - "get": { - "description": "Experimental: this endpoint is subject to change.\n\nReturns the user-authored prompts in a chat, newest first,\nwith each prompt's text parts concatenated in the order they\nwere authored. Used by the composer to power the up/down\narrow prompt-history cycle without paging through every\nmessage in the chat.", - "produces": ["application/json"], - "tags": ["Chats"], - "summary": "List chat user prompts", - "operationId": "list-chat-user-prompts", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Chat ID", - "name": "chat", - "in": "path", - "required": true - }, - { - "type": "integer", - "description": "Page size, 0 to 2000. 0 (the default) means the server-side default of 500.", - "name": "limit", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.ChatPromptsResponse" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ] - } - }, - "/api/experimental/chats/{chat}/reconcile-invalid": { - "post": { - "description": "Experimental: this endpoint is subject to change.", - "produces": ["application/json"], - "tags": ["Chats"], - "summary": "Reconcile invalid chat state", - "operationId": "reconcile-invalid-chat-state", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Chat ID", - "name": "chat", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.Chat" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ] - } - }, - "/api/experimental/chats/{chat}/stream": { - "get": { - "description": "Experimental: this endpoint is subject to change.", - "produces": ["application/json"], - "tags": ["Chats"], - "summary": "Stream chat events via WebSockets", - "operationId": "stream-chat-events-via-websockets", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Chat ID", - "name": "chat", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.ChatStreamEvent" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ] - } - }, "/api/experimental/chats/{chat}/stream/desktop": { "get": { "description": "Raw binary WebSocket stream of the chat workspace desktop.\nExperimental: this endpoint is subject to change.", @@ -1022,105 +156,6 @@ ] } }, - "/api/experimental/chats/{chat}/stream/git": { - "get": { - "description": "Experimental: this endpoint is subject to change.", - "produces": ["application/json"], - "tags": ["Chats"], - "summary": "Watch chat workspace git state via WebSockets", - "operationId": "watch-chat-workspace-git-state-via-websockets", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Chat ID", - "name": "chat", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.WorkspaceAgentGitServerMessage" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ] - } - }, - "/api/experimental/chats/{chat}/stream/parts": { - "get": { - "description": "Experimental: this endpoint is subject to change.", - "produces": ["application/json"], - "tags": ["Chats"], - "summary": "Stream chat parts via WebSockets", - "operationId": "stream-chat-parts-via-websockets", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Chat ID", - "name": "chat", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.ChatStreamEvent" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ], - "x-apidocgen": { - "skip": true - } - } - }, - "/api/experimental/chats/{chat}/title/propose": { - "post": { - "description": "Experimental: this endpoint is subject to change.", - "produces": ["application/json"], - "tags": ["Chats"], - "summary": "Propose chat title", - "operationId": "propose-chat-title", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Chat ID", - "name": "chat", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.ProposeChatTitleResponse" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ] - } - }, "/api/experimental/mcp/servers/{mcpServer}/oauth2/callback": { "get": { "produces": ["text/html"], @@ -1176,839 +211,6 @@ } } }, - "/api/experimental/mcp/servers/{mcpServer}/oauth2/disconnect": { - "delete": { - "produces": ["application/json"], - "tags": ["MCP"], - "summary": "Disconnect MCP server OAuth2 token", - "operationId": "disconnect-mcp-server-oauth2-token", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "MCP server config ID", - "name": "mcpServer", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.MCPServerOAuth2DisconnectResponse" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ], - "x-apidocgen": { - "skip": true - } - } - }, - "/api/experimental/organizations/{organization}/chats/model-overrides": { - "get": { - "produces": ["application/json"], - "tags": ["Chats"], - "summary": "List organization chat model overrides", - "operationId": "list-organization-chat-model-overrides", - "parameters": [ - { - "type": "string", - "description": "Organization name or ID", - "name": "organization", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.ChatModelOverridesResponse" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ], - "x-apidocgen": { - "skip": true - } - } - }, - "/api/experimental/organizations/{organization}/chats/model-overrides/{context}": { - "put": { - "consumes": ["application/json"], - "produces": ["application/json"], - "tags": ["Chats"], - "summary": "Update organization chat model override", - "operationId": "update-organization-chat-model-override", - "parameters": [ - { - "type": "string", - "description": "Organization name or ID", - "name": "organization", - "in": "path", - "required": true - }, - { - "enum": [ - "general", - "explore", - "title_generation", - "compaction", - "advisor" - ], - "type": "string", - "description": "Override context", - "name": "context", - "in": "path", - "required": true - }, - { - "description": "Model override", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.UpdateChatModelOverrideRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.ChatModelOverrideResponse" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ], - "x-apidocgen": { - "skip": true - } - } - }, - "/api/experimental/organizations/{organization}/chats/models": { - "get": { - "produces": ["application/json"], - "tags": ["Chats"], - "summary": "List AI models and provider descriptors in an organization", - "operationId": "list-ai-models-by-organization", - "parameters": [ - { - "type": "string", - "description": "Organization name or ID", - "name": "organization", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.OrganizationChatModelsResponse" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ], - "x-apidocgen": { - "skip": true - } - }, - "post": { - "consumes": ["application/json"], - "produces": ["application/json"], - "tags": ["Chats"], - "summary": "Create an AI model in an organization", - "operationId": "create-ai-model", - "parameters": [ - { - "type": "string", - "description": "Organization name or ID", - "name": "organization", - "in": "path", - "required": true - }, - { - "description": "Model", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.CreateChatModelRequest" - } - } - ], - "responses": { - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/codersdk.ChatModel" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ], - "x-apidocgen": { - "skip": true - } - } - }, - "/api/experimental/organizations/{organization}/chats/models/{model}": { - "get": { - "produces": ["application/json"], - "tags": ["Chats"], - "summary": "Get an AI model", - "operationId": "get-ai-model", - "parameters": [ - { - "type": "string", - "description": "Organization name or ID", - "name": "organization", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Model ID", - "name": "model", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.ChatModel" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ], - "x-apidocgen": { - "skip": true - } - }, - "delete": { - "tags": ["Chats"], - "summary": "Delete an AI model", - "operationId": "delete-ai-model", - "parameters": [ - { - "type": "string", - "description": "Organization name or ID", - "name": "organization", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Model ID", - "name": "model", - "in": "path", - "required": true - } - ], - "responses": { - "204": { - "description": "No Content" - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ], - "x-apidocgen": { - "skip": true - } - }, - "patch": { - "consumes": ["application/json"], - "produces": ["application/json"], - "tags": ["Chats"], - "summary": "Update an AI model", - "operationId": "update-ai-model", - "parameters": [ - { - "type": "string", - "description": "Organization name or ID", - "name": "organization", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Model ID", - "name": "model", - "in": "path", - "required": true - }, - { - "description": "Model updates", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.UpdateChatModelRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.ChatModel" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ], - "x-apidocgen": { - "skip": true - } - } - }, - "/api/experimental/organizations/{organization}/chats/models/{model}/acl": { - "get": { - "produces": ["application/json"], - "tags": ["Chats"], - "summary": "Get an AI model ACL", - "operationId": "get-ai-model-acl", - "parameters": [ - { - "type": "string", - "description": "Organization name or ID", - "name": "organization", - "in": "path", - "required": true - }, - { - "type": "string", - "format": "uuid", - "description": "Model ID", - "name": "model", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.ChatModelACL" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ], - "x-apidocgen": { - "skip": true - } - }, - "patch": { - "consumes": ["application/json"], - "tags": ["Chats"], - "summary": "Update an AI model ACL", - "operationId": "update-ai-model-acl", - "parameters": [ - { - "type": "string", - "description": "Organization name or ID", - "name": "organization", - "in": "path", - "required": true - }, - { - "type": "string", - "format": "uuid", - "description": "Model ID", - "name": "model", - "in": "path", - "required": true - }, - { - "description": "Sparse model ACL update", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.UpdateChatModelACLRequest" - } - } - ], - "responses": { - "204": { - "description": "No Content" - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ], - "x-apidocgen": { - "skip": true - } - } - }, - "/api/experimental/organizations/{organization}/mcp-servers": { - "get": { - "produces": ["application/json"], - "tags": ["MCP"], - "summary": "List MCP server configs", - "operationId": "list-mcp-server-configs", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Organization ID", - "name": "organization", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/codersdk.MCPServerConfig" - } - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ], - "x-apidocgen": { - "skip": true - } - }, - "post": { - "consumes": ["application/json"], - "produces": ["application/json"], - "tags": ["MCP"], - "summary": "Create MCP server config", - "operationId": "create-mcp-server-config", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Organization ID", - "name": "organization", - "in": "path", - "required": true - }, - { - "description": "Create MCP server config request", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.CreateMCPServerConfigRequest" - } - } - ], - "responses": { - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/codersdk.MCPServerConfig" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ], - "x-apidocgen": { - "skip": true - } - } - }, - "/api/experimental/organizations/{organization}/mcp-servers/{mcpserverconfig}": { - "get": { - "produces": ["application/json"], - "tags": ["MCP"], - "summary": "Get MCP server config", - "operationId": "get-mcp-server-config", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Organization ID", - "name": "organization", - "in": "path", - "required": true - }, - { - "type": "string", - "format": "uuid", - "description": "MCP server config ID", - "name": "mcpserverconfig", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.MCPServerConfig" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ], - "x-apidocgen": { - "skip": true - } - }, - "delete": { - "tags": ["MCP"], - "summary": "Delete MCP server config", - "operationId": "delete-mcp-server-config", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Organization ID", - "name": "organization", - "in": "path", - "required": true - }, - { - "type": "string", - "format": "uuid", - "description": "MCP server config ID", - "name": "mcpserverconfig", - "in": "path", - "required": true - } - ], - "responses": { - "204": { - "description": "No Content" - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ], - "x-apidocgen": { - "skip": true - } - }, - "patch": { - "consumes": ["application/json"], - "produces": ["application/json"], - "tags": ["MCP"], - "summary": "Update MCP server config", - "operationId": "update-mcp-server-config", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Organization ID", - "name": "organization", - "in": "path", - "required": true - }, - { - "type": "string", - "format": "uuid", - "description": "MCP server config ID", - "name": "mcpserverconfig", - "in": "path", - "required": true - }, - { - "description": "Update MCP server config request", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.UpdateMCPServerConfigRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.MCPServerConfig" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ], - "x-apidocgen": { - "skip": true - } - } - }, - "/api/experimental/organizations/{organization}/mcp-servers/{mcpserverconfig}/acl": { - "get": { - "produces": ["application/json"], - "tags": ["MCP"], - "summary": "Get MCP server config ACL", - "operationId": "get-mcp-server-config-acl", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Organization ID", - "name": "organization", - "in": "path", - "required": true - }, - { - "type": "string", - "format": "uuid", - "description": "MCP server config ID", - "name": "mcpserverconfig", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.MCPServerConfigACL" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ], - "x-apidocgen": { - "skip": true - } - }, - "patch": { - "consumes": ["application/json"], - "tags": ["MCP"], - "summary": "Update MCP server config ACL", - "operationId": "update-mcp-server-config-acl", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Organization ID", - "name": "organization", - "in": "path", - "required": true - }, - { - "type": "string", - "format": "uuid", - "description": "MCP server config ID", - "name": "mcpserverconfig", - "in": "path", - "required": true - }, - { - "description": "Update MCP server config ACL request", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.UpdateMCPServerConfigACLRequest" - } - } - ], - "responses": { - "204": { - "description": "No Content" - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ], - "x-apidocgen": { - "skip": true - } - } - }, - "/api/experimental/organizations/{organization}/mcp-servers/{mcpserverconfig}/oauth2/connect": { - "get": { - "tags": ["MCP"], - "summary": "Initiate MCP server OAuth2 connect", - "operationId": "initiate-mcp-server-oauth2-connect", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Organization ID", - "name": "organization", - "in": "path", - "required": true - }, - { - "type": "string", - "format": "uuid", - "description": "MCP server config ID", - "name": "mcpserverconfig", - "in": "path", - "required": true - } - ], - "responses": { - "307": { - "description": "Temporary Redirect" - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ], - "x-apidocgen": { - "skip": true - } - } - }, - "/api/experimental/organizations/{organization}/members/{user}/chats/model-overrides": { - "get": { - "produces": ["application/json"], - "tags": ["Chats"], - "summary": "Get organization member chat model overrides", - "operationId": "get-organization-member-chat-model-overrides", - "parameters": [ - { - "type": "string", - "description": "Organization name or ID", - "name": "organization", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "User name, ID, or me", - "name": "user", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.UserChatPersonalModelOverridesResponse" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ], - "x-apidocgen": { - "skip": true - } - } - }, - "/api/experimental/organizations/{organization}/members/{user}/chats/model-overrides/{context}": { - "put": { - "consumes": ["application/json"], - "tags": ["Chats"], - "summary": "Update organization member chat model override", - "operationId": "update-organization-member-chat-model-override", - "parameters": [ - { - "type": "string", - "description": "Organization name or ID", - "name": "organization", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "User name, ID, or me", - "name": "user", - "in": "path", - "required": true - }, - { - "enum": ["root", "general", "explore"], - "type": "string", - "description": "Override context", - "name": "context", - "in": "path", - "required": true - }, - { - "description": "Personal model override", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.UpdateUserChatPersonalModelOverrideRequest" - } - } - ], - "responses": { - "204": { - "description": "No Content" - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ], - "x-apidocgen": { - "skip": true - } - } - }, "/api/experimental/users/{user}/skills": { "get": { "produces": ["application/json"], @@ -2992,6 +1194,1674 @@ } } }, + "/api/v2/chats": { + "get": { + "produces": ["application/json"], + "tags": ["Chats"], + "summary": "List chats", + "operationId": "list-chats", + "parameters": [ + { + "type": "string", + "description": "Search query. Supports `title:\u003csubstring\u003e` (case-insensitive, quote multi-word values), `archived:bool`, `has_unread:bool`, `pr_status:\u003cdraft\\|open\\|merged\\|closed\u003e` as repeated or comma-separated values, `source:\u003ccreated_by_me\\|shared_with_me\u003e`, `diff_url:\u003curl\u003e` (quote values containing colons), `pr:\u003cnumber\u003e` (exact PR number match), `repo:\u003cowner/repo\u003e` (case-insensitive substring match against git remote origin or URL), `pr_title:\u003ctext\u003e` (case-insensitive PR title substring), `search:\u003ctext\u003e` (full-text search across chat titles, PR titles, PR numbers, and message bodies; message bodies match English word stems, e.g. `refactor` matches `refactoring`, and ignore English stopwords; titles and PR titles match whole words case-insensitively without stemming; quote multi-word values; cannot be combined with title, pr_title, or pr; a value that tokenizes to no searchable words, e.g. punctuation only, returns an empty list). Bare terms are not supported; use `title:\u003cvalue\u003e` or `search:\u003cvalue\u003e`.", + "name": "q", + "in": "query" + }, + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "multi", + "description": "Filter by label as key:value. Repeat for multiple (AND logic).", + "name": "label", + "in": "query" + }, + { + "type": "string", + "format": "uuid", + "description": "After ID", + "name": "after_id", + "in": "query" + }, + { + "type": "integer", + "description": "Page limit", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "description": "Page offset", + "name": "offset", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.Chat" + } + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + }, + "post": { + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["Chats"], + "summary": "Create chat", + "operationId": "create-chat", + "parameters": [ + { + "description": "Create chat request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.CreateChatRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/codersdk.Chat" + } + }, + "413": { + "description": "Request body exceeds 256 KiB", + "schema": { + "$ref": "#/definitions/codersdk.Response" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/chats/by-workspace": { + "get": { + "produces": ["application/json"], + "tags": ["Chats"], + "summary": "List chats by workspace", + "operationId": "list-chats-by-workspace", + "parameters": [ + { + "type": "string", + "description": "Comma-separated workspace IDs", + "name": "workspace_ids", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/coderd.chatsByWorkspaceResponse" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/chats/config/auto-archive-days": { + "get": { + "produces": ["application/json"], + "tags": ["Chats"], + "summary": "Get chat auto archive days", + "operationId": "get-chat-auto-archive-days", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.ChatAutoArchiveDaysResponse" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + }, + "put": { + "consumes": ["application/json"], + "tags": ["Chats"], + "summary": "Update chat auto archive days", + "operationId": "update-chat-auto-archive-days", + "parameters": [ + { + "description": "Request body", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateChatAutoArchiveDaysRequest" + } + } + ], + "responses": { + "204": { + "description": "No Content" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/chats/config/debug-logging": { + "get": { + "produces": ["application/json"], + "tags": ["Chats"], + "summary": "Get chat debug logging setting", + "operationId": "get-chat-debug-logging-setting", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.ChatDebugLoggingAdminSettings" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + }, + "put": { + "consumes": ["application/json"], + "tags": ["Chats"], + "summary": "Update chat debug logging setting", + "operationId": "update-chat-debug-logging-setting", + "parameters": [ + { + "description": "Request body", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateChatDebugLoggingAllowUsersRequest" + } + } + ], + "responses": { + "204": { + "description": "No Content" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/chats/config/debug-retention-days": { + "get": { + "produces": ["application/json"], + "tags": ["Chats"], + "summary": "Get chat debug retention days", + "operationId": "get-chat-debug-retention-days", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.ChatDebugRetentionDaysResponse" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + }, + "put": { + "consumes": ["application/json"], + "tags": ["Chats"], + "summary": "Update chat debug retention days", + "operationId": "update-chat-debug-retention-days", + "parameters": [ + { + "description": "Request body", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateChatDebugRetentionDaysRequest" + } + } + ], + "responses": { + "204": { + "description": "No Content" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/chats/config/personal-model-overrides": { + "get": { + "produces": ["application/json"], + "tags": ["Chats"], + "summary": "Get chat personal model override settings", + "operationId": "get-chat-personal-model-override-settings", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.ChatPersonalModelOverridesAdminSettings" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + }, + "put": { + "consumes": ["application/json"], + "tags": ["Chats"], + "summary": "Update chat personal model override settings", + "operationId": "update-chat-personal-model-override-settings", + "parameters": [ + { + "description": "Request body", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateChatPersonalModelOverridesAdminSettingsRequest" + } + } + ], + "responses": { + "204": { + "description": "No Content" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/chats/config/plan-mode-instructions": { + "get": { + "produces": ["application/json"], + "tags": ["Chats"], + "summary": "Get chat plan mode instructions", + "operationId": "get-chat-plan-mode-instructions", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.ChatPlanModeInstructionsResponse" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + }, + "put": { + "consumes": ["application/json"], + "tags": ["Chats"], + "summary": "Update chat plan mode instructions", + "operationId": "update-chat-plan-mode-instructions", + "parameters": [ + { + "description": "Request body", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateChatPlanModeInstructionsRequest" + } + } + ], + "responses": { + "204": { + "description": "No Content" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/chats/config/retention-days": { + "get": { + "produces": ["application/json"], + "tags": ["Chats"], + "summary": "Get chat retention days", + "operationId": "get-chat-retention-days", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.ChatRetentionDaysResponse" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + }, + "put": { + "consumes": ["application/json"], + "tags": ["Chats"], + "summary": "Update chat retention days", + "operationId": "update-chat-retention-days", + "parameters": [ + { + "description": "Request body", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateChatRetentionDaysRequest" + } + } + ], + "responses": { + "204": { + "description": "No Content" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + } + }, + "/api/v2/chats/config/system-prompt": { + "get": { + "produces": ["application/json"], + "tags": ["Chats"], + "summary": "Get chat system prompt", + "operationId": "get-chat-system-prompt", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.ChatSystemPromptResponse" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + }, + "put": { + "consumes": ["application/json"], + "tags": ["Chats"], + "summary": "Update chat system prompt", + "operationId": "update-chat-system-prompt", + "parameters": [ + { + "description": "Request body", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateChatSystemPromptRequest" + } + } + ], + "responses": { + "204": { + "description": "No Content" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/chats/config/user-compaction-thresholds": { + "get": { + "produces": ["application/json"], + "tags": ["Chats"], + "summary": "Get user chat compaction thresholds", + "operationId": "get-user-chat-compaction-thresholds", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.UserChatCompactionThresholds" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + } + }, + "/api/v2/chats/config/user-compaction-thresholds/{modelConfig}": { + "put": { + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["Chats"], + "summary": "Update user chat compaction threshold", + "operationId": "update-user-chat-compaction-threshold", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Model config ID", + "name": "modelConfig", + "in": "path", + "required": true + }, + { + "description": "Request body", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateUserChatCompactionThresholdRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.UserChatCompactionThreshold" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + }, + "delete": { + "tags": ["Chats"], + "summary": "Delete user chat compaction threshold", + "operationId": "delete-user-chat-compaction-threshold", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Model config ID", + "name": "modelConfig", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + } + }, + "/api/v2/chats/config/user-debug-logging": { + "get": { + "produces": ["application/json"], + "tags": ["Chats"], + "summary": "Get user chat debug logging setting", + "operationId": "get-user-chat-debug-logging-setting", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.UserChatDebugLoggingSettings" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + }, + "put": { + "consumes": ["application/json"], + "tags": ["Chats"], + "summary": "Update user chat debug logging setting", + "operationId": "update-user-chat-debug-logging-setting", + "parameters": [ + { + "description": "Request body", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateUserChatDebugLoggingRequest" + } + } + ], + "responses": { + "204": { + "description": "No Content" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/chats/config/user-prompt": { + "get": { + "produces": ["application/json"], + "tags": ["Chats"], + "summary": "Get user chat custom prompt", + "operationId": "get-user-chat-custom-prompt", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.UserChatCustomPrompt" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + }, + "put": { + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["Chats"], + "summary": "Update user chat custom prompt", + "operationId": "update-user-chat-custom-prompt", + "parameters": [ + { + "description": "Request body", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UserChatCustomPrompt" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.UserChatCustomPrompt" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/chats/config/workspace-ttl": { + "get": { + "produces": ["application/json"], + "tags": ["Chats"], + "summary": "Get chat workspace time to live", + "operationId": "get-chat-workspace-time-to-live", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.ChatWorkspaceTTLResponse" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + }, + "put": { + "consumes": ["application/json"], + "tags": ["Chats"], + "summary": "Update chat workspace time to live", + "operationId": "update-chat-workspace-time-to-live", + "parameters": [ + { + "description": "Request body", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateChatWorkspaceTTLRequest" + } + } + ], + "responses": { + "204": { + "description": "No Content" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/chats/files": { + "post": { + "consumes": [ + "image/png", + "image/jpeg", + "image/gif", + "image/webp", + "text/plain", + "text/markdown", + "text/csv", + "application/json", + "application/pdf" + ], + "produces": ["application/json"], + "tags": ["Chats"], + "summary": "Upload chat file", + "operationId": "upload-chat-file", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Organization ID", + "name": "organization", + "in": "query", + "required": true + }, + { + "type": "string", + "example": "attachment; filename=\"image.png\"", + "description": "Attachment disposition carrying the file name", + "name": "Content-Disposition", + "in": "header", + "required": true + }, + { + "description": "Raw file binary data", + "name": "request", + "in": "body", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/codersdk.UploadChatFileResponse" + } + }, + "413": { + "description": "Request body exceeds 10 MiB", + "schema": { + "$ref": "#/definitions/codersdk.Response" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "rawBodyFile": "image.png" + } + } + }, + "/api/v2/chats/files/{file}": { + "get": { + "produces": [ + "image/png", + "image/jpeg", + "image/gif", + "image/webp", + "text/plain", + "text/markdown", + "text/csv", + "application/json", + "application/pdf" + ], + "tags": ["Chats"], + "summary": "Get chat file", + "operationId": "get-chat-file", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "File ID", + "name": "file", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/chats/files/{file}/download": { + "get": { + "produces": [ + "image/png", + "image/jpeg", + "image/gif", + "image/webp", + "text/plain", + "text/markdown", + "text/csv", + "application/json", + "application/pdf" + ], + "tags": ["Chats"], + "summary": "Download chat file with signed token", + "operationId": "download-chat-file-with-signed-token", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "File ID", + "name": "file", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Signed download token", + "name": "token", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "x-apidocgen": { + "skip": true + } + } + }, + "/api/v2/chats/files/{file}/download-url": { + "post": { + "produces": ["application/json"], + "tags": ["Chats"], + "summary": "Create chat file download URL", + "operationId": "create-chat-file-download-url", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "File ID", + "name": "file", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.ChatFileDownloadURLResponse" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + } + }, + "/api/v2/chats/watch": { + "get": { + "produces": ["application/json"], + "tags": ["Chats"], + "summary": "Watch chat events for a user via WebSockets", + "operationId": "watch-chat-events-for-a-user-via-websockets", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.ChatWatchEvent" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/chats/{chat}": { + "get": { + "produces": ["application/json"], + "tags": ["Chats"], + "summary": "Get chat by ID", + "operationId": "get-chat-by-id", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.Chat" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + }, + "patch": { + "consumes": ["application/json"], + "tags": ["Chats"], + "summary": "Update chat", + "operationId": "update-chat", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true + }, + { + "description": "Update chat request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateChatRequest" + } + } + ], + "responses": { + "204": { + "description": "No Content" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/chats/{chat}/acl": { + "get": { + "produces": ["application/json"], + "tags": ["Chats"], + "summary": "Get chat ACLs", + "operationId": "get-chat-acls", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.ChatACL" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + }, + "patch": { + "consumes": ["application/json"], + "tags": ["Chats"], + "summary": "Update chat ACL", + "operationId": "update-chat-acl", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true + }, + { + "description": "Update chat ACL request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateChatACL" + } + } + ], + "responses": { + "204": { + "description": "No Content" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + } + }, + "/api/v2/chats/{chat}/compact": { + "post": { + "description": "Requests a manual context compaction on an idle or errored\nchat, clearing any stored error. The compaction runs\nasynchronously through the chat worker and bypasses the\nautomatic usage threshold.", + "produces": ["application/json"], + "tags": ["Chats"], + "summary": "Compact chat", + "operationId": "compact-chat", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.Chat" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + } + }, + "/api/v2/chats/{chat}/context": { + "put": { + "produces": ["application/json"], + "tags": ["Chats"], + "summary": "Refresh chat context", + "operationId": "refresh-chat-context", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.Chat" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/chats/{chat}/cost": { + "get": { + "description": "Cost covers the whole chat tree: the root chat plus every\nsubagent chat beneath it. Requesting cost for a subagent chat\nreturns that same total.\n\nCost is derived from AI Gateway data, which is subject to its\nown retention period, 60 days by default, configured\nindependently of chat retention. Spend for requests older than\nthat period is no longer reported, so a chat whose requests\nhave all been purged reports zero cost.", + "produces": ["application/json"], + "tags": ["Chats"], + "summary": "Get chat cost", + "operationId": "get-chat-cost", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.ChatCost" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/chats/{chat}/diff": { + "get": { + "produces": ["application/json"], + "tags": ["Chats"], + "summary": "Get chat diff contents", + "operationId": "get-chat-diff-contents", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.ChatDiffContents" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/chats/{chat}/interrupt": { + "post": { + "produces": ["application/json"], + "tags": ["Chats"], + "summary": "Interrupt chat", + "operationId": "interrupt-chat", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.Chat" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/chats/{chat}/messages": { + "get": { + "produces": ["application/json"], + "tags": ["Chats"], + "summary": "List chat messages", + "operationId": "list-chat-messages", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "Return messages with id \u003c before_id", + "name": "before_id", + "in": "query" + }, + { + "type": "integer", + "description": "Return messages with id \u003e after_id", + "name": "after_id", + "in": "query" + }, + { + "type": "integer", + "description": "Page size, 1 to 200. Defaults to 50.", + "name": "limit", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.ChatMessagesResponse" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + }, + "post": { + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["Chats"], + "summary": "Send chat message", + "operationId": "send-chat-message", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true + }, + { + "description": "Create chat message request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.CreateChatMessageRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.CreateChatMessageResponse" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/chats/{chat}/messages/{message}": { + "patch": { + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["Chats"], + "summary": "Edit chat message", + "operationId": "edit-chat-message", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "Message ID", + "name": "message", + "in": "path", + "required": true + }, + { + "description": "Edit chat message request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.EditChatMessageRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.EditChatMessageResponse" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/chats/{chat}/prompts": { + "get": { + "description": "Returns the user-authored prompts in a chat, newest first,\nwith each prompt's text parts concatenated in the order they\nwere authored. Used by the composer to power the up/down\narrow prompt-history cycle without paging through every\nmessage in the chat.", + "produces": ["application/json"], + "tags": ["Chats"], + "summary": "List chat user prompts", + "operationId": "list-chat-user-prompts", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "Page size, 0 to 2000. 0 (the default) means the server-side default of 500.", + "name": "limit", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.ChatPromptsResponse" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/chats/{chat}/queue/{queuedMessage}": { + "delete": { + "tags": ["Chats"], + "summary": "Delete chat queued message", + "operationId": "delete-chat-queued-message", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "Queued message ID", + "name": "queuedMessage", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/chats/{chat}/queue/{queuedMessage}/promote": { + "post": { + "produces": ["application/json"], + "tags": ["Chats"], + "summary": "Promote chat queued message", + "operationId": "promote-chat-queued-message", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "Queued message ID", + "name": "queuedMessage", + "in": "path", + "required": true + } + ], + "responses": { + "202": { + "description": "Accepted", + "schema": { + "$ref": "#/definitions/codersdk.Response" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/chats/{chat}/reconcile-invalid": { + "post": { + "produces": ["application/json"], + "tags": ["Chats"], + "summary": "Reconcile invalid chat state", + "operationId": "reconcile-invalid-chat-state", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.Chat" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/chats/{chat}/stream": { + "get": { + "produces": ["application/json"], + "tags": ["Chats"], + "summary": "Stream chat events via WebSockets", + "operationId": "stream-chat-events-via-websockets", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "Skip snapshot messages with id at or before this cursor", + "name": "after_id", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.ChatStreamEvent" + } + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/chats/{chat}/stream/git": { + "get": { + "produces": ["application/json"], + "tags": ["Chats"], + "summary": "Watch chat workspace git state via WebSockets", + "operationId": "watch-chat-workspace-git-state-via-websockets", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.WorkspaceAgentGitServerMessage" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/chats/{chat}/stream/parts": { + "get": { + "produces": ["application/json"], + "tags": ["Chats"], + "summary": "Stream chat parts via WebSockets", + "operationId": "stream-chat-parts-via-websockets", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.ChatStreamEvent" + } + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + } + }, + "/api/v2/chats/{chat}/title/propose": { + "post": { + "produces": ["application/json"], + "tags": ["Chats"], + "summary": "Propose chat title", + "operationId": "propose-chat-title", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.ProposeChatTitleResponse" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/chats/{chat}/tool-results": { + "post": { + "consumes": ["application/json"], + "tags": ["Chats"], + "summary": "Submit chat tool results", + "operationId": "submit-chat-tool-results", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true + }, + { + "description": "Request body", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.SubmitToolResultsRequest" + } + } + ], + "responses": { + "204": { + "description": "No Content" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, "/api/v2/connectionlog": { "get": { "produces": ["application/json"], @@ -4581,6 +4451,40 @@ ] } }, + "/api/v2/mcp/servers/{mcpServer}/oauth2/disconnect": { + "delete": { + "produces": ["application/json"], + "tags": ["MCP"], + "summary": "Disconnect MCP server OAuth2 token", + "operationId": "disconnect-mcp-server-oauth2-token", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "MCP server config ID", + "name": "mcpServer", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.MCPServerOAuth2DisconnectResponse" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + } + }, "/api/v2/notifications/custom": { "post": { "consumes": ["application/json"], @@ -5457,6 +5361,381 @@ ] } }, + "/api/v2/organizations/{organization}/chats/model-overrides": { + "get": { + "produces": ["application/json"], + "tags": ["Chats"], + "summary": "List organization chat model overrides", + "operationId": "list-organization-chat-model-overrides", + "parameters": [ + { + "type": "string", + "description": "Organization name or ID", + "name": "organization", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.ChatModelOverridesResponse" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + } + }, + "/api/v2/organizations/{organization}/chats/model-overrides/{context}": { + "put": { + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["Chats"], + "summary": "Update organization chat model override", + "operationId": "update-organization-chat-model-override", + "parameters": [ + { + "type": "string", + "description": "Organization name or ID", + "name": "organization", + "in": "path", + "required": true + }, + { + "enum": [ + "general", + "explore", + "title_generation", + "compaction", + "advisor" + ], + "type": "string", + "description": "Override context", + "name": "context", + "in": "path", + "required": true + }, + { + "description": "Model override", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateChatModelOverrideRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.ChatModelOverrideResponse" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + } + }, + "/api/v2/organizations/{organization}/chats/models": { + "get": { + "produces": ["application/json"], + "tags": ["Chats"], + "summary": "List AI models and provider descriptors in an organization", + "operationId": "list-ai-models-and-provider-descriptors-in-an-organization", + "parameters": [ + { + "type": "string", + "description": "Organization name or ID", + "name": "organization", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.OrganizationChatModelsResponse" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + }, + "post": { + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["Chats"], + "summary": "Create an AI model in an organization", + "operationId": "create-an-ai-model-in-an-organization", + "parameters": [ + { + "type": "string", + "description": "Organization name or ID", + "name": "organization", + "in": "path", + "required": true + }, + { + "description": "Model", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.CreateChatModelRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/codersdk.ChatModel" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + } + }, + "/api/v2/organizations/{organization}/chats/models/{model}": { + "get": { + "produces": ["application/json"], + "tags": ["Chats"], + "summary": "Get an AI model", + "operationId": "get-an-ai-model", + "parameters": [ + { + "type": "string", + "description": "Organization name or ID", + "name": "organization", + "in": "path", + "required": true + }, + { + "type": "string", + "format": "uuid", + "description": "Model ID", + "name": "model", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.ChatModel" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + }, + "delete": { + "tags": ["Chats"], + "summary": "Delete an AI model", + "operationId": "delete-an-ai-model", + "parameters": [ + { + "type": "string", + "description": "Organization name or ID", + "name": "organization", + "in": "path", + "required": true + }, + { + "type": "string", + "format": "uuid", + "description": "Model ID", + "name": "model", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + }, + "patch": { + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["Chats"], + "summary": "Update an AI model", + "operationId": "update-an-ai-model", + "parameters": [ + { + "type": "string", + "description": "Organization name or ID", + "name": "organization", + "in": "path", + "required": true + }, + { + "type": "string", + "format": "uuid", + "description": "Model ID", + "name": "model", + "in": "path", + "required": true + }, + { + "description": "Model updates", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateChatModelRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.ChatModel" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + } + }, + "/api/v2/organizations/{organization}/chats/models/{model}/acl": { + "get": { + "produces": ["application/json"], + "tags": ["Chats"], + "summary": "Get an AI model ACL", + "operationId": "get-an-ai-model-acl", + "parameters": [ + { + "type": "string", + "description": "Organization name or ID", + "name": "organization", + "in": "path", + "required": true + }, + { + "type": "string", + "format": "uuid", + "description": "Model ID", + "name": "model", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.ChatModelACL" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + }, + "patch": { + "consumes": ["application/json"], + "tags": ["Chats"], + "summary": "Update an AI model ACL", + "operationId": "update-an-ai-model-acl", + "parameters": [ + { + "type": "string", + "description": "Organization name or ID", + "name": "organization", + "in": "path", + "required": true + }, + { + "type": "string", + "format": "uuid", + "description": "Model ID", + "name": "model", + "in": "path", + "required": true + }, + { + "description": "Sparse model ACL update", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateChatModelACLRequest" + } + } + ], + "responses": { + "204": { + "description": "No Content" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + } + }, "/api/v2/organizations/{organization}/groups": { "get": { "produces": ["application/json"], @@ -5715,6 +5994,331 @@ ] } }, + "/api/v2/organizations/{organization}/mcp-servers": { + "get": { + "produces": ["application/json"], + "tags": ["MCP"], + "summary": "List MCP server configs", + "operationId": "list-mcp-server-configs", + "parameters": [ + { + "type": "string", + "description": "Organization name or ID", + "name": "organization", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.MCPServerConfig" + } + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + }, + "post": { + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["MCP"], + "summary": "Create MCP server config", + "operationId": "create-mcp-server-config", + "parameters": [ + { + "type": "string", + "description": "Organization name or ID", + "name": "organization", + "in": "path", + "required": true + }, + { + "description": "Create MCP server config request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.CreateMCPServerConfigRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/codersdk.MCPServerConfig" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + } + }, + "/api/v2/organizations/{organization}/mcp-servers/{mcpserverconfig}": { + "get": { + "produces": ["application/json"], + "tags": ["MCP"], + "summary": "Get MCP server config", + "operationId": "get-mcp-server-config", + "parameters": [ + { + "type": "string", + "description": "Organization name or ID", + "name": "organization", + "in": "path", + "required": true + }, + { + "type": "string", + "format": "uuid", + "description": "MCP server config ID", + "name": "mcpserverconfig", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.MCPServerConfig" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + }, + "delete": { + "tags": ["MCP"], + "summary": "Delete MCP server config", + "operationId": "delete-mcp-server-config", + "parameters": [ + { + "type": "string", + "description": "Organization name or ID", + "name": "organization", + "in": "path", + "required": true + }, + { + "type": "string", + "format": "uuid", + "description": "MCP server config ID", + "name": "mcpserverconfig", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + }, + "patch": { + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["MCP"], + "summary": "Update MCP server config", + "operationId": "update-mcp-server-config", + "parameters": [ + { + "type": "string", + "description": "Organization name or ID", + "name": "organization", + "in": "path", + "required": true + }, + { + "type": "string", + "format": "uuid", + "description": "MCP server config ID", + "name": "mcpserverconfig", + "in": "path", + "required": true + }, + { + "description": "Update MCP server config request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateMCPServerConfigRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.MCPServerConfig" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + } + }, + "/api/v2/organizations/{organization}/mcp-servers/{mcpserverconfig}/acl": { + "get": { + "produces": ["application/json"], + "tags": ["MCP"], + "summary": "Get MCP server config ACL", + "operationId": "get-mcp-server-config-acl", + "parameters": [ + { + "type": "string", + "description": "Organization name or ID", + "name": "organization", + "in": "path", + "required": true + }, + { + "type": "string", + "format": "uuid", + "description": "MCP server config ID", + "name": "mcpserverconfig", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.MCPServerConfigACL" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + }, + "patch": { + "consumes": ["application/json"], + "tags": ["MCP"], + "summary": "Update MCP server config ACL", + "operationId": "update-mcp-server-config-acl", + "parameters": [ + { + "type": "string", + "description": "Organization name or ID", + "name": "organization", + "in": "path", + "required": true + }, + { + "type": "string", + "format": "uuid", + "description": "MCP server config ID", + "name": "mcpserverconfig", + "in": "path", + "required": true + }, + { + "description": "Update MCP server config ACL request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateMCPServerConfigACLRequest" + } + } + ], + "responses": { + "204": { + "description": "No Content" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + } + }, + "/api/v2/organizations/{organization}/mcp-servers/{mcpserverconfig}/oauth2/connect": { + "get": { + "tags": ["MCP"], + "summary": "Initiate MCP server OAuth2 connect", + "operationId": "initiate-mcp-server-oauth2-connect", + "parameters": [ + { + "type": "string", + "description": "Organization name or ID", + "name": "organization", + "in": "path", + "required": true + }, + { + "type": "string", + "format": "uuid", + "description": "MCP server config ID", + "name": "mcpserverconfig", + "in": "path", + "required": true + } + ], + "responses": { + "307": { + "description": "Temporary Redirect" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + } + }, "/api/v2/organizations/{organization}/members": { "get": { "produces": ["application/json"], @@ -6011,6 +6615,100 @@ ] } }, + "/api/v2/organizations/{organization}/members/{user}/chats/model-overrides": { + "get": { + "produces": ["application/json"], + "tags": ["Chats"], + "summary": "Get organization member chat model overrides", + "operationId": "get-organization-member-chat-model-overrides", + "parameters": [ + { + "type": "string", + "description": "Organization name or ID", + "name": "organization", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "User name, ID, or me", + "name": "user", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.UserChatPersonalModelOverridesResponse" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + } + }, + "/api/v2/organizations/{organization}/members/{user}/chats/model-overrides/{context}": { + "put": { + "consumes": ["application/json"], + "tags": ["Chats"], + "summary": "Update organization member chat model override", + "operationId": "update-organization-member-chat-model-override", + "parameters": [ + { + "type": "string", + "description": "Organization name or ID", + "name": "organization", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "User name, ID, or me", + "name": "user", + "in": "path", + "required": true + }, + { + "enum": ["root", "general", "explore"], + "type": "string", + "description": "Override context", + "name": "context", + "in": "path", + "required": true + }, + { + "description": "Personal model override", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateUserChatPersonalModelOverrideRequest" + } + } + ], + "responses": { + "204": { + "description": "No Content" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + } + }, "/api/v2/organizations/{organization}/members/{user}/roles": { "put": { "consumes": ["application/json"], @@ -9646,6 +10344,119 @@ ] } }, + "/api/v2/users/{user}/ai-provider-keys": { + "get": { + "produces": ["application/json"], + "tags": ["Chats"], + "summary": "List user AI provider key configurations", + "operationId": "list-user-ai-provider-key-configurations", + "parameters": [ + { + "type": "string", + "description": "User ID, username, or me", + "name": "user", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.UserAIProviderKeyConfig" + } + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/users/{user}/ai-provider-keys/{aiProvider}": { + "put": { + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["Chats"], + "summary": "Update user AI provider key", + "operationId": "update-user-ai-provider-key", + "parameters": [ + { + "type": "string", + "description": "User ID, username, or me", + "name": "user", + "in": "path", + "required": true + }, + { + "type": "string", + "format": "uuid", + "description": "AI provider ID", + "name": "aiProvider", + "in": "path", + "required": true + }, + { + "description": "Request body", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.CreateUserAIProviderKeyRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.UserAIProviderKeyConfig" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + }, + "delete": { + "tags": ["Chats"], + "summary": "Delete user AI provider key", + "operationId": "delete-user-ai-provider-key", + "parameters": [ + { + "type": "string", + "description": "User ID, username, or me", + "name": "user", + "in": "path", + "required": true + }, + { + "type": "string", + "format": "uuid", + "description": "AI provider ID", + "name": "aiProvider", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, "/api/v2/users/{user}/ai/budget/override": { "get": { "produces": ["application/json"], @@ -14354,6 +15165,12 @@ "enum": ["prebuild_claimed"], "x-enum-varnames": ["ReinitializeReasonPrebuildClaimed"] }, + "coderd.chatsByWorkspaceResponse": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, "coderd.cspViolation": { "type": "object", "properties": { @@ -15139,6 +15956,33 @@ } } }, + "codersdk.AIProviderSummary": { + "type": "object", + "properties": { + "deleted": { + "type": "boolean" + }, + "display_name": { + "type": "string" + }, + "enabled": { + "type": "boolean" + }, + "icon": { + "type": "string" + }, + "id": { + "type": "string", + "format": "uuid" + }, + "name": { + "type": "string" + }, + "type": { + "$ref": "#/definitions/codersdk.AIProviderType" + } + } + }, "codersdk.AIProviderType": { "type": "string", "enum": [ @@ -16521,6 +17365,14 @@ } } }, + "codersdk.ChatAutoArchiveDaysResponse": { + "type": "object", + "properties": { + "auto_archive_days": { + "type": "integer" + } + } + }, "codersdk.ChatBusyBehavior": { "type": "string", "enum": ["queue", "interrupt"], @@ -16676,6 +17528,25 @@ } } }, + "codersdk.ChatDebugLoggingAdminSettings": { + "type": "object", + "properties": { + "allow_users": { + "type": "boolean" + }, + "forced_by_deployment": { + "type": "boolean" + } + } + }, + "codersdk.ChatDebugRetentionDaysResponse": { + "type": "object", + "properties": { + "debug_retention_days": { + "type": "integer" + } + } + }, "codersdk.ChatDiffContents": { "type": "object", "properties": { @@ -17877,11 +18748,27 @@ "ChatPersonalModelOverrideModeModel" ] }, + "codersdk.ChatPersonalModelOverridesAdminSettings": { + "type": "object", + "properties": { + "allow_users": { + "type": "boolean" + } + } + }, "codersdk.ChatPlanMode": { "type": "string", "enum": ["plan"], "x-enum-varnames": ["ChatPlanModePlan"] }, + "codersdk.ChatPlanModeInstructionsResponse": { + "type": "object", + "properties": { + "plan_mode_instructions": { + "type": "string" + } + } + }, "codersdk.ChatPrompt": { "type": "object", "properties": { @@ -18112,6 +18999,20 @@ } } }, + "codersdk.ChatSystemPromptResponse": { + "type": "object", + "properties": { + "default_system_prompt": { + "type": "string" + }, + "include_default_system_prompt": { + "type": "boolean" + }, + "system_prompt": { + "type": "string" + } + } + }, "codersdk.ChatUnsupportedProvider": { "type": "object", "properties": { @@ -18194,6 +19095,15 @@ "ChatWatchEventKindContextDirty" ] }, + "codersdk.ChatWorkspaceTTLResponse": { + "type": "object", + "properties": { + "workspace_ttl_ms": { + "description": "WorkspaceTTLMillis is the workspace TTL in milliseconds.\nZero means disabled; the template's own autostop setting applies.", + "type": "integer" + } + } + }, "codersdk.ClusterConfig": { "type": "object", "properties": { @@ -19148,6 +20058,14 @@ } } }, + "codersdk.CreateUserAIProviderKeyRequest": { + "type": "object", + "properties": { + "api_key": { + "type": "string" + } + } + }, "codersdk.CreateUserRequestWithOrgs": { "type": "object", "required": ["username"], @@ -24303,6 +25221,17 @@ } } }, + "codersdk.SubmitToolResultsRequest": { + "type": "object", + "properties": { + "results": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.ToolResult" + } + } + } + }, "codersdk.SupportConfig": { "type": "object", "properties": { @@ -25474,6 +26403,23 @@ } } }, + "codersdk.ToolResult": { + "type": "object", + "properties": { + "is_error": { + "type": "boolean" + }, + "output": { + "type": "array", + "items": { + "type": "integer" + } + }, + "tool_call_id": { + "type": "string" + } + } + }, "codersdk.TraceConfig": { "type": "object", "properties": { @@ -25582,6 +26528,30 @@ } } }, + "codersdk.UpdateChatAutoArchiveDaysRequest": { + "type": "object", + "properties": { + "auto_archive_days": { + "type": "integer" + } + } + }, + "codersdk.UpdateChatDebugLoggingAllowUsersRequest": { + "type": "object", + "properties": { + "allow_users": { + "type": "boolean" + } + } + }, + "codersdk.UpdateChatDebugRetentionDaysRequest": { + "type": "object", + "properties": { + "debug_retention_days": { + "type": "integer" + } + } + }, "codersdk.UpdateChatModelACLRequest": { "type": "object", "properties": { @@ -25640,6 +26610,22 @@ } } }, + "codersdk.UpdateChatPersonalModelOverridesAdminSettingsRequest": { + "type": "object", + "properties": { + "allow_users": { + "type": "boolean" + } + } + }, + "codersdk.UpdateChatPlanModeInstructionsRequest": { + "type": "object", + "properties": { + "plan_mode_instructions": { + "type": "string" + } + } + }, "codersdk.UpdateChatRequest": { "type": "object", "properties": { @@ -25681,6 +26667,26 @@ } } }, + "codersdk.UpdateChatSystemPromptRequest": { + "type": "object", + "properties": { + "include_default_system_prompt": { + "type": "boolean" + }, + "system_prompt": { + "type": "string" + } + } + }, + "codersdk.UpdateChatWorkspaceTTLRequest": { + "type": "object", + "properties": { + "workspace_ttl_ms": { + "description": "WorkspaceTTLMillis is the workspace TTL in milliseconds.\nZero means disabled; the template's own autostop setting applies.", + "type": "integer" + } + } + }, "codersdk.UpdateCheckResponse": { "type": "object", "properties": { @@ -26009,6 +27015,24 @@ } } }, + "codersdk.UpdateUserChatCompactionThresholdRequest": { + "type": "object", + "properties": { + "threshold_percent": { + "type": "integer", + "maximum": 100, + "minimum": 0 + } + } + }, + "codersdk.UpdateUserChatDebugLoggingRequest": { + "type": "object", + "properties": { + "debug_logging_enabled": { + "type": "boolean" + } + } + }, "codersdk.UpdateUserChatPersonalModelOverrideRequest": { "type": "object", "properties": { @@ -26423,6 +27447,23 @@ } } }, + "codersdk.UserAIProviderKeyConfig": { + "type": "object", + "properties": { + "byok_enabled": { + "type": "boolean" + }, + "has_provider_api_key": { + "type": "boolean" + }, + "has_user_api_key": { + "type": "boolean" + }, + "provider": { + "$ref": "#/definitions/codersdk.AIProviderSummary" + } + } + }, "codersdk.UserAISpendStatus": { "type": "object", "properties": { @@ -26543,6 +27584,51 @@ } } }, + "codersdk.UserChatCompactionThreshold": { + "type": "object", + "properties": { + "model_config_id": { + "type": "string", + "format": "uuid" + }, + "threshold_percent": { + "type": "integer" + } + } + }, + "codersdk.UserChatCompactionThresholds": { + "type": "object", + "properties": { + "thresholds": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.UserChatCompactionThreshold" + } + } + } + }, + "codersdk.UserChatCustomPrompt": { + "type": "object", + "properties": { + "custom_prompt": { + "type": "string" + } + } + }, + "codersdk.UserChatDebugLoggingSettings": { + "type": "object", + "properties": { + "debug_logging_enabled": { + "type": "boolean" + }, + "forced_by_deployment": { + "type": "boolean" + }, + "user_toggle_allowed": { + "type": "boolean" + } + } + }, "codersdk.UserChatPersonalModelOverridesResponse": { "type": "object", "properties": { diff --git a/coderd/chat_routes.go b/coderd/chat_routes.go new file mode 100644 index 0000000000..70a942fc3a --- /dev/null +++ b/coderd/chat_routes.go @@ -0,0 +1,274 @@ +package coderd + +import ( + "net/http" + "time" + + "github.com/go-chi/chi/v5" + + "github.com/coder/coder/v2/coderd/httpapi" + "github.com/coder/coder/v2/coderd/httpmw" + "github.com/coder/coder/v2/coderd/rbac/policy" + "github.com/coder/coder/v2/codersdk" +) + +// chatFilesRateLimitMW returns the middleware enforcing FilesRateLimit +// on chat file routes. Both API prefixes mount the same instance, and +// the limiter keys on a prefix-stripped endpoint, so alternating +// prefixes cannot double the budget. +func (api *API) chatFilesRateLimitMW() func(http.Handler) http.Handler { + api.chatFilesRateLimitOnce.Do(func() { + api.chatFilesRateLimit = httpmw.RateLimitByAPICompatibilityEndpoint(api.FilesRateLimit, time.Minute) + }) + return api.chatFilesRateLimit +} + +// chatAPIPrefix identifies which API prefix a chat route mount serves. +type chatAPIPrefix int + +const ( + chatAPIPrefixV2 chatAPIPrefix = iota + chatAPIPrefixExperimental +) + +// injectDefaultOrganizationParam lets the legacy default-organization +// routes reuse the organization-scoped handlers. +func injectDefaultOrganizationParam(next http.Handler) http.Handler { + return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + chi.RouteContext(req.Context()).URLParams.Add("organization", codersdk.DefaultOrganization) + next.ServeHTTP(rw, req) + }) +} + +// registerChatAPIRoutes mounts the chat API surface on r, the root router +// of an API prefix. /api/v2 and /api/experimental serve the same promoted +// routes during the CODAGT-921 compatibility window. The experimental +// mount also serves the routes that were not promoted, while /api/v2 +// reserves their path segments so they return 404 instead of falling into +// the {chat} wildcard. +// TODO(CODAGT-921): unmount from /api/experimental after the transition +// window (tracked in CODAGT-922). +func (api *API) registerChatAPIRoutes(r chi.Router, apiKeyMiddleware func(http.Handler) http.Handler, prefix chatAPIPrefix) { + experimental := prefix == chatAPIPrefixExperimental + // Signed URL tokens authenticate downloads, so the route stays + // outside the API key middleware. + r.Group(func(r chi.Router) { + r.Use(api.chatFilesRateLimitMW()) + r.Get("/chats/files/{file}/download", api.downloadChatFile) + }) + if experimental { + // Superseded by the organization-scoped models collection and + // deliberately not promoted. Keep until the frontend uses the + // organization-scoped routes. + r.Route("/chats/model-configs", func(r chi.Router) { + r.Use( + apiKeyMiddleware, + injectDefaultOrganizationParam, + httpmw.ExtractOrganizationParam(api.Database), + ) + r.Get("/", api.listDefaultOrganizationChatModelConfigs) + r.Post("/", api.createChatModelConfig) + }) + } + r.Route("/chats", func(r chi.Router) { + r.Use(apiKeyMiddleware) + if experimental { + // Superseded by the organization-scoped models route and + // deliberately not promoted. Keep until the frontend uses + // the organization-scoped routes. + r.With( + injectDefaultOrganizationParam, + httpmw.ExtractOrganizationParam(api.Database), + ).Get("/models", api.listChatModelConfigsByOrganization) + // TODO(cian): place under /api/experimental/chats/config + r.Route("/providers", func(r chi.Router) { + r.Get("/", api.listChatProviders) + r.Post("/", api.createChatProvider) + r.Route("/{providerConfig}", func(r chi.Router) { + r.Patch("/", api.updateChatProvider) + r.Delete("/", api.deleteChatProvider) + }) + }) + r.Route("/user-provider-configs", func(r chi.Router) { + r.Get("/", api.listUserChatProviderConfigs) + r.Route("/{providerConfig}", func(r chi.Router) { + r.Put("/", api.upsertUserChatProviderKey) + r.Delete("/", api.deleteUserChatProviderKey) + }) + }) + } else { + // These segments exist only under /api/experimental. Reserve + // them with empty subrouters so they return 404 instead of + // falling into the {chat} wildcard and failing UUID parsing + // with a 400. + // TODO(CODAGT-922): drop the reservations with the + // experimental mounts. + for _, segment := range []string{"/models", "/model-configs", "/providers", "/user-provider-configs"} { + r.Route(segment, func(r chi.Router) { + r.NotFound(func(rw http.ResponseWriter, _ *http.Request) { + httpapi.RouteNotFound(rw) + }) + }) + } + } + r.Get("/by-workspace", api.chatsByWorkspace) + r.Get("/", api.listChats) + r.Post("/", api.postChats) + r.Get("/watch", api.watchChats) + r.Route("/files", func(r chi.Router) { + r.Use(api.chatFilesRateLimitMW()) + r.Post("/", api.postChatFile) + r.Post("/{file}/download-url", api.postChatFileDownloadURL) + r.Get("/{file}", api.chatFileByID) + }) + r.Route("/config", func(r chi.Router) { + r.Get("/system-prompt", api.getChatSystemPrompt) + r.Put("/system-prompt", api.putChatSystemPrompt) + r.Get("/plan-mode-instructions", api.getChatPlanModeInstructions) + r.Put("/plan-mode-instructions", api.putChatPlanModeInstructions) + r.Get("/personal-model-overrides", api.getChatPersonalModelOverridesAdminSettings) + r.Put("/personal-model-overrides", api.putChatPersonalModelOverridesAdminSettings) + r.Get("/debug-logging", api.getChatDebugLogging) + r.Put("/debug-logging", api.putChatDebugLogging) + r.Get("/user-debug-logging", api.getUserChatDebugLogging) + r.Put("/user-debug-logging", api.putUserChatDebugLogging) + r.Get("/user-prompt", api.getUserChatCustomPrompt) + r.Put("/user-prompt", api.putUserChatCustomPrompt) + r.Get("/user-compaction-thresholds", api.getUserChatCompactionThresholds) + r.Put("/user-compaction-thresholds/{modelConfig}", api.putUserChatCompactionThreshold) + r.Delete("/user-compaction-thresholds/{modelConfig}", api.deleteUserChatCompactionThreshold) + r.Get("/workspace-ttl", api.getChatWorkspaceTTL) + r.Put("/workspace-ttl", api.putChatWorkspaceTTL) + r.Get("/retention-days", api.getChatRetentionDays) + r.Put("/retention-days", api.putChatRetentionDays) + r.Get("/debug-retention-days", api.getChatDebugRetentionDays) + r.Put("/debug-retention-days", api.putChatDebugRetentionDays) + r.Get("/auto-archive-days", api.getChatAutoArchiveDays) + r.Put("/auto-archive-days", api.putChatAutoArchiveDays) + if experimental { + r.Group(func(r chi.Router) { + r.Use(httpmw.RequireExperimentWithDevBypass(api.Experiments, codersdk.ExperimentChatVirtualDesktop)) + r.Get("/computer-use-provider", api.getChatComputerUseProvider) + r.Put("/computer-use-provider", api.putChatComputerUseProvider) + }) + r.Group(func(r chi.Router) { + r.Use(httpmw.RequireExperimentWithDevBypass(api.Experiments, codersdk.ExperimentChatAdvisor)) + r.Get("/advisor", api.getChatAdvisorConfig) + r.Put("/advisor", api.putChatAdvisorConfig) + }) + } + }) + r.Route("/{chat}", func(r chi.Router) { + r.Use(httpmw.ExtractChatParam(api.Database)) + r.Route("/acl", func(r chi.Router) { + r.Get("/", api.getChatACL) + r.Patch("/", api.patchChatACL) + }) + r.Get("/", api.getChat) + r.Patch("/", api.patchChat) + r.Get("/cost", api.getChatCost) + r.Get("/messages", api.getChatMessages) + r.Post("/messages", api.postChatMessages) + r.Patch("/messages/{message}", api.patchChatMessage) + r.Get("/prompts", api.getChatUserPrompts) + r.Post("/interrupt", api.interruptChat) + r.Post("/compact", api.compactChat) + r.Post("/reconcile-invalid", api.reconcileInvalidChatState) + r.Post("/tool-results", api.postChatToolResults) + r.Post("/title/propose", api.proposeChatTitle) + r.Get("/diff", api.getChatDiffContents) + r.Put("/context", api.refreshChatContext) + r.Route("/queue/{queuedMessage}", func(r chi.Router) { + r.Delete("/", api.deleteChatQueuedMessage) + r.Post("/promote", api.promoteChatQueuedMessage) + }) + r.Route("/stream", func(r chi.Router) { + r.Get("/", api.streamChat) + r.Get("/parts", api.streamChatParts) + r.Get("/git", api.watchChatGit) + if experimental { + r.Get("/desktop", api.watchChatDesktop) + } + }) + if experimental { + r.Route("/debug", func(r chi.Router) { + r.Get("/runs", api.getChatDebugRuns) + r.Get("/runs/{debugRun}", api.getChatDebugRun) + }) + } + }) + }) +} + +// registerMCPServerOAuth2Routes mounts the user-scoped MCP server OAuth2 +// routes shared by both API prefixes. +func (api *API) registerMCPServerOAuth2Routes(r chi.Router, prefix chatAPIPrefix) { + if prefix == chatAPIPrefixExperimental { + // Providers pin the redirect URI when a session is established, + // so the callback URL cannot change for existing sessions without + // breaking token refresh and forcing a re-auth. + // TODO(CODAGT-922): promote once existing sessions can be + // re-authenticated against a /api/v2 callback. + r.Get("/servers/{mcpServer}/oauth2/callback", api.mcpServerOAuth2Callback) + } + // Disconnect stays outside organization routes so former organization + // members can delete their stored token after losing config read access. + r.Delete("/servers/{mcpServer}/oauth2/disconnect", api.mcpServerOAuth2Disconnect) +} + +func (api *API) registerUserAIProviderKeyRoutes(r chi.Router) { + r.Get("/", api.listUserAIProviderKeyConfigs) + r.Route("/{aiProvider}", func(r chi.Router) { + r.Put("/", api.upsertUserAIProviderKey) + r.Delete("/", api.deleteUserAIProviderKey) + }) +} + +// registerOrganizationChatRoutes mounts the organization-scoped chat and +// MCP server configuration routes; r must already extract the +// organization parameter. +func (api *API) registerOrganizationChatRoutes(r chi.Router) { + r.Route("/mcp-servers", func(r chi.Router) { + r.Get("/", api.listMCPServerConfigs) + r.Post("/", api.createMCPServerConfig) + r.Route("/{mcpserverconfig}", func(r chi.Router) { + r.With(httpmw.ExtractMCPServerConfigParam(api.Database, api.HTTPAuth.Authorize, + policy.ActionRead, policy.ActionUpdate, policy.ActionDelete)).Get("/", api.getMCPServerConfig) + r.With(httpmw.ExtractMCPServerConfigParam(api.Database, api.HTTPAuth.Authorize, + policy.ActionUpdate)).Patch("/", api.updateMCPServerConfig) + r.With(httpmw.ExtractMCPServerConfigParam(api.Database, api.HTTPAuth.Authorize, + policy.ActionDelete)).Delete("/", api.deleteMCPServerConfig) + r.With(httpmw.ExtractMCPServerConfigParam(api.Database, api.HTTPAuth.Authorize, + policy.ActionShare)).Get("/acl", api.mcpServerConfigACL) + r.With(httpmw.ExtractMCPServerConfigParam(api.Database, api.HTTPAuth.Authorize, + policy.ActionShare)).Patch("/acl", api.patchMCPServerConfigACL) + r.With(httpmw.ExtractMCPServerConfigParam(api.Database, api.HTTPAuth.Authorize, + policy.ActionRead)).Get("/oauth2/connect", api.mcpServerOAuth2Connect) + }) + }) + r.Route("/chats/model-overrides", func(r chi.Router) { + r.Get("/", api.getOrganizationChatModelOverrides) + r.Put("/{context}", api.putOrganizationChatModelOverride) + }) + r.Route("/chats/models", func(r chi.Router) { + r.Get("/", api.listChatModelConfigsByOrganization) + r.Post("/", api.createChatModelConfig) + r.Route("/{model}", func(r chi.Router) { + r.Use(httpmw.ExtractChatModelConfigParam(api.Database)) + r.Get("/", api.getChatModelConfig) + r.Patch("/", api.updateChatModelConfig) + r.Delete("/", api.deleteChatModelConfig) + r.Route("/acl", func(r chi.Router) { + r.Get("/", api.chatModelConfigACLHandler) + r.Patch("/", api.updateChatModelConfigACL) + }) + }) + }) +} + +func (api *API) registerOrganizationMemberChatRoutes(r chi.Router) { + r.Route("/chats/model-overrides", func(r chi.Router) { + r.Get("/", api.getUserChatPersonalModelOverrides) + r.Put("/{context}", api.putUserChatPersonalModelOverride) + }) +} diff --git a/coderd/chat_routes_internal_test.go b/coderd/chat_routes_internal_test.go new file mode 100644 index 0000000000..2522911f9f --- /dev/null +++ b/coderd/chat_routes_internal_test.go @@ -0,0 +1,36 @@ +package coderd + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestChatFilesRateLimitMWCompatibilityAliases(t *testing.T) { + t.Parallel() + + api := &API{Options: &Options{FilesRateLimit: 1}} + handler := api.chatFilesRateLimitMW()(http.HandlerFunc(func(rw http.ResponseWriter, _ *http.Request) { + rw.WriteHeader(http.StatusOK) + })) + + for i, requestPath := range []string{ + "/api/v2/chats/files/00000000-0000-0000-0000-000000000000", + "/api/experimental/chats/files/00000000-0000-0000-0000-000000000000", + } { + req := httptest.NewRequest(http.MethodGet, requestPath, nil) + req.RemoteAddr = "192.0.2.1:1234" + rec := httptest.NewRecorder() + handler.ServeHTTP(rec, req) + resp := rec.Result() + _ = resp.Body.Close() + + expectedStatus := http.StatusOK + if i > 0 { + expectedStatus = http.StatusTooManyRequests + } + require.Equal(t, expectedStatus, resp.StatusCode, requestPath) + } +} diff --git a/coderd/chat_routes_test.go b/coderd/chat_routes_test.go new file mode 100644 index 0000000000..5b54c30788 --- /dev/null +++ b/coderd/chat_routes_test.go @@ -0,0 +1,65 @@ +package coderd_test + +import ( + "fmt" + "net/http" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/coder/coder/v2/coderd/coderdtest" + "github.com/coder/coder/v2/coderd/database" + "github.com/coder/coder/v2/coderd/database/dbgen" + "github.com/coder/coder/v2/testutil" +) + +func TestChatRoutesCompatibility(t *testing.T) { + t.Parallel() + + ctx := testutil.Context(t, testutil.WaitLong) + client, db := coderdtest.NewWithDatabase(t, nil) + firstUser := coderdtest.CreateFirstUser(t, client) + model := dbgen.ChatModelConfig(t, db, database.ChatModelConfig{ + OrganizationID: firstUser.OrganizationID, + }) + chat := dbgen.Chat(t, db, database.Chat{ + OrganizationID: firstUser.OrganizationID, + OwnerID: firstUser.UserID, + LastModelConfigID: model.ID, + }) + + for _, route := range []string{ + "/api/experimental/chats", + "/api/experimental/chats/config/system-prompt", + "/api/experimental/chats/models", + "/api/v2/chats", + "/api/v2/chats/config/system-prompt", + } { + res, err := client.Request(ctx, http.MethodGet, route, nil) + require.NoError(t, err) + _ = res.Body.Close() + require.Equal(t, http.StatusOK, res.StatusCode, route) + } + + for _, route := range []struct { + method string + path string + }{ + {http.MethodGet, "/api/v2/chats/models"}, + {http.MethodGet, "/api/v2/chats/model-configs"}, + {http.MethodPost, "/api/v2/chats/model-configs"}, + {http.MethodGet, "/api/v2/chats/providers"}, + {http.MethodGet, "/api/v2/chats/user-provider-configs"}, + {http.MethodGet, "/api/v2/chats/config/computer-use-provider"}, + {http.MethodGet, "/api/v2/chats/config/advisor"}, + {http.MethodGet, fmt.Sprintf("/api/v2/chats/%s/debug/runs", chat.ID)}, + {http.MethodGet, fmt.Sprintf("/api/v2/chats/%s/stream/desktop", chat.ID)}, + {http.MethodGet, "/api/v2/mcp/servers/not-a-uuid/oauth2/callback"}, + {http.MethodPost, "/api/v2/mcp/http/server"}, + } { + res, err := client.Request(ctx, route.method, route.path, nil) + require.NoError(t, err) + _ = res.Body.Close() + require.Equal(t, http.StatusNotFound, res.StatusCode, "%s %s", route.method, route.path) + } +} diff --git a/coderd/coderd.go b/coderd/coderd.go index 7199a8977d..ce04b0cb94 100644 --- a/coderd/coderd.go +++ b/coderd/coderd.go @@ -1369,220 +1369,33 @@ func New(options *Options) *API { r.Delete("/", api.deleteUserSkill) }) }) + // Chat routes are promoted to /api/v2. CODAGT-921 decided a compatibility + // window, so these experimental duplicates must remain for one release. + // TODO(CODAGT-921): remove after the transition window (tracked in CODAGT-922). r.Route("/users/{user}/ai-provider-keys", func(r chi.Router) { r.Use( apiKeyMiddleware, httpmw.ExtractUserParam(options.Database), ) - r.Get("/", api.listUserAIProviderKeyConfigs) - r.Route("/{aiProvider}", func(r chi.Router) { - r.Put("/", api.upsertUserAIProviderKey) - r.Delete("/", api.deleteUserAIProviderKey) - }) - }) - r.Group(func(r chi.Router) { - r.Use(httpmw.RateLimit(options.FilesRateLimit, time.Minute)) - r.Get("/chats/files/{file}/download", api.downloadChatFile) + api.registerUserAIProviderKeyRoutes(r) }) r.Route("/organizations", func(r chi.Router) { r.Use(apiKeyMiddleware) r.Route("/{organization}", func(r chi.Router) { r.Use(httpmw.ExtractOrganizationParam(options.Database)) - r.Route("/mcp-servers", func(r chi.Router) { - r.Get("/", api.listMCPServerConfigs) - r.Post("/", api.createMCPServerConfig) - r.Route("/{mcpserverconfig}", func(r chi.Router) { - r.With(httpmw.ExtractMCPServerConfigParam(options.Database, api.HTTPAuth.Authorize, - policy.ActionRead, policy.ActionUpdate, policy.ActionDelete)).Get("/", api.getMCPServerConfig) - r.With(httpmw.ExtractMCPServerConfigParam(options.Database, api.HTTPAuth.Authorize, - policy.ActionUpdate)).Patch("/", api.updateMCPServerConfig) - r.With(httpmw.ExtractMCPServerConfigParam(options.Database, api.HTTPAuth.Authorize, - policy.ActionDelete)).Delete("/", api.deleteMCPServerConfig) - r.With(httpmw.ExtractMCPServerConfigParam(options.Database, api.HTTPAuth.Authorize, - policy.ActionShare)).Get("/acl", api.mcpServerConfigACL) - r.With(httpmw.ExtractMCPServerConfigParam(options.Database, api.HTTPAuth.Authorize, - policy.ActionShare)).Patch("/acl", api.patchMCPServerConfigACL) - r.With(httpmw.ExtractMCPServerConfigParam(options.Database, api.HTTPAuth.Authorize, - policy.ActionRead)).Get("/oauth2/connect", api.mcpServerOAuth2Connect) - }) - }) - }) - }) - // Organization-scoped ChatModel management and runtime discovery. - // Keep the previous default-organization collection routes until the - // frontend uses the organization-scoped routes. - r.Route("/chats/model-configs", func(r chi.Router) { - r.Use( - apiKeyMiddleware, - func(next http.Handler) http.Handler { - return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { - chi.RouteContext(req.Context()).URLParams.Add("organization", codersdk.DefaultOrganization) - next.ServeHTTP(rw, req) - }) - }, - httpmw.ExtractOrganizationParam(options.Database), - ) - r.Get("/", api.listDefaultOrganizationChatModelConfigs) - r.Post("/", api.createChatModelConfig) - }) - r.With( - apiKeyMiddleware, - func(next http.Handler) http.Handler { - return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { - chi.RouteContext(req.Context()).URLParams.Add("organization", codersdk.DefaultOrganization) - next.ServeHTTP(rw, req) - }) - }, - httpmw.ExtractOrganizationParam(options.Database), - ).Get("/chats/models", api.listChatModelConfigsByOrganization) - - r.Route("/organizations/{organization}/chats/model-overrides", func(r chi.Router) { - r.Use( - apiKeyMiddleware, - httpmw.ExtractOrganizationParam(options.Database), - ) - r.Get("/", api.getOrganizationChatModelOverrides) - r.Put("/{context}", api.putOrganizationChatModelOverride) - }) - r.Route("/organizations/{organization}/members/{user}/chats/model-overrides", func(r chi.Router) { - r.Use( - apiKeyMiddleware, - httpmw.ExtractOrganizationParam(options.Database), - httpmw.ExtractOrganizationMemberParam(options.Database), - ) - r.Get("/", api.getUserChatPersonalModelOverrides) - r.Put("/{context}", api.putUserChatPersonalModelOverride) - }) - r.Route("/organizations/{organization}/chats/models", func(r chi.Router) { - r.Use(apiKeyMiddleware) - r.With(httpmw.ExtractOrganizationParam(options.Database)).Get("/", api.listChatModelConfigsByOrganization) - r.With(httpmw.ExtractOrganizationParam(options.Database)).Post("/", api.createChatModelConfig) - r.Route("/{model}", func(r chi.Router) { - r.Use( - httpmw.ExtractOrganizationParam(options.Database), - httpmw.ExtractChatModelConfigParam(options.Database), - ) - r.Get("/", api.getChatModelConfig) - r.Patch("/", api.updateChatModelConfig) - r.Delete("/", api.deleteChatModelConfig) - r.Route("/acl", func(r chi.Router) { - r.Get("/", api.chatModelConfigACLHandler) - r.Patch("/", api.updateChatModelConfigACL) - }) - }) - }) - r.Route("/chats", func(r chi.Router) { - r.Use( - apiKeyMiddleware, - ) - r.Get("/by-workspace", api.chatsByWorkspace) - r.Get("/", api.listChats) - r.Post("/", api.postChats) - r.Get("/watch", api.watchChats) - r.Route("/files", func(r chi.Router) { - r.Use(httpmw.RateLimit(options.FilesRateLimit, time.Minute)) - r.Post("/", api.postChatFile) - r.Post("/{file}/download-url", api.postChatFileDownloadURL) - r.Get("/{file}", api.chatFileByID) - }) - r.Route("/config", func(r chi.Router) { - r.Get("/system-prompt", api.getChatSystemPrompt) - r.Put("/system-prompt", api.putChatSystemPrompt) - r.Get("/plan-mode-instructions", api.getChatPlanModeInstructions) - r.Put("/plan-mode-instructions", api.putChatPlanModeInstructions) - r.Get("/personal-model-overrides", api.getChatPersonalModelOverridesAdminSettings) - r.Put("/personal-model-overrides", api.putChatPersonalModelOverridesAdminSettings) - r.Group(func(r chi.Router) { - r.Use(httpmw.RequireExperimentWithDevBypass(api.Experiments, codersdk.ExperimentChatVirtualDesktop)) - r.Get("/computer-use-provider", api.getChatComputerUseProvider) - r.Put("/computer-use-provider", api.putChatComputerUseProvider) - }) - r.Get("/debug-logging", api.getChatDebugLogging) - r.Put("/debug-logging", api.putChatDebugLogging) - r.Get("/user-debug-logging", api.getUserChatDebugLogging) - r.Put("/user-debug-logging", api.putUserChatDebugLogging) - r.Group(func(r chi.Router) { - r.Use(httpmw.RequireExperimentWithDevBypass(api.Experiments, codersdk.ExperimentChatAdvisor)) - r.Get("/advisor", api.getChatAdvisorConfig) - r.Put("/advisor", api.putChatAdvisorConfig) - }) - r.Get("/user-prompt", api.getUserChatCustomPrompt) - r.Put("/user-prompt", api.putUserChatCustomPrompt) - r.Get("/user-compaction-thresholds", api.getUserChatCompactionThresholds) - r.Put("/user-compaction-thresholds/{modelConfig}", api.putUserChatCompactionThreshold) - r.Delete("/user-compaction-thresholds/{modelConfig}", api.deleteUserChatCompactionThreshold) - r.Get("/workspace-ttl", api.getChatWorkspaceTTL) - r.Put("/workspace-ttl", api.putChatWorkspaceTTL) - r.Get("/retention-days", api.getChatRetentionDays) - r.Put("/retention-days", api.putChatRetentionDays) - r.Get("/debug-retention-days", api.getChatDebugRetentionDays) - r.Put("/debug-retention-days", api.putChatDebugRetentionDays) - r.Get("/auto-archive-days", api.getChatAutoArchiveDays) - r.Put("/auto-archive-days", api.putChatAutoArchiveDays) - }) - // TODO(cian): place under /api/experimental/chats/config - r.Route("/providers", func(r chi.Router) { - r.Get("/", api.listChatProviders) - r.Post("/", api.createChatProvider) - r.Route("/{providerConfig}", func(r chi.Router) { - r.Patch("/", api.updateChatProvider) - r.Delete("/", api.deleteChatProvider) - }) - }) - r.Route("/user-provider-configs", func(r chi.Router) { - r.Get("/", api.listUserChatProviderConfigs) - r.Route("/{providerConfig}", func(r chi.Router) { - r.Put("/", api.upsertUserChatProviderKey) - r.Delete("/", api.deleteUserChatProviderKey) - }) - }) - r.Route("/{chat}", func(r chi.Router) { - r.Use(httpmw.ExtractChatParam(options.Database)) - r.Route("/acl", func(r chi.Router) { - r.Get("/", api.getChatACL) - r.Patch("/", api.patchChatACL) - }) - r.Get("/", api.getChat) - r.Patch("/", api.patchChat) - r.Get("/cost", api.getChatCost) - r.Get("/messages", api.getChatMessages) - r.Post("/messages", api.postChatMessages) - r.Patch("/messages/{message}", api.patchChatMessage) - r.Get("/prompts", api.getChatUserPrompts) - r.Route("/stream", func(r chi.Router) { - r.Get("/", api.streamChat) - r.Get("/parts", api.streamChatParts) - r.Get("/desktop", api.watchChatDesktop) - r.Get("/git", api.watchChatGit) - }) - r.Post("/interrupt", api.interruptChat) - r.Post("/compact", api.compactChat) - r.Post("/reconcile-invalid", api.reconcileInvalidChatState) - r.Post("/tool-results", api.postChatToolResults) - r.Post("/title/propose", api.proposeChatTitle) - r.Get("/diff", api.getChatDiffContents) - r.Put("/context", api.refreshChatContext) - r.Route("/queue/{queuedMessage}", func(r chi.Router) { - r.Delete("/", api.deleteChatQueuedMessage) - r.Post("/promote", api.promoteChatQueuedMessage) - }) - r.Route("/debug", func(r chi.Router) { - r.Get("/runs", api.getChatDebugRuns) - r.Get("/runs/{debugRun}", api.getChatDebugRun) + api.registerOrganizationChatRoutes(r) + r.Route("/members/{user}", func(r chi.Router) { + r.Use(httpmw.ExtractOrganizationMemberParam(options.Database)) + api.registerOrganizationMemberChatRoutes(r) }) }) }) + api.registerChatAPIRoutes(r, apiKeyMiddleware, chatAPIPrefixExperimental) r.Route("/mcp", func(r chi.Router) { - r.Use( - apiKeyMiddleware, - ) - // This callback path is frozen because it is registered with OAuth2 providers. - r.Get("/servers/{mcpServer}/oauth2/callback", api.mcpServerOAuth2Callback) - // Disconnect stays outside organization routes so former organization - // members can delete their stored token after losing config read access. - r.Delete("/servers/{mcpServer}/oauth2/disconnect", api.mcpServerOAuth2Disconnect) - // MCP HTTP transport endpoint with mandatory authentication + r.Use(apiKeyMiddleware) + api.registerMCPServerOAuth2Routes(r, chatAPIPrefixExperimental) + // MCP HTTP transport endpoint with mandatory authentication. r.Route("/http", func(r chi.Router) { r.Use(httpmw.RequireExperimentWithDevBypass(api.Experiments, codersdk.ExperimentOAuth2, codersdk.ExperimentMCPServerHTTP)) r.Mount("/", api.mcpHTTPHandler()) @@ -1633,6 +1446,12 @@ func New(options *Options) *API { r.Get("/available", handleExperimentsAvailable) r.Get("/", api.handleExperimentsGet) }) + api.registerChatAPIRoutes(r, apiKeyMiddleware, chatAPIPrefixV2) + r.Route("/mcp", func(r chi.Router) { + r.Use(apiKeyMiddleware) + api.registerMCPServerOAuth2Routes(r, chatAPIPrefixV2) + }) + r.Get("/updatecheck", api.updateCheck) r.Route("/audit", func(r chi.Router) { r.Use( @@ -1695,6 +1514,7 @@ func New(options *Options) *API { r.Use( httpmw.ExtractOrganizationParam(options.Database), ) + api.registerOrganizationChatRoutes(r) r.Get("/", api.organization) r.Post("/templateversions", api.postTemplateVersionsByOrganization) r.Route("/templates", func(r chi.Router) { @@ -1732,6 +1552,7 @@ func New(options *Options) *API { r.Use( httpmw.ExtractOrganizationMemberParam(options.Database), ) + api.registerOrganizationMemberChatRoutes(r) r.Get("/", api.organizationMember) r.Delete("/", api.deleteOrganizationMember) r.Put("/roles", api.putMemberRoles) @@ -1884,6 +1705,7 @@ func New(options *Options) *API { r.Group(func(r chi.Router) { r.Use(httpmw.ExtractUserParam(options.Database)) + r.Route("/ai-provider-keys", api.registerUserAIProviderKeyRoutes) r.Post("/convert-login", api.postConvertLoginType) r.Delete("/", api.deleteUser) r.Get("/", api.userByName) @@ -2396,6 +2218,12 @@ type API struct { ctx context.Context cancel context.CancelFunc + // chatFilesRateLimit is shared by the /api/experimental and /api/v2 + // chat file mounts so the compatibility window does not double the + // FilesRateLimit budget. + chatFilesRateLimitOnce sync.Once + chatFilesRateLimit func(http.Handler) http.Handler + // DeploymentID is loaded from the database on startup. DeploymentID string diff --git a/coderd/coderdtest/swagger_test.go b/coderd/coderdtest/swagger_test.go index 07ea3c7440..f805264de7 100644 --- a/coderd/coderdtest/swagger_test.go +++ b/coderd/coderdtest/swagger_test.go @@ -1,6 +1,7 @@ package coderdtest_test import ( + "encoding/json" "go/ast" "go/parser" "go/token" @@ -10,6 +11,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/coder/coder/v2/coderd/apidoc" "github.com/coder/coder/v2/coderd/coderdtest" ) @@ -30,6 +32,36 @@ func TestEndpointsDocumented(t *testing.T) { coderdtest.VerifySwaggerDefinitions(t, api.APIHandler, swaggerComments, coderdtest.WithSwaggerRoutePrefix("/api/v2")) } +func TestChatModelPathParametersFormatted(t *testing.T) { + t.Parallel() + + var swagger struct { + Paths map[string]map[string]struct { + Parameters []struct { + Name string `json:"name"` + In string `json:"in"` + Format string `json:"format"` + } `json:"parameters"` + } `json:"paths"` + } + require.NoError(t, json.Unmarshal([]byte(apidoc.SwaggerInfo.ReadDoc()), &swagger)) + + operations := swagger.Paths["/api/v2/organizations/{organization}/chats/models/{model}"] + for _, method := range []string{"get", "patch", "delete"} { + t.Run(method, func(t *testing.T) { + t.Parallel() + + for _, parameter := range operations[method].Parameters { + if parameter.Name == "model" && parameter.In == "path" { + require.Equal(t, "uuid", parameter.Format) + return + } + } + require.Fail(t, "model path parameter not found") + }) + } +} + func TestSDKFieldsFormatted(t *testing.T) { t.Parallel() diff --git a/coderd/coderdtest/swaggerparser.go b/coderd/coderdtest/swaggerparser.go index 2fe62ecd1a..bff1ec553a 100644 --- a/coderd/coderdtest/swaggerparser.go +++ b/coderd/coderdtest/swaggerparser.go @@ -321,7 +321,7 @@ func assertGoCommentFirst(t *testing.T, comment SwaggerComment) { text := strings.TrimSpace(line.Text) if inSwaggerBlock { - if !strings.HasPrefix(text, "// @") && !strings.HasPrefix(text, "// nolint:") { + if text != "//" && !strings.HasPrefix(text, "// @") && !strings.HasPrefix(text, "// nolint:") && !strings.HasPrefix(text, "//nolint:") { assert.Fail(t, "Go function comment must be placed before swagger comments") return } @@ -369,7 +369,8 @@ func assertSecurityDefined(t *testing.T, comment SwaggerComment) { comment.router == "/api/v2/users/login" || comment.router == "/api/v2/users/otp/request" || comment.router == "/api/v2/users/otp/change-password" || - comment.router == "/api/v2/init-script/{os}/{arch}" { + comment.router == "/api/v2/init-script/{os}/{arch}" || + comment.router == "/api/v2/chats/files/{file}/download" { return // endpoints do not require authorization } if comment.router == "/api/v2/ai-gateway/serve" { @@ -428,8 +429,10 @@ func assertProduce(t *testing.T, comment SwaggerComment) { (comment.router == "/api/v2/workspaces/{workspace}/acl" && comment.method == "patch") || (comment.router == "/api/v2/init-script/{os}/{arch}" && comment.method == "get") || (comment.router == "/api/v2/organizations/{organization}/ai/spend/export" && comment.method == "get") || - (comment.router == "/api/v2/templatebuilder/compose" && comment.method == "post") { - return // Exception: HTTP 200 is returned without response entity + (comment.router == "/api/v2/templatebuilder/compose" && comment.method == "post") || + (comment.router == "/api/v2/chats/files/{file}" && comment.method == "get") || + (comment.router == "/api/v2/chats/files/{file}/download" && comment.method == "get") { + return // Exception: HTTP 200 is returned without a response model } assert.Truef(t, comment.produce == "", "Response model is undefined, so we can't predict the content type: %v", comment) diff --git a/coderd/exp_chats.go b/coderd/exp_chats.go index a7a093e398..9a0fadcfd5 100644 --- a/coderd/exp_chats.go +++ b/coderd/exp_chats.go @@ -221,16 +221,13 @@ func publishChatConfigEvent(logger slog.Logger, ps dbpubsub.Pubsub, kind pubsub. } } -// EXPERIMENTAL: this endpoint is experimental and is subject to change. -// // @Summary Watch chat events for a user via WebSockets // @ID watch-chat-events-for-a-user-via-websockets // @Security CoderSessionToken // @Tags Chats // @Produce json // @Success 200 {object} codersdk.ChatWatchEvent -// @Router /api/experimental/chats/watch [get] -// @Description Experimental: this endpoint is subject to change. +// @Router /api/v2/chats/watch [get] func (api *API) watchChats(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() apiKey := httpmw.APIKey(r) @@ -311,33 +308,30 @@ func (api *API) watchChats(rw http.ResponseWriter, r *http.Request) { <-ctx.Done() } -// EXPERIMENTAL: chatsByWorkspace returns a mapping of workspace ID to // the latest non-archived chat ID for each requested workspace. // The query returns all matching chats and RBAC post-filters them; // the handler then picks the latest per workspace in Go. This avoids // the DISTINCT ON + post-filter bug where the sole candidate is // silently dropped when the caller can't read it. // -// TODO: -// 1. move aggregation to a SQL view with proper in-query authz so we -// can return a single row per workspace without this two-pass approach. -// 2. Restore the below router annotation and un-skip docs gen -// Router /api/experimental/chats/by-workspace [post] -// -// @Summary Get latest chats by workspace IDs -// @ID get-latest-chats-by-workspace-ids +// TODO: move aggregation to a SQL view with proper in-query authz so the +// handler can return a single row per workspace without this two-pass approach. +type chatsByWorkspaceResponse map[uuid.UUID]uuid.UUID + +// @Summary List chats by workspace +// @ID list-chats-by-workspace // @Security CoderSessionToken // @Tags Chats -// @Accept json +// @Param workspace_ids query string false "Comma-separated workspace IDs" // @Produce json -// @Success 200 -// @x-apidocgen {"skip": true} +// @Success 200 {object} chatsByWorkspaceResponse +// @Router /api/v2/chats/by-workspace [get] func (api *API) chatsByWorkspace(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() idsParam := r.URL.Query().Get("workspace_ids") if idsParam == "" { - httpapi.Write(ctx, rw, http.StatusOK, map[uuid.UUID]uuid.UUID{}) + httpapi.Write(ctx, rw, http.StatusOK, chatsByWorkspaceResponse{}) return } @@ -381,7 +375,7 @@ func (api *API) chatsByWorkspace(rw http.ResponseWriter, r *http.Request) { // The SQL orders by (workspace_id, updated_at DESC), so the first // chat seen per workspace after RBAC filtering is the latest // readable one. - result := make(map[uuid.UUID]uuid.UUID, len(chats)) + result := make(chatsByWorkspaceResponse, len(chats)) for _, chat := range chats { if chat.WorkspaceID.Valid { if _, exists := result[chat.WorkspaceID.UUID]; !exists { @@ -393,18 +387,18 @@ func (api *API) chatsByWorkspace(rw http.ResponseWriter, r *http.Request) { httpapi.Write(ctx, rw, http.StatusOK, result) } -// EXPERIMENTAL: this endpoint is experimental and is subject to change. -// // @Summary List chats // @ID list-chats // @Security CoderSessionToken // @Tags Chats // @Produce json // @Param q query string false "Search query. Supports `title:` (case-insensitive, quote multi-word values), `archived:bool`, `has_unread:bool`, `pr_status:` as repeated or comma-separated values, `source:`, `diff_url:` (quote values containing colons), `pr:` (exact PR number match), `repo:` (case-insensitive substring match against git remote origin or URL), `pr_title:` (case-insensitive PR title substring), `search:` (full-text search across chat titles, PR titles, PR numbers, and message bodies; message bodies match English word stems, e.g. `refactor` matches `refactoring`, and ignore English stopwords; titles and PR titles match whole words case-insensitively without stemming; quote multi-word values; cannot be combined with title, pr_title, or pr; a value that tokenizes to no searchable words, e.g. punctuation only, returns an empty list). Bare terms are not supported; use `title:` or `search:`." -// @Param label query string false "Filter by label as key:value. Repeat for multiple (AND logic)." +// @Param label query []string false "Filter by label as key:value. Repeat for multiple (AND logic)." collectionFormat(multi) +// @Param after_id query string false "After ID" format(uuid) +// @Param limit query int false "Page limit" +// @Param offset query int false "Page offset" // @Success 200 {array} codersdk.Chat -// @Router /api/experimental/chats [get] -// @Description Experimental: this endpoint is subject to change. +// @Router /api/v2/chats [get] func (api *API) listChats(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() apiKey := httpmw.APIKey(r) @@ -1186,8 +1180,6 @@ func invalidChatMCPServerIDsResponse(ids []uuid.UUID) codersdk.Response { } } -// EXPERIMENTAL: this endpoint is experimental and is subject to change. -// // @Summary Create chat // @ID create-chat // @Security CoderSessionToken @@ -1197,8 +1189,7 @@ func invalidChatMCPServerIDsResponse(ids []uuid.UUID) codersdk.Response { // @Param request body codersdk.CreateChatRequest true "Create chat request" // @Success 201 {object} codersdk.Chat // @Failure 413 {object} codersdk.Response "Request body exceeds 256 KiB" -// @Router /api/experimental/chats [post] -// @Description Experimental: this endpoint is subject to change. +// @Router /api/v2/chats [post] func (api *API) postChats(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() apiKey := httpmw.APIKey(r) @@ -1460,8 +1451,6 @@ func (api *API) postChats(rw http.ResponseWriter, r *http.Request) { httpapi.Write(ctx, rw, http.StatusCreated, response) } -// EXPERIMENTAL: this endpoint is experimental and is subject to change. -// // @Summary Get chat by ID // @ID get-chat-by-id // @Security CoderSessionToken @@ -1469,8 +1458,7 @@ func (api *API) postChats(rw http.ResponseWriter, r *http.Request) { // @Produce json // @Param chat path string true "Chat ID" format(uuid) // @Success 200 {object} codersdk.Chat -// @Router /api/experimental/chats/{chat} [get] -// @Description Experimental: this endpoint is subject to change. +// @Router /api/v2/chats/{chat} [get] // //nolint:revive // HTTP handler writes to ResponseWriter. func (api *API) getChat(rw http.ResponseWriter, r *http.Request) { @@ -1567,8 +1555,6 @@ func (api *API) getChat(rw http.ResponseWriter, r *http.Request) { httpapi.Write(ctx, rw, http.StatusOK, sdkChat) } -// EXPERIMENTAL: this endpoint is experimental and is subject to change. -// // @Summary List chat messages // @ID list-chat-messages // @Security CoderSessionToken @@ -1579,8 +1565,7 @@ func (api *API) getChat(rw http.ResponseWriter, r *http.Request) { // @Param after_id query int false "Return messages with id > after_id" // @Param limit query int false "Page size, 1 to 200. Defaults to 50." // @Success 200 {object} codersdk.ChatMessagesResponse -// @Router /api/experimental/chats/{chat}/messages [get] -// @Description Experimental: this endpoint is subject to change. +// @Router /api/v2/chats/{chat}/messages [get] // //nolint:revive // HTTP handler writes to ResponseWriter. func (api *API) getChatMessages(rw http.ResponseWriter, r *http.Request) { @@ -1672,8 +1657,6 @@ func (api *API) getChatMessages(rw http.ResponseWriter, r *http.Request) { }) } -// EXPERIMENTAL: this endpoint is experimental and is subject to change. -// // @Summary Get chat cost // @ID get-chat-cost // @Security CoderSessionToken @@ -1681,8 +1664,7 @@ func (api *API) getChatMessages(rw http.ResponseWriter, r *http.Request) { // @Produce json // @Param chat path string true "Chat ID" format(uuid) // @Success 200 {object} codersdk.ChatCost -// @Router /api/experimental/chats/{chat}/cost [get] -// @Description Experimental: this endpoint is subject to change. +// @Router /api/v2/chats/{chat}/cost [get] // @Description // @Description Cost covers the whole chat tree: the root chat plus every // @Description subagent chat beneath it. Requesting cost for a subagent chat @@ -1743,8 +1725,7 @@ func (api *API) getChatCost(rw http.ResponseWriter, r *http.Request) { // @Param chat path string true "Chat ID" format(uuid) // @Param limit query int false "Page size, 0 to 2000. 0 (the default) means the server-side default of 500." // @Success 200 {object} codersdk.ChatPromptsResponse -// @Router /api/experimental/chats/{chat}/prompts [get] -// @Description Experimental: this endpoint is subject to change. +// @Router /api/v2/chats/{chat}/prompts [get] // @Description // @Description Returns the user-authored prompts in a chat, newest first, // @Description with each prompt's text parts concatenated in the order they @@ -1858,8 +1839,6 @@ func (api *API) authorizeChatWorkspaceExec( return workspace, true } -// EXPERIMENTAL: this endpoint is experimental and is subject to change. -// // @Summary Watch chat workspace git state via WebSockets // @ID watch-chat-workspace-git-state-via-websockets // @Security CoderSessionToken @@ -1867,8 +1846,7 @@ func (api *API) authorizeChatWorkspaceExec( // @Produce json // @Param chat path string true "Chat ID" format(uuid) // @Success 200 {object} codersdk.WorkspaceAgentGitServerMessage -// @Router /api/experimental/chats/{chat}/stream/git [get] -// @Description Experimental: this endpoint is subject to change. +// @Router /api/v2/chats/{chat}/stream/git [get] // //nolint:revive // HTTP handler writes to ResponseWriter. func (api *API) watchChatGit(rw http.ResponseWriter, r *http.Request) { @@ -2111,7 +2089,7 @@ func (api *API) watchChatDesktop(rw http.ResponseWriter, r *http.Request) { return } - // No read limit — RFB framebuffer updates can be large. + // No read limit because RFB framebuffer updates can be large. conn.SetReadLimit(-1) ctx, cancel := context.WithCancel(ctx) @@ -2178,8 +2156,7 @@ func (api *API) applyChatTitleUpdate( // @Produce json // @Param chat path string true "Chat ID" format(uuid) // @Success 200 {object} codersdk.Chat -// @Router /api/experimental/chats/{chat}/context [put] -// @Description Experimental: this endpoint is subject to change. +// @Router /api/v2/chats/{chat}/context [put] func (api *API) refreshChatContext(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() chat := httpmw.ChatParam(r) @@ -2235,8 +2212,7 @@ func (api *API) refreshChatContext(rw http.ResponseWriter, r *http.Request) { // @Param chat path string true "Chat ID" format(uuid) // @Param request body codersdk.UpdateChatRequest true "Update chat request" // @Success 204 -// @Router /api/experimental/chats/{chat} [patch] -// @Description Experimental: this endpoint is subject to change. +// @Router /api/v2/chats/{chat} [patch] func (api *API) patchChat(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() chat := httpmw.ChatParam(r) @@ -2564,8 +2540,6 @@ func writeCommonChatMutationError(ctx context.Context, rw http.ResponseWriter, e return true } -// EXPERIMENTAL: this endpoint is experimental and is subject to change. -// // @Summary Send chat message // @ID send-chat-message // @Security CoderSessionToken @@ -2575,8 +2549,7 @@ func writeCommonChatMutationError(ctx context.Context, rw http.ResponseWriter, e // @Param chat path string true "Chat ID" format(uuid) // @Param request body codersdk.CreateChatMessageRequest true "Create chat message request" // @Success 200 {object} codersdk.CreateChatMessageResponse -// @Router /api/experimental/chats/{chat}/messages [post] -// @Description Experimental: this endpoint is subject to change. +// @Router /api/v2/chats/{chat}/messages [post] func (api *API) postChatMessages(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() apiKey := httpmw.APIKey(r) @@ -2769,8 +2742,6 @@ func (api *API) postChatMessages(rw http.ResponseWriter, r *http.Request) { httpapi.Write(ctx, rw, http.StatusOK, response) } -// EXPERIMENTAL: this endpoint is experimental and is subject to change. -// // @Summary Edit chat message // @ID edit-chat-message // @Security CoderSessionToken @@ -2781,8 +2752,7 @@ func (api *API) postChatMessages(rw http.ResponseWriter, r *http.Request) { // @Param message path int true "Message ID" // @Param request body codersdk.EditChatMessageRequest true "Edit chat message request" // @Success 200 {object} codersdk.EditChatMessageResponse -// @Router /api/experimental/chats/{chat}/messages/{message} [patch] -// @Description Experimental: this endpoint is subject to change. +// @Router /api/v2/chats/{chat}/messages/{message} [patch] func (api *API) patchChatMessage(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() apiKey := httpmw.APIKey(r) @@ -2928,7 +2898,14 @@ func (api *API) patchChatMessage(rw http.ResponseWriter, r *http.Request) { httpapi.Write(ctx, rw, http.StatusOK, response) } -// EXPERIMENTAL: this endpoint is experimental and is subject to change. +// @Summary Delete chat queued message +// @ID delete-chat-queued-message +// @Security CoderSessionToken +// @Tags Chats +// @Param chat path string true "Chat ID" format(uuid) +// @Param queuedMessage path int true "Queued message ID" +// @Success 204 +// @Router /api/v2/chats/{chat}/queue/{queuedMessage} [delete] func (api *API) deleteChatQueuedMessage(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() chat := httpmw.ChatParam(r) @@ -2981,7 +2958,15 @@ func (api *API) deleteChatQueuedMessage(rw http.ResponseWriter, r *http.Request) rw.WriteHeader(http.StatusNoContent) } -// EXPERIMENTAL: this endpoint is experimental and is subject to change. +// @Summary Promote chat queued message +// @ID promote-chat-queued-message +// @Security CoderSessionToken +// @Tags Chats +// @Param chat path string true "Chat ID" format(uuid) +// @Param queuedMessage path int true "Queued message ID" +// @Produce json +// @Success 202 {object} codersdk.Response +// @Router /api/v2/chats/{chat}/queue/{queuedMessage}/promote [post] func (api *API) promoteChatQueuedMessage(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() apiKey := httpmw.APIKey(r) @@ -3099,17 +3084,15 @@ func (api *API) markChatAsRead(ctx context.Context, chatID uuid.UUID) { } } -// EXPERIMENTAL: this endpoint is experimental and is subject to change. -// // @Summary Stream chat events via WebSockets // @ID stream-chat-events-via-websockets // @Security CoderSessionToken // @Tags Chats // @Produce json // @Param chat path string true "Chat ID" format(uuid) -// @Success 200 {object} codersdk.ChatStreamEvent -// @Router /api/experimental/chats/{chat}/stream [get] -// @Description Experimental: this endpoint is subject to change. +// @Param after_id query int false "Skip snapshot messages with id at or before this cursor" +// @Success 200 {array} codersdk.ChatStreamEvent +// @Router /api/v2/chats/{chat}/stream [get] func (api *API) streamChat(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() chat := httpmw.ChatParam(r) @@ -3240,8 +3223,6 @@ func (api *API) streamChat(rw http.ResponseWriter, r *http.Request) { } } -// EXPERIMENTAL: this endpoint is experimental and is subject to change. -// // @Summary Interrupt chat // @ID interrupt-chat // @Security CoderSessionToken @@ -3249,8 +3230,7 @@ func (api *API) streamChat(rw http.ResponseWriter, r *http.Request) { // @Param chat path string true "Chat ID" format(uuid) // @Produce json // @Success 200 {object} codersdk.Chat -// @Router /api/experimental/chats/{chat}/interrupt [post] -// @Description Experimental: this endpoint is subject to change. +// @Router /api/v2/chats/{chat}/interrupt [post] func (api *API) interruptChat(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() chat := httpmw.ChatParam(r) @@ -3291,8 +3271,6 @@ func (api *API) interruptChat(rw http.ResponseWriter, r *http.Request) { httpapi.Write(ctx, rw, http.StatusOK, db2sdk.Chat(chat, nil, nil)) } -// EXPERIMENTAL: this endpoint is experimental and is subject to change. -// // @Summary Compact chat // @ID compact-chat // @Security CoderSessionToken @@ -3300,9 +3278,8 @@ func (api *API) interruptChat(rw http.ResponseWriter, r *http.Request) { // @Param chat path string true "Chat ID" format(uuid) // @Produce json // @Success 200 {object} codersdk.Chat -// @Router /api/experimental/chats/{chat}/compact [post] +// @Router /api/v2/chats/{chat}/compact [post] // @x-apidocgen {"skip": true} -// @Description Experimental: this endpoint is subject to change. // @Description Requests a manual context compaction on an idle or errored // @Description chat, clearing any stored error. The compaction runs // @Description asynchronously through the chat worker and bypasses the @@ -3364,8 +3341,6 @@ func (api *API) compactChat(rw http.ResponseWriter, r *http.Request) { httpapi.Write(ctx, rw, http.StatusOK, db2sdk.Chat(updated, nil, nil)) } -// EXPERIMENTAL: this endpoint is experimental and is subject to change. -// // @Summary Reconcile invalid chat state // @ID reconcile-invalid-chat-state // @Security CoderSessionToken @@ -3373,8 +3348,7 @@ func (api *API) compactChat(rw http.ResponseWriter, r *http.Request) { // @Produce json // @Param chat path string true "Chat ID" format(uuid) // @Success 200 {object} codersdk.Chat -// @Router /api/experimental/chats/{chat}/reconcile-invalid [post] -// @Description Experimental: this endpoint is subject to change. +// @Router /api/v2/chats/{chat}/reconcile-invalid [post] // //nolint:revive // HTTP handler writes to ResponseWriter. func (api *API) reconcileInvalidChatState(rw http.ResponseWriter, r *http.Request) { @@ -3416,8 +3390,6 @@ func (api *API) reconcileInvalidChatState(rw http.ResponseWriter, r *http.Reques httpapi.Write(ctx, rw, http.StatusOK, db2sdk.Chat(updated, nil, nil)) } -// EXPERIMENTAL: this endpoint is experimental and is subject to change. -// // @Summary Propose chat title // @ID propose-chat-title // @Security CoderSessionToken @@ -3425,8 +3397,7 @@ func (api *API) reconcileInvalidChatState(rw http.ResponseWriter, r *http.Reques // @Produce json // @Param chat path string true "Chat ID" format(uuid) // @Success 200 {object} codersdk.ProposeChatTitleResponse -// @Router /api/experimental/chats/{chat}/title/propose [post] -// @Description Experimental: this endpoint is subject to change. +// @Router /api/v2/chats/{chat}/title/propose [post] // //nolint:revive // HTTP handler writes to ResponseWriter. func (api *API) proposeChatTitle(rw http.ResponseWriter, r *http.Request) { @@ -3478,8 +3449,6 @@ func (api *API) proposeChatTitle(rw http.ResponseWriter, r *http.Request) { httpapi.Write(ctx, rw, http.StatusOK, codersdk.ProposeChatTitleResponse{Title: title}) } -// EXPERIMENTAL: this endpoint is experimental and is subject to change. -// // @Summary Get chat diff contents // @ID get-chat-diff-contents // @Security CoderSessionToken @@ -3487,8 +3456,7 @@ func (api *API) proposeChatTitle(rw http.ResponseWriter, r *http.Request) { // @Produce json // @Param chat path string true "Chat ID" format(uuid) // @Success 200 {object} codersdk.ChatDiffContents -// @Router /api/experimental/chats/{chat}/diff [get] -// @Description Experimental: this endpoint is subject to change. +// @Router /api/v2/chats/{chat}/diff [get] // //nolint:revive // HTTP handler writes to ResponseWriter. func (api *API) getChatDiffContents(rw http.ResponseWriter, r *http.Request) { @@ -4096,7 +4064,7 @@ func (api *API) resolveChatGitAccessToken( slog.F("user_id", userID), slog.Error(refreshErr), ) - // Fall through — the existing token may still work + // Fall through because the existing token may still work. // (e.g. GitHub tokens with no expiry). } else { link = refreshed @@ -4329,6 +4297,14 @@ func parseCompactionThresholdKey(key string) (uuid.UUID, error) { return id, nil } +// @Summary Get chat system prompt +// @ID get-chat-system-prompt +// @Security CoderSessionToken +// @Tags Chats +// @Produce json +// @Success 200 {object} codersdk.ChatSystemPromptResponse +// @Router /api/v2/chats/config/system-prompt [get] +// //nolint:revive // get-return: revive assumes get* must be a getter, but this is an HTTP handler. func (api *API) getChatSystemPrompt(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() @@ -4356,6 +4332,14 @@ func (api *API) getChatSystemPrompt(rw http.ResponseWriter, r *http.Request) { // holding it for a single upsert. const chatInstructionSettingsLockTimeout = 5 * time.Second +// @Summary Update chat system prompt +// @ID update-chat-system-prompt +// @Security CoderSessionToken +// @Tags Chats +// @Accept json +// @Param request body codersdk.UpdateChatSystemPromptRequest true "Request body" +// @Success 204 +// @Router /api/v2/chats/config/system-prompt [put] func (api *API) putChatSystemPrompt(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() @@ -4474,7 +4458,13 @@ func (api *API) putChatSystemPrompt(rw http.ResponseWriter, r *http.Request) { rw.WriteHeader(http.StatusNoContent) } -// EXPERIMENTAL: this endpoint is experimental and is subject to change. +// @Summary Get chat plan mode instructions +// @ID get-chat-plan-mode-instructions +// @Security CoderSessionToken +// @Tags Chats +// @Produce json +// @Success 200 {object} codersdk.ChatPlanModeInstructionsResponse +// @Router /api/v2/chats/config/plan-mode-instructions [get] // //nolint:revive // get-return: revive assumes get* must be a getter, but this is an HTTP handler. func (api *API) getChatPlanModeInstructions(rw http.ResponseWriter, r *http.Request) { @@ -4498,7 +4488,14 @@ func (api *API) getChatPlanModeInstructions(rw http.ResponseWriter, r *http.Requ }) } -// EXPERIMENTAL: this endpoint is experimental and is subject to change. +// @Summary Update chat plan mode instructions +// @ID update-chat-plan-mode-instructions +// @Security CoderSessionToken +// @Tags Chats +// @Accept json +// @Param request body codersdk.UpdateChatPlanModeInstructionsRequest true "Request body" +// @Success 204 +// @Router /api/v2/chats/config/plan-mode-instructions [put] func (api *API) putChatPlanModeInstructions(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() @@ -4613,11 +4610,9 @@ func readChatModelOverrideContext( // @Produce json // @Param organization path string true "Organization name or ID" // @Success 200 {object} codersdk.ChatModelOverridesResponse -// @Router /api/experimental/organizations/{organization}/chats/model-overrides [get] +// @Router /api/v2/organizations/{organization}/chats/model-overrides [get] // @x-apidocgen {"skip": true} // -// EXPERIMENTAL: this endpoint is experimental and is subject to change. -// //nolint:revive // get-return: revive assumes get* must be a getter, but this is an HTTP handler. func (api *API) getOrganizationChatModelOverrides(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() @@ -4653,10 +4648,8 @@ func (api *API) getOrganizationChatModelOverrides(rw http.ResponseWriter, r *htt // @Param context path string true "Override context" Enums(general,explore,title_generation,compaction,advisor) // @Param request body codersdk.UpdateChatModelOverrideRequest true "Model override" // @Success 200 {object} codersdk.ChatModelOverrideResponse -// @Router /api/experimental/organizations/{organization}/chats/model-overrides/{context} [put] +// @Router /api/v2/organizations/{organization}/chats/model-overrides/{context} [put] // @x-apidocgen {"skip": true} -// -// EXPERIMENTAL: this endpoint is experimental and is subject to change. func (api *API) putOrganizationChatModelOverride(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() organization := httpmw.OrganizationParam(r) @@ -4776,7 +4769,13 @@ func readChatPersonalModelOverrideContext( return "", false } -// EXPERIMENTAL: this endpoint is experimental and is subject to change. +// @Summary Get chat personal model override settings +// @ID get-chat-personal-model-override-settings +// @Security CoderSessionToken +// @Tags Chats +// @Produce json +// @Success 200 {object} codersdk.ChatPersonalModelOverridesAdminSettings +// @Router /api/v2/chats/config/personal-model-overrides [get] // //nolint:revive // get-return: revive assumes get* must be a getter, but this is an HTTP handler. func (api *API) getChatPersonalModelOverridesAdminSettings(rw http.ResponseWriter, r *http.Request) { @@ -4916,7 +4915,14 @@ func (api *API) auditedChatOperationalSettingWrite( return nil } -// EXPERIMENTAL: this endpoint is experimental and is subject to change. +// @Summary Update chat personal model override settings +// @ID update-chat-personal-model-override-settings +// @Security CoderSessionToken +// @Tags Chats +// @Accept json +// @Param request body codersdk.UpdateChatPersonalModelOverridesAdminSettingsRequest true "Request body" +// @Success 204 +// @Router /api/v2/chats/config/personal-model-overrides [put] func (api *API) putChatPersonalModelOverridesAdminSettings(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() aReq, commitAudit := api.initChatOperationalSettingsAudit(rw, r) @@ -4954,11 +4960,9 @@ func (api *API) putChatPersonalModelOverridesAdminSettings(rw http.ResponseWrite // @Param organization path string true "Organization name or ID" // @Param user path string true "User name, ID, or me" // @Success 200 {object} codersdk.UserChatPersonalModelOverridesResponse -// @Router /api/experimental/organizations/{organization}/members/{user}/chats/model-overrides [get] +// @Router /api/v2/organizations/{organization}/members/{user}/chats/model-overrides [get] // @x-apidocgen {"skip": true} // -// EXPERIMENTAL: this endpoint is experimental and is subject to change. -// //nolint:revive // get-return: revive assumes get* must be a getter, but this is an HTTP handler. func (api *API) getUserChatPersonalModelOverrides(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() @@ -5035,10 +5039,8 @@ func (api *API) getUserChatPersonalModelOverrides(rw http.ResponseWriter, r *htt // @Param context path string true "Override context" Enums(root,general,explore) // @Param request body codersdk.UpdateUserChatPersonalModelOverrideRequest true "Personal model override" // @Success 204 -// @Router /api/experimental/organizations/{organization}/members/{user}/chats/model-overrides/{context} [put] +// @Router /api/v2/organizations/{organization}/members/{user}/chats/model-overrides/{context} [put] // @x-apidocgen {"skip": true} -// -// EXPERIMENTAL: this endpoint is experimental and is subject to change. func (api *API) putUserChatPersonalModelOverride(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() apiKey := httpmw.APIKey(r) @@ -5235,7 +5237,13 @@ func (api *API) deploymentChatDebugLoggingEnabled() bool { return api.DeploymentValues != nil && api.DeploymentValues.AI.Chat.DebugLoggingEnabled.Value() } -// EXPERIMENTAL: this endpoint is experimental and is subject to change. +// @Summary Get chat debug logging setting +// @ID get-chat-debug-logging-setting +// @Security CoderSessionToken +// @Tags Chats +// @Produce json +// @Success 200 {object} codersdk.ChatDebugLoggingAdminSettings +// @Router /api/v2/chats/config/debug-logging [get] // //nolint:revive // get-return: revive assumes get* must be a getter, but this is an HTTP handler. func (api *API) getChatDebugLogging(rw http.ResponseWriter, r *http.Request) { @@ -5259,7 +5267,14 @@ func (api *API) getChatDebugLogging(rw http.ResponseWriter, r *http.Request) { }) } -// EXPERIMENTAL: this endpoint is experimental and is subject to change. +// @Summary Update chat debug logging setting +// @ID update-chat-debug-logging-setting +// @Security CoderSessionToken +// @Tags Chats +// @Accept json +// @Param request body codersdk.UpdateChatDebugLoggingAllowUsersRequest true "Request body" +// @Success 204 +// @Router /api/v2/chats/config/debug-logging [put] func (api *API) putChatDebugLogging(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() aReq, commitAudit := api.initChatOperationalSettingsAudit(rw, r) @@ -5289,7 +5304,13 @@ func (api *API) putChatDebugLogging(rw http.ResponseWriter, r *http.Request) { rw.WriteHeader(http.StatusNoContent) } -// EXPERIMENTAL: this endpoint is experimental and is subject to change. +// @Summary Get user chat debug logging setting +// @ID get-user-chat-debug-logging-setting +// @Security CoderSessionToken +// @Tags Chats +// @Produce json +// @Success 200 {object} codersdk.UserChatDebugLoggingSettings +// @Router /api/v2/chats/config/user-debug-logging [get] // //nolint:revive // get-return: revive assumes get* must be a getter, but this is an HTTP handler. func (api *API) getUserChatDebugLogging(rw http.ResponseWriter, r *http.Request) { @@ -5330,7 +5351,14 @@ func (api *API) getUserChatDebugLogging(rw http.ResponseWriter, r *http.Request) }) } -// EXPERIMENTAL: this endpoint is experimental and is subject to change. +// @Summary Update user chat debug logging setting +// @ID update-user-chat-debug-logging-setting +// @Security CoderSessionToken +// @Tags Chats +// @Accept json +// @Param request body codersdk.UpdateUserChatDebugLoggingRequest true "Request body" +// @Success 204 +// @Router /api/v2/chats/config/user-debug-logging [put] func (api *API) putUserChatDebugLogging(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() apiKey := httpmw.APIKey(r) @@ -5459,7 +5487,13 @@ func (api *API) putChatAdvisorConfig(rw http.ResponseWriter, r *http.Request) { rw.WriteHeader(http.StatusNoContent) } -// EXPERIMENTAL: this endpoint is experimental and is subject to change. +// @Summary Get chat workspace time to live +// @ID get-chat-workspace-time-to-live +// @Security CoderSessionToken +// @Tags Chats +// @Produce json +// @Success 200 {object} codersdk.ChatWorkspaceTTLResponse +// @Router /api/v2/chats/config/workspace-ttl [get] // //nolint:revive // get-return: revive assumes get* must be a getter, but this is an HTTP handler. func (api *API) getChatWorkspaceTTL(rw http.ResponseWriter, r *http.Request) { @@ -5491,7 +5525,14 @@ func (api *API) getChatWorkspaceTTL(rw http.ResponseWriter, r *http.Request) { }) } -// EXPERIMENTAL: this endpoint is experimental and is subject to change. +// @Summary Update chat workspace time to live +// @ID update-chat-workspace-time-to-live +// @Security CoderSessionToken +// @Tags Chats +// @Accept json +// @Param request body codersdk.UpdateChatWorkspaceTTLRequest true "Request body" +// @Success 204 +// @Router /api/v2/chats/config/workspace-ttl [put] func (api *API) putChatWorkspaceTTL(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() aReq, commitAudit := api.initChatOperationalSettingsAudit(rw, r) @@ -5557,7 +5598,7 @@ func (api *API) putChatWorkspaceTTL(rw http.ResponseWriter, r *http.Request) { // @Tags Chats // @Produce json // @Success 200 {object} codersdk.ChatRetentionDaysResponse -// @Router /api/experimental/chats/config/retention-days [get] +// @Router /api/v2/chats/config/retention-days [get] // @x-apidocgen {"skip": true} // //nolint:revive // get-return: revive assumes get* must be a getter, but this is an HTTP handler. @@ -5587,7 +5628,7 @@ const retentionDaysMaximum = 3650 // ~10 years // @Accept json // @Param request body codersdk.UpdateChatRetentionDaysRequest true "Request body" // @Success 204 -// @Router /api/experimental/chats/config/retention-days [put] +// @Router /api/v2/chats/config/retention-days [put] // @x-apidocgen {"skip": true} func (api *API) putChatRetentionDays(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() @@ -5627,6 +5668,14 @@ func (api *API) putChatRetentionDays(rw http.ResponseWriter, r *http.Request) { // getChatDebugRetentionDays returns the deployment-wide chat debug run // retention window. Any authenticated user can read it; writes require admin. // +// @Summary Get chat debug retention days +// @ID get-chat-debug-retention-days +// @Security CoderSessionToken +// @Tags Chats +// @Produce json +// @Success 200 {object} codersdk.ChatDebugRetentionDaysResponse +// @Router /api/v2/chats/config/debug-retention-days [get] +// //nolint:revive // get-return: revive assumes get* must be a getter, but this is an HTTP handler. func (api *API) getChatDebugRetentionDays(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() @@ -5649,6 +5698,15 @@ const chatDebugRetentionDaysMaximum = 3650 // ~10 years // putChatDebugRetentionDays updates the deployment-wide chat debug run // retention window. Admin-only. +// +// @Summary Update chat debug retention days +// @ID update-chat-debug-retention-days +// @Security CoderSessionToken +// @Tags Chats +// @Accept json +// @Param request body codersdk.UpdateChatDebugRetentionDaysRequest true "Request body" +// @Success 204 +// @Router /api/v2/chats/config/debug-retention-days [put] func (api *API) putChatDebugRetentionDays(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() aReq, commitAudit := api.initChatOperationalSettingsAudit(rw, r) @@ -5688,6 +5746,14 @@ func (api *API) putChatDebugRetentionDays(rw http.ResponseWriter, r *http.Reques // window. Any authenticated user can read it (same as retention // days); writes require admin. // +// @Summary Get chat auto archive days +// @ID get-chat-auto-archive-days +// @Security CoderSessionToken +// @Tags Chats +// @Produce json +// @Success 200 {object} codersdk.ChatAutoArchiveDaysResponse +// @Router /api/v2/chats/config/auto-archive-days [get] +// //nolint:revive // get-return: revive assumes get* must be a getter, but this is an HTTP handler. func (api *API) getChatAutoArchiveDays(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() @@ -5710,6 +5776,15 @@ const autoArchiveDaysMaximum = 3650 // ~10 years // putChatAutoArchiveDays updates the deployment-wide auto-archive // window. Admin-only; documented in docs/ai-coder/agents/chats-api.md. +// +// @Summary Update chat auto archive days +// @ID update-chat-auto-archive-days +// @Security CoderSessionToken +// @Tags Chats +// @Accept json +// @Param request body codersdk.UpdateChatAutoArchiveDaysRequest true "Request body" +// @Success 204 +// @Router /api/v2/chats/config/auto-archive-days [put] func (api *API) putChatAutoArchiveDays(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() aReq, commitAudit := api.initChatOperationalSettingsAudit(rw, r) @@ -5745,7 +5820,13 @@ func (api *API) putChatAutoArchiveDays(rw http.ResponseWriter, r *http.Request) rw.WriteHeader(http.StatusNoContent) } -// EXPERIMENTAL: this endpoint is experimental and is subject to change. +// @Summary Get user chat custom prompt +// @ID get-user-chat-custom-prompt +// @Security CoderSessionToken +// @Tags Chats +// @Produce json +// @Success 200 {object} codersdk.UserChatCustomPrompt +// @Router /api/v2/chats/config/user-prompt [get] // //nolint:revive // get-return: revive assumes get* must be a getter, but this is an HTTP handler. func (api *API) getUserChatCustomPrompt(rw http.ResponseWriter, r *http.Request) { @@ -5772,7 +5853,15 @@ func (api *API) getUserChatCustomPrompt(rw http.ResponseWriter, r *http.Request) }) } -// EXPERIMENTAL: this endpoint is experimental and is subject to change. +// @Summary Update user chat custom prompt +// @ID update-user-chat-custom-prompt +// @Security CoderSessionToken +// @Tags Chats +// @Accept json +// @Param request body codersdk.UserChatCustomPrompt true "Request body" +// @Produce json +// @Success 200 {object} codersdk.UserChatCustomPrompt +// @Router /api/v2/chats/config/user-prompt [put] func (api *API) putUserChatCustomPrompt(rw http.ResponseWriter, r *http.Request) { var ( ctx = r.Context() @@ -5815,8 +5904,13 @@ func (api *API) putUserChatCustomPrompt(rw http.ResponseWriter, r *http.Request) } // @Summary Get user chat compaction thresholds +// @ID get-user-chat-compaction-thresholds +// @Security CoderSessionToken +// @Tags Chats +// @Produce json +// @Success 200 {object} codersdk.UserChatCompactionThresholds +// @Router /api/v2/chats/config/user-compaction-thresholds [get] // @x-apidocgen {"skip": true} -// EXPERIMENTAL: this endpoint is experimental and is subject to change. // //nolint:revive // get-return: revive assumes get* must be a getter, but this is an HTTP handler. func (api *API) getUserChatCompactionThresholds(rw http.ResponseWriter, r *http.Request) { @@ -5875,9 +5969,17 @@ func (api *API) getUserChatCompactionThresholds(rw http.ResponseWriter, r *http. httpapi.Write(ctx, rw, http.StatusOK, resp) } -// @Summary Set user chat compaction threshold for a model config +// @Summary Update user chat compaction threshold +// @ID update-user-chat-compaction-threshold +// @Security CoderSessionToken +// @Tags Chats +// @Param modelConfig path string true "Model config ID" format(uuid) +// @Accept json +// @Param request body codersdk.UpdateUserChatCompactionThresholdRequest true "Request body" +// @Produce json +// @Success 200 {object} codersdk.UserChatCompactionThreshold +// @Router /api/v2/chats/config/user-compaction-thresholds/{modelConfig} [put] // @x-apidocgen {"skip": true} -// EXPERIMENTAL: this endpoint is experimental and is subject to change. func (api *API) putUserChatCompactionThreshold(rw http.ResponseWriter, r *http.Request) { var ( ctx = r.Context() @@ -5948,9 +6050,14 @@ func (api *API) putUserChatCompactionThreshold(rw http.ResponseWriter, r *http.R }) } -// @Summary Delete user chat compaction threshold for a model config +// @Summary Delete user chat compaction threshold +// @ID delete-user-chat-compaction-threshold +// @Security CoderSessionToken +// @Tags Chats +// @Param modelConfig path string true "Model config ID" format(uuid) +// @Success 204 +// @Router /api/v2/chats/config/user-compaction-thresholds/{modelConfig} [delete] // @x-apidocgen {"skip": true} -// EXPERIMENTAL: this endpoint is experimental and is subject to change. func (api *API) deleteUserChatCompactionThreshold(rw http.ResponseWriter, r *http.Request) { var ( ctx = r.Context() @@ -5976,8 +6083,6 @@ func (api *API) deleteUserChatCompactionThreshold(rw http.ResponseWriter, r *htt rw.WriteHeader(http.StatusNoContent) } -// EXPERIMENTAL: this endpoint is experimental and is subject to change. -// // @Summary Upload chat file // @ID upload-chat-file // @Security CoderSessionToken @@ -5985,10 +6090,12 @@ func (api *API) deleteUserChatCompactionThreshold(rw http.ResponseWriter, r *htt // @Accept image/png,image/jpeg,image/gif,image/webp,text/plain,text/markdown,text/csv,application/json,application/pdf // @Produce json // @Param organization query string true "Organization ID" format(uuid) +// @Param Content-Disposition header string true "Attachment disposition carrying the file name" example(attachment; filename="image.png") +// @Param request body string true "Raw file binary data" +// @x-apidocgen {"rawBodyFile": "image.png"} // @Success 201 {object} codersdk.UploadChatFileResponse // @Failure 413 {object} codersdk.Response "Request body exceeds 10 MiB" -// @Router /api/experimental/chats/files [post] -// @Description Experimental: this endpoint is subject to change. +// @Router /api/v2/chats/files [post] func (api *API) postChatFile(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() apiKey := httpmw.APIKey(r) @@ -6138,8 +6245,6 @@ func (c ChatFileDownloadClaims) Validate(expected jwt.Expected) error { return c.RegisteredClaims.Validate(expected) } -// EXPERIMENTAL: this endpoint is experimental and is subject to change. -// // @Summary Create chat file download URL // @ID create-chat-file-download-url // @Security CoderSessionToken @@ -6147,9 +6252,8 @@ func (c ChatFileDownloadClaims) Validate(expected jwt.Expected) error { // @Produce json // @Param file path string true "File ID" format(uuid) // @Success 200 {object} codersdk.ChatFileDownloadURLResponse -// @Router /api/experimental/chats/files/{file}/download-url [post] +// @Router /api/v2/chats/files/{file}/download-url [post] // @x-apidocgen {"skip": true} -// @Description Experimental: this endpoint is subject to change. func (api *API) postChatFileDownloadURL(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() fileID, err := uuid.Parse(chi.URLParam(r, "file")) @@ -6194,6 +6298,7 @@ func (api *API) postChatFileDownloadURL(rw http.ResponseWriter, r *http.Request) return } + // TODO(CODAGT-922): flip to /api/v2 when experimental mounts are removed. downloadURL := api.AccessURL.JoinPath("api", "experimental", "chats", "files", fileID.String(), "download") downloadURL.RawQuery = url.Values{"token": {token}}.Encode() digest := sha256.Sum256(chatFile.Data) @@ -6207,18 +6312,15 @@ func (api *API) postChatFileDownloadURL(rw http.ResponseWriter, r *http.Request) }) } -// EXPERIMENTAL: this endpoint is experimental and is subject to change. -// // @Summary Download chat file with signed token -// @ID download-chat-file +// @ID download-chat-file-with-signed-token // @Tags Chats // @Produce image/png,image/jpeg,image/gif,image/webp,text/plain,text/markdown,text/csv,application/json,application/pdf // @Param file path string true "File ID" format(uuid) // @Param token query string true "Signed download token" // @Success 200 -// @Router /api/experimental/chats/files/{file}/download [get] +// @Router /api/v2/chats/files/{file}/download [get] // @x-apidocgen {"skip": true} -// @Description Experimental: this endpoint is subject to change. func (api *API) downloadChatFile(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() fileID, err := uuid.Parse(chi.URLParam(r, "file")) @@ -6259,8 +6361,6 @@ func (api *API) downloadChatFile(rw http.ResponseWriter, r *http.Request) { api.serveChatFile(ctx, rw, chatFile) } -// EXPERIMENTAL: this endpoint is experimental and is subject to change. -// // @Summary Get chat file // @ID get-chat-file // @Security CoderSessionToken @@ -6268,8 +6368,7 @@ func (api *API) downloadChatFile(rw http.ResponseWriter, r *http.Request) { // @Produce image/png,image/jpeg,image/gif,image/webp,text/plain,text/markdown,text/csv,application/json,application/pdf // @Param file path string true "File ID" format(uuid) // @Success 200 -// @Router /api/experimental/chats/files/{file} [get] -// @Description Experimental: this endpoint is subject to change. +// @Router /api/v2/chats/files/{file} [get] func (api *API) chatFileByID(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() @@ -6526,6 +6625,14 @@ func convertAIProviderSummary(provider database.AIProvider) codersdk.AIProviderS } } +// @Summary List user AI provider key configurations +// @ID list-user-ai-provider-key-configurations +// @Security CoderSessionToken +// @Tags Chats +// @Param user path string true "User ID, username, or me" +// @Produce json +// @Success 200 {array} codersdk.UserAIProviderKeyConfig +// @Router /api/v2/users/{user}/ai-provider-keys [get] func (api *API) listUserAIProviderKeyConfigs(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() targetUser := httpmw.UserParam(r) @@ -6588,6 +6695,17 @@ func (api *API) listUserAIProviderKeyConfigs(rw http.ResponseWriter, r *http.Req httpapi.Write(ctx, rw, http.StatusOK, configs) } +// @Summary Update user AI provider key +// @ID update-user-ai-provider-key +// @Security CoderSessionToken +// @Tags Chats +// @Param user path string true "User ID, username, or me" +// @Param aiProvider path string true "AI provider ID" format(uuid) +// @Accept json +// @Param request body codersdk.CreateUserAIProviderKeyRequest true "Request body" +// @Produce json +// @Success 200 {object} codersdk.UserAIProviderKeyConfig +// @Router /api/v2/users/{user}/ai-provider-keys/{aiProvider} [put] func (api *API) upsertUserAIProviderKey(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() if !api.DeploymentValues.AI.BridgeConfig.AllowBYOK.Value() { @@ -6664,6 +6782,14 @@ func (api *API) upsertUserAIProviderKey(rw http.ResponseWriter, r *http.Request) }) } +// @Summary Delete user AI provider key +// @ID delete-user-ai-provider-key +// @Security CoderSessionToken +// @Tags Chats +// @Param user path string true "User ID, username, or me" +// @Param aiProvider path string true "AI provider ID" format(uuid) +// @Success 204 +// @Router /api/v2/users/{user}/ai-provider-keys/{aiProvider} [delete] func (api *API) deleteUserAIProviderKey(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() targetUser := httpmw.UserParam(r) @@ -6800,13 +6926,13 @@ func (api *API) listDefaultOrganizationChatModelConfigs(rw http.ResponseWriter, } // @Summary List AI models and provider descriptors in an organization -// @ID list-ai-models-by-organization +// @ID list-ai-models-and-provider-descriptors-in-an-organization // @Security CoderSessionToken // @Tags Chats // @Produce json // @Param organization path string true "Organization name or ID" // @Success 200 {object} codersdk.OrganizationChatModelsResponse -// @Router /api/experimental/organizations/{organization}/chats/models [get] +// @Router /api/v2/organizations/{organization}/chats/models [get] // @x-apidocgen {"skip": true} func (api *API) listChatModelConfigsByOrganization(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() @@ -6957,15 +7083,16 @@ func chatModelConfigRBACObject(config database.ChatModelConfig) rbac.Object { // getChatModelConfig returns one chat model config after the organization and // model identities have been resolved by route middleware. +// // @Summary Get an AI model -// @ID get-ai-model +// @ID get-an-ai-model // @Security CoderSessionToken // @Tags Chats // @Produce json // @Param organization path string true "Organization name or ID" -// @Param model path string true "Model ID" +// @Param model path string true "Model ID" format(uuid) // @Success 200 {object} codersdk.ChatModel -// @Router /api/experimental/organizations/{organization}/chats/models/{model} [get] +// @Router /api/v2/organizations/{organization}/chats/models/{model} [get] // @x-apidocgen {"skip": true} // //nolint:revive // get-return: revive assumes get* must be a getter, but this is an HTTP handler. @@ -7061,7 +7188,7 @@ func (api *API) auditChatModelConfigTransitions( } // @Summary Create an AI model in an organization -// @ID create-ai-model +// @ID create-an-ai-model-in-an-organization // @Security CoderSessionToken // @Tags Chats // @Accept json @@ -7069,7 +7196,7 @@ func (api *API) auditChatModelConfigTransitions( // @Param organization path string true "Organization name or ID" // @Param request body codersdk.CreateChatModelRequest true "Model" // @Success 201 {object} codersdk.ChatModel -// @Router /api/experimental/organizations/{organization}/chats/models [post] +// @Router /api/v2/organizations/{organization}/chats/models [post] // @x-apidocgen {"skip": true} func (api *API) createChatModelConfig(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() @@ -7278,16 +7405,16 @@ func (api *API) createChatModelConfig(rw http.ResponseWriter, r *http.Request) { } // @Summary Update an AI model -// @ID update-ai-model +// @ID update-an-ai-model // @Security CoderSessionToken // @Tags Chats // @Accept json // @Produce json // @Param organization path string true "Organization name or ID" -// @Param model path string true "Model ID" +// @Param model path string true "Model ID" format(uuid) // @Param request body codersdk.UpdateChatModelRequest true "Model updates" // @Success 200 {object} codersdk.ChatModel -// @Router /api/experimental/organizations/{organization}/chats/models/{model} [patch] +// @Router /api/v2/organizations/{organization}/chats/models/{model} [patch] // @x-apidocgen {"skip": true} func (api *API) updateChatModelConfig(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() @@ -7534,13 +7661,13 @@ func (api *API) updateChatModelConfig(rw http.ResponseWriter, r *http.Request) { } // @Summary Delete an AI model -// @ID delete-ai-model +// @ID delete-an-ai-model // @Security CoderSessionToken // @Tags Chats // @Param organization path string true "Organization name or ID" -// @Param model path string true "Model ID" +// @Param model path string true "Model ID" format(uuid) // @Success 204 -// @Router /api/experimental/organizations/{organization}/chats/models/{model} [delete] +// @Router /api/v2/organizations/{organization}/chats/models/{model} [delete] // @x-apidocgen {"skip": true} func (api *API) deleteChatModelConfig(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() @@ -7922,7 +8049,15 @@ func ChatProviderAPIKeysFromDeploymentValues( return chatprovider.ProviderAPIKeys{} } -// EXPERIMENTAL: this endpoint is experimental and is subject to change. +// @Summary Submit chat tool results +// @ID submit-chat-tool-results +// @Security CoderSessionToken +// @Tags Chats +// @Param chat path string true "Chat ID" format(uuid) +// @Accept json +// @Param request body codersdk.SubmitToolResultsRequest true "Request body" +// @Success 204 +// @Router /api/v2/chats/{chat}/tool-results [post] // //nolint:revive // HTTP handler writes to ResponseWriter. func (api *API) postChatToolResults(rw http.ResponseWriter, r *http.Request) { @@ -8128,18 +8263,15 @@ func (api *API) getChatDebugRun(rw http.ResponseWriter, r *http.Request) { httpapi.Write(ctx, rw, http.StatusOK, db2sdk.ChatDebugRunDetail(run, steps)) } -// EXPERIMENTAL: this endpoint is experimental and is subject to change. -// // @Summary Stream chat parts via WebSockets // @ID stream-chat-parts-via-websockets // @Security CoderSessionToken // @Tags Chats // @Produce json // @Param chat path string true "Chat ID" format(uuid) -// @Success 200 {object} codersdk.ChatStreamEvent -// @Router /api/experimental/chats/{chat}/stream/parts [get] +// @Success 200 {array} codersdk.ChatStreamEvent +// @Router /api/v2/chats/{chat}/stream/parts [get] // @x-apidocgen {"skip": true} -// @Description Experimental: this endpoint is subject to change. func (api *API) streamChatParts(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() chat := httpmw.ChatParam(r) diff --git a/coderd/exp_chats_acl.go b/coderd/exp_chats_acl.go index 889cd93374..cf0cc8d0d5 100644 --- a/coderd/exp_chats_acl.go +++ b/coderd/exp_chats_acl.go @@ -26,8 +26,6 @@ import ( "github.com/coder/coder/v2/codersdk" ) -// EXPERIMENTAL: this endpoint is experimental and is subject to change. -// // @Summary Get chat ACLs // @ID get-chat-acls // @Security CoderSessionToken @@ -35,9 +33,8 @@ import ( // @Produce json // @Param chat path string true "Chat ID" format(uuid) // @Success 200 {object} codersdk.ChatACL -// @Router /api/experimental/chats/{chat}/acl [get] +// @Router /api/v2/chats/{chat}/acl [get] // @x-apidocgen {"skip": true} -// @Description Experimental: this endpoint is subject to change. // //nolint:revive // get-return: revive assumes get* must be a getter, but this is an HTTP handler. func (api *API) getChatACL(rw http.ResponseWriter, r *http.Request) { @@ -81,8 +78,6 @@ func (api *API) getChatACL(rw http.ResponseWriter, r *http.Request) { }) } -// EXPERIMENTAL: this endpoint is experimental and is subject to change. -// // @Summary Update chat ACL // @ID update-chat-acl // @Security CoderSessionToken @@ -91,9 +86,8 @@ func (api *API) getChatACL(rw http.ResponseWriter, r *http.Request) { // @Param chat path string true "Chat ID" format(uuid) // @Param request body codersdk.UpdateChatACL true "Update chat ACL request" // @Success 204 -// @Router /api/experimental/chats/{chat}/acl [patch] +// @Router /api/v2/chats/{chat}/acl [patch] // @x-apidocgen {"skip": true} -// @Description Experimental: this endpoint is subject to change. func (api *API) patchChatACL(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() chat := httpmw.ChatParam(r) diff --git a/coderd/exp_chats_model_acl.go b/coderd/exp_chats_model_acl.go index de1922d417..6a63757d6d 100644 --- a/coderd/exp_chats_model_acl.go +++ b/coderd/exp_chats_model_acl.go @@ -24,17 +24,15 @@ import ( "github.com/coder/coder/v2/codersdk" ) -// EXPERIMENTAL: this endpoint is experimental and is subject to change. -// // @Summary Get an AI model ACL -// @ID get-ai-model-acl +// @ID get-an-ai-model-acl // @Security CoderSessionToken // @Tags Chats // @Produce json // @Param organization path string true "Organization name or ID" // @Param model path string true "Model ID" format(uuid) // @Success 200 {object} codersdk.ChatModelACL -// @Router /api/experimental/organizations/{organization}/chats/models/{model}/acl [get] +// @Router /api/v2/organizations/{organization}/chats/models/{model}/acl [get] // @x-apidocgen {"skip": true} func (api *API) chatModelConfigACLHandler(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() @@ -55,10 +53,8 @@ func (*chatModelACLValidationError) Error() string { return "invalid chat model ACL" } -// EXPERIMENTAL: this endpoint is experimental and is subject to change. -// // @Summary Update an AI model ACL -// @ID update-ai-model-acl +// @ID update-an-ai-model-acl // @Security CoderSessionToken // @Tags Chats // @Accept json @@ -66,7 +62,7 @@ func (*chatModelACLValidationError) Error() string { // @Param model path string true "Model ID" format(uuid) // @Param request body codersdk.UpdateChatModelACLRequest true "Sparse model ACL update" // @Success 204 -// @Router /api/experimental/organizations/{organization}/chats/models/{model}/acl [patch] +// @Router /api/v2/organizations/{organization}/chats/models/{model}/acl [patch] // @x-apidocgen {"skip": true} func (api *API) updateChatModelConfigACL(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() diff --git a/coderd/httpmw/cors.go b/coderd/httpmw/cors.go index 218aab6609..56d8956fc4 100644 --- a/coderd/httpmw/cors.go +++ b/coderd/httpmw/cors.go @@ -80,6 +80,7 @@ func Cors(allowAll bool, origins ...string) func(next http.Handler) http.Handler // Use permissive CORS for OAuth2, MCP, and well-known endpoints if strings.HasPrefix(r.URL.Path, "/oauth2/") || strings.HasPrefix(r.URL.Path, "/api/experimental/mcp/") || + strings.HasPrefix(r.URL.Path, "/api/v2/mcp/") || strings.HasPrefix(r.URL.Path, "/.well-known/oauth-") { permissiveCors(next).ServeHTTP(w, r) return diff --git a/coderd/httpmw/ratelimit.go b/coderd/httpmw/ratelimit.go index 17af4be242..a36be183da 100644 --- a/coderd/httpmw/ratelimit.go +++ b/coderd/httpmw/ratelimit.go @@ -5,6 +5,7 @@ import ( "net/http" "path" "strconv" + "strings" "sync/atomic" "time" @@ -22,6 +23,16 @@ import ( // RateLimit returns a handler that limits requests per-minute based // on IP, endpoint, and user ID (if available). func RateLimit(count int, window time.Duration) func(http.Handler) http.Handler { + return rateLimitWithEndpointKey(count, window, keyByNormalizedEndpoint) +} + +// RateLimitByAPICompatibilityEndpoint shares rate-limit buckets for matching +// endpoints under the /api/v2 and /api/experimental compatibility prefixes. +func RateLimitByAPICompatibilityEndpoint(count int, window time.Duration) func(http.Handler) http.Handler { + return rateLimitWithEndpointKey(count, window, keyByAPICompatibilityEndpoint) +} + +func rateLimitWithEndpointKey(count int, window time.Duration, endpointKey func(*http.Request) (string, error)) func(http.Handler) http.Handler { // -1 is no rate limit if count <= 0 { return func(handler http.Handler) http.Handler { @@ -86,7 +97,7 @@ func RateLimit(count int, window time.Duration) func(http.Handler) http.Handler "%q provided but user is not %v", codersdk.BypassRatelimitHeader, rbac.RoleOwner(), ) - }, keyByNormalizedEndpoint), + }, endpointKey), httprate.WithLimitHandler(func(w http.ResponseWriter, r *http.Request) { httpapi.Write(r.Context(), w, http.StatusTooManyRequests, codersdk.Response{ Message: fmt.Sprintf("You've been rate limited for sending more than %v requests in %v.", count, window), @@ -110,6 +121,22 @@ func keyByNormalizedEndpoint(r *http.Request) (string, error) { return path.Clean(p), nil } +func keyByAPICompatibilityEndpoint(r *http.Request) (string, error) { + p, err := keyByNormalizedEndpoint(r) + if err != nil { + return "", err + } + for _, prefix := range []string{"/api/v2", "/api/experimental"} { + if p == prefix { + return "/", nil + } + if strings.HasPrefix(p, prefix+"/") { + return strings.TrimPrefix(p, prefix), nil + } + } + return p, nil +} + // RateLimitByAuthToken returns a handler that limits requests based on the // authentication token in the request. // diff --git a/coderd/httpmw/ratelimit_test.go b/coderd/httpmw/ratelimit_test.go index c6122685f8..8acb2fbd44 100644 --- a/coderd/httpmw/ratelimit_test.go +++ b/coderd/httpmw/ratelimit_test.go @@ -79,6 +79,29 @@ func TestRateLimit(t *testing.T) { } }) + t.Run("DifferentAPIPrefixes", func(t *testing.T) { + t.Parallel() + rtr := chi.NewRouter() + rtr.Use(httpmw.RateLimit(1, time.Second)) + rtr.Get("/*", func(rw http.ResponseWriter, r *http.Request) { + rw.WriteHeader(http.StatusOK) + }) + + remoteAddr := randRemoteAddr() + for _, p := range []string{ + "/api/v2/chats/providers", + "/api/experimental/chats/providers", + } { + req := httptest.NewRequest("GET", p, nil) + req.RemoteAddr = remoteAddr + rec := httptest.NewRecorder() + rtr.ServeHTTP(rec, req) + resp := rec.Result() + _ = resp.Body.Close() + require.Equal(t, http.StatusOK, resp.StatusCode, p) + } + }) + t.Run("RandomIPs", func(t *testing.T) { t.Parallel() rtr := chi.NewRouter() @@ -178,6 +201,30 @@ func TestRateLimit(t *testing.T) { }) } +func TestRateLimitByAPICompatibilityEndpoint(t *testing.T) { + t.Parallel() + + rtr := chi.NewRouter() + rtr.Use(httpmw.RateLimitByAPICompatibilityEndpoint(1, time.Second)) + rtr.Get("/*", func(rw http.ResponseWriter, r *http.Request) { + rw.WriteHeader(http.StatusOK) + }) + + remoteAddr := randRemoteAddr() + for i, p := range []string{ + "/api/v2/chats/files/00000000-0000-0000-0000-000000000000", + "/api/experimental/chats/files/00000000-0000-0000-0000-000000000000", + } { + req := httptest.NewRequest("GET", p, nil) + req.RemoteAddr = remoteAddr + rec := httptest.NewRecorder() + rtr.ServeHTTP(rec, req) + resp := rec.Result() + _ = resp.Body.Close() + require.Equal(t, i != 0, resp.StatusCode == http.StatusTooManyRequests, p) + } +} + func TestRateLimitByAuthToken(t *testing.T) { t.Parallel() diff --git a/coderd/mcp.go b/coderd/mcp.go index 7ba73157c5..a1b297bfd3 100644 --- a/coderd/mcp.go +++ b/coderd/mcp.go @@ -145,11 +145,10 @@ func shouldRefreshOIDCToken(link database.UserLink) (bool, time.Time) { // @Security CoderSessionToken // @Tags MCP // @Produce json -// @Param organization path string true "Organization ID" format(uuid) +// @Param organization path string true "Organization name or ID" // @Success 200 {array} codersdk.MCPServerConfig -// @Router /api/experimental/organizations/{organization}/mcp-servers [get] +// @Router /api/v2/organizations/{organization}/mcp-servers [get] // @x-apidocgen {"skip": true} -// EXPERIMENTAL: this endpoint is experimental and is subject to change. // //nolint:revive // HTTP handler writes to ResponseWriter. func (api *API) listMCPServerConfigs(rw http.ResponseWriter, r *http.Request) { @@ -286,12 +285,11 @@ func (api *API) mcpServerConfigReadInKeyScope(r *http.Request, organizationID uu // @Tags MCP // @Accept json // @Produce json -// @Param organization path string true "Organization ID" format(uuid) +// @Param organization path string true "Organization name or ID" // @Param request body codersdk.CreateMCPServerConfigRequest true "Create MCP server config request" // @Success 201 {object} codersdk.MCPServerConfig -// @Router /api/experimental/organizations/{organization}/mcp-servers [post] +// @Router /api/v2/organizations/{organization}/mcp-servers [post] // @x-apidocgen {"skip": true} -// EXPERIMENTAL: this endpoint is experimental and is subject to change. // //nolint:revive // HTTP handler writes to ResponseWriter. func (api *API) createMCPServerConfig(rw http.ResponseWriter, r *http.Request) { @@ -514,12 +512,11 @@ func (api *API) createMCPServerConfig(rw http.ResponseWriter, r *http.Request) { // @Security CoderSessionToken // @Tags MCP // @Produce json -// @Param organization path string true "Organization ID" format(uuid) +// @Param organization path string true "Organization name or ID" // @Param mcpserverconfig path string true "MCP server config ID" format(uuid) // @Success 200 {object} codersdk.MCPServerConfig -// @Router /api/experimental/organizations/{organization}/mcp-servers/{mcpserverconfig} [get] +// @Router /api/v2/organizations/{organization}/mcp-servers/{mcpserverconfig} [get] // @x-apidocgen {"skip": true} -// EXPERIMENTAL: this endpoint is experimental and is subject to change. // //nolint:revive // HTTP handler writes to ResponseWriter. func (api *API) getMCPServerConfig(rw http.ResponseWriter, r *http.Request) { @@ -602,13 +599,12 @@ func (api *API) getMCPServerConfigForMutation(rw http.ResponseWriter, r *http.Re // @Tags MCP // @Accept json // @Produce json -// @Param organization path string true "Organization ID" format(uuid) +// @Param organization path string true "Organization name or ID" // @Param mcpserverconfig path string true "MCP server config ID" format(uuid) // @Param request body codersdk.UpdateMCPServerConfigRequest true "Update MCP server config request" // @Success 200 {object} codersdk.MCPServerConfig -// @Router /api/experimental/organizations/{organization}/mcp-servers/{mcpserverconfig} [patch] +// @Router /api/v2/organizations/{organization}/mcp-servers/{mcpserverconfig} [patch] // @x-apidocgen {"skip": true} -// EXPERIMENTAL: this endpoint is experimental and is subject to change. // //nolint:revive // HTTP handler writes to ResponseWriter. func (api *API) updateMCPServerConfig(rw http.ResponseWriter, r *http.Request) { @@ -968,12 +964,11 @@ func (api *API) updateMCPServerConfig(rw http.ResponseWriter, r *http.Request) { // @ID delete-mcp-server-config // @Security CoderSessionToken // @Tags MCP -// @Param organization path string true "Organization ID" format(uuid) +// @Param organization path string true "Organization name or ID" // @Param mcpserverconfig path string true "MCP server config ID" format(uuid) // @Success 204 -// @Router /api/experimental/organizations/{organization}/mcp-servers/{mcpserverconfig} [delete] +// @Router /api/v2/organizations/{organization}/mcp-servers/{mcpserverconfig} [delete] // @x-apidocgen {"skip": true} -// EXPERIMENTAL: this endpoint is experimental and is subject to change. func (api *API) deleteMCPServerConfig(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() auditor := api.Auditor.Load() @@ -1022,17 +1017,17 @@ func (api *API) deleteMCPServerConfig(rw http.ResponseWriter, r *http.Request) { rw.WriteHeader(http.StatusNoContent) } +// Redirects the user to the MCP server's OAuth2 authorization URL. +// // @Summary Initiate MCP server OAuth2 connect // @ID initiate-mcp-server-oauth2-connect // @Security CoderSessionToken // @Tags MCP -// @Param organization path string true "Organization ID" format(uuid) +// @Param organization path string true "Organization name or ID" // @Param mcpserverconfig path string true "MCP server config ID" format(uuid) // @Success 307 -// @Router /api/experimental/organizations/{organization}/mcp-servers/{mcpserverconfig}/oauth2/connect [get] +// @Router /api/v2/organizations/{organization}/mcp-servers/{mcpserverconfig}/oauth2/connect [get] // @x-apidocgen {"skip": true} -// EXPERIMENTAL: this endpoint is experimental and is subject to change. -// Redirects the user to the MCP server's OAuth2 authorization URL. // //nolint:revive // HTTP handler writes to ResponseWriter. func (api *API) mcpServerOAuth2Connect(rw http.ResponseWriter, r *http.Request) { @@ -1111,21 +1106,21 @@ func (api *API) mcpServerOAuth2Connect(rw http.ResponseWriter, r *http.Request) http.Redirect(rw, r, authURL, http.StatusTemporaryRedirect) } +// Exchanges the authorization code for tokens and stores them. +// // @Summary Handle MCP server OAuth2 callback // @ID handle-mcp-server-oauth2-callback // @Security CoderSessionToken // @Tags MCP -// @Produce html // @Param mcpServer path string true "MCP server config ID" format(uuid) // @Param code query string false "Authorization code issued by the provider. Required together with state on success." // @Param state query string false "Opaque state issued by the connect endpoint. Required together with code on success." // @Param error query string false "Provider error code. Present instead of code when authorization fails." // @Param error_description query string false "Provider error description accompanying error." +// @Produce text/html // @Success 200 // @Router /api/experimental/mcp/servers/{mcpServer}/oauth2/callback [get] // @x-apidocgen {"skip": true} -// EXPERIMENTAL: this endpoint is experimental and is subject to change. -// Exchanges the authorization code for tokens and stores them. // //nolint:revive // HTTP handler writes to ResponseWriter. func (api *API) mcpServerOAuth2Callback(rw http.ResponseWriter, r *http.Request) { @@ -1326,6 +1321,9 @@ func (api *API) mcpServerOAuth2Callback(rw http.ResponseWriter, r *http.Request) `)) } +// Removes the user's stored OAuth2 token for an MCP server. +// Provider revocation is best-effort and cannot block local deletion. +// // @Summary Disconnect MCP server OAuth2 token // @ID disconnect-mcp-server-oauth2-token // @Security CoderSessionToken @@ -1333,11 +1331,8 @@ func (api *API) mcpServerOAuth2Callback(rw http.ResponseWriter, r *http.Request) // @Produce json // @Param mcpServer path string true "MCP server config ID" format(uuid) // @Success 200 {object} codersdk.MCPServerOAuth2DisconnectResponse -// @Router /api/experimental/mcp/servers/{mcpServer}/oauth2/disconnect [delete] +// @Router /api/v2/mcp/servers/{mcpServer}/oauth2/disconnect [delete] // @x-apidocgen {"skip": true} -// EXPERIMENTAL: this endpoint is experimental and is subject to change. -// Removes the user's stored OAuth2 token for an MCP server. -// Provider revocation is best-effort and cannot block local deletion. func (api *API) mcpServerOAuth2Disconnect(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() apiKey := httpmw.APIKey(r) @@ -1569,6 +1564,8 @@ func (api *API) markMCPTokenRefreshFailure( // external authorization servers, so it must not change when other MCP // routes move. The route registration in coderd.go and the OAuth cookie // Path values must stay aligned with it. +// TODO(CODAGT-922): define a migration story before moving registered +// redirect URIs to /api/v2. func mcpServerOAuth2CallbackPath(configID uuid.UUID) string { return fmt.Sprintf("/api/experimental/mcp/servers/%s/oauth2/callback", configID) } diff --git a/coderd/mcp_acl.go b/coderd/mcp_acl.go index 0227c15cb1..6b793ed04c 100644 --- a/coderd/mcp_acl.go +++ b/coderd/mcp_acl.go @@ -21,17 +21,15 @@ import ( "github.com/coder/coder/v2/codersdk" ) -// EXPERIMENTAL: this endpoint is experimental and is subject to change. -// // @Summary Get MCP server config ACL // @ID get-mcp-server-config-acl // @Security CoderSessionToken // @Tags MCP // @Produce json -// @Param organization path string true "Organization ID" format(uuid) +// @Param organization path string true "Organization name or ID" // @Param mcpserverconfig path string true "MCP server config ID" format(uuid) // @Success 200 {object} codersdk.MCPServerConfigACL -// @Router /api/experimental/organizations/{organization}/mcp-servers/{mcpserverconfig}/acl [get] +// @Router /api/v2/organizations/{organization}/mcp-servers/{mcpserverconfig}/acl [get] // @x-apidocgen {"skip": true} func (api *API) mcpServerConfigACL(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() @@ -58,18 +56,16 @@ func (api *API) mcpServerConfigACL(rw http.ResponseWriter, r *http.Request) { }) } -// EXPERIMENTAL: this endpoint is experimental and is subject to change. -// // @Summary Update MCP server config ACL // @ID update-mcp-server-config-acl // @Security CoderSessionToken // @Tags MCP // @Accept json -// @Param organization path string true "Organization ID" format(uuid) +// @Param organization path string true "Organization name or ID" // @Param mcpserverconfig path string true "MCP server config ID" format(uuid) // @Param request body codersdk.UpdateMCPServerConfigACLRequest true "Update MCP server config ACL request" // @Success 204 -// @Router /api/experimental/organizations/{organization}/mcp-servers/{mcpserverconfig}/acl [patch] +// @Router /api/v2/organizations/{organization}/mcp-servers/{mcpserverconfig}/acl [patch] // @x-apidocgen {"skip": true} func (api *API) patchMCPServerConfigACL(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() diff --git a/coderd/x/chatd/ARCHITECTURE.md b/coderd/x/chatd/ARCHITECTURE.md index d91f91d4f0..2ce7b68f64 100644 --- a/coderd/x/chatd/ARCHITECTURE.md +++ b/coderd/x/chatd/ARCHITECTURE.md @@ -1262,13 +1262,13 @@ WHERE id = ANY($1::uuid[]); We make use of a relay mechanism when there are multiple coderd replicas. If a client connects to the stream endpoint on replica A, but the chat worker that owns the chat is on replica B, the endpoint will connect to replica B and relay streaming message parts. -There exists a `GET /api/experimental/chats/{chat}/stream/parts` endpoint that is responsible exclusively for streaming message parts. That endpoint talks to the chat worker on the same replica to obtain the message parts and relay them to the client. +There exists a `GET /api/v2/chats/{chat}/stream/parts` endpoint that is responsible exclusively for streaming message parts. That endpoint talks to the chat worker on the same replica to obtain the message parts and relay them to the client. The flow is: -1. Client connects to the `GET /api/experimental/chats/{chat}/stream` endpoint. +1. Client connects to the `GET /api/v2/chats/{chat}/stream` endpoint. 2. The endpoint checks the database to see which replica owns the chat and resolves the replica's address. -3. The endpoint connects to the `GET /api/experimental/chats/{chat}/stream/parts` endpoint on that replica. +3. The endpoint connects to the `GET /api/v2/chats/{chat}/stream/parts` endpoint on that replica. 4. The stream endpoint relays both the full chat state and the streaming message parts to the client. Some edge cases: @@ -1298,7 +1298,7 @@ The parts endpoint is a WebSocket endpoint. Connection setup: -- the URL identifies the chat ID, for example `GET /api/experimental/chats/{chat}/stream/parts`; +- the URL identifies the chat ID, for example `GET /api/v2/chats/{chat}/stream/parts`; - the endpoint accepts the connection regardless of whether the local replica owns the chat; - after connecting, the client sends control messages over the WebSocket to choose which episode it wants. diff --git a/codersdk/chats.go b/codersdk/chats.go index b1c05172b7..bb14b1462a 100644 --- a/codersdk/chats.go +++ b/codersdk/chats.go @@ -1121,7 +1121,7 @@ type ChatDebugStep struct { } // DefaultChatWorkspaceTTL is the default TTL for chat workspaces. -// Zero means disabled — the template's own autostop setting applies. +// Zero means disabled; the template's own autostop setting applies. const DefaultChatWorkspaceTTL = 0 // DefaultChatAutoArchiveDays is the default auto-archive window, in @@ -1138,7 +1138,7 @@ const DefaultChatDebugRetentionDays int32 = 30 // workspace TTL setting. type ChatWorkspaceTTLResponse struct { // WorkspaceTTLMillis is the workspace TTL in milliseconds. - // Zero means disabled — the template's own autostop setting applies. + // Zero means disabled; the template's own autostop setting applies. WorkspaceTTLMillis int64 `json:"workspace_ttl_ms"` } @@ -1146,7 +1146,7 @@ type ChatWorkspaceTTLResponse struct { // workspace TTL setting. type UpdateChatWorkspaceTTLRequest struct { // WorkspaceTTLMillis is the workspace TTL in milliseconds. - // Zero means disabled — the template's own autostop setting applies. + // Zero means disabled; the template's own autostop setting applies. WorkspaceTTLMillis int64 `json:"workspace_ttl_ms"` } @@ -1817,7 +1817,7 @@ type DynamicTool struct { InputSchema json.RawMessage `json:"input_schema"` // Handler executes the tool when the LLM invokes it. - // Not serialized — this only exists on the client side. + // Not serialized; this only exists on the client side. Handler func(ctx context.Context, call DynamicToolCall) (DynamicToolResponse, error) `json:"-"` } diff --git a/docs/ai-coder/agents/models.md b/docs/ai-coder/agents/models.md index a3a8b17c41..051220b1b4 100644 --- a/docs/ai-coder/agents/models.md +++ b/docs/ai-coder/agents/models.md @@ -363,9 +363,9 @@ reject explicit model selection. > [!NOTE] > Both override layers may change between releases. > Admin overrides are available through the API at -> `/api/experimental/organizations/{organization}/chats/model-overrides` +> `/api/v2/organizations/{organization}/chats/model-overrides` > and personal overrides at -> `/api/experimental/organizations/{organization}/members/{user}/chats/model-overrides`. +> `/api/v2/organizations/{organization}/members/{user}/chats/model-overrides`. ## User API keys (BYOK) diff --git a/docs/ai-coder/agents/platform-controls/mcp-servers.md b/docs/ai-coder/agents/platform-controls/mcp-servers.md index e74985686f..c74b169531 100644 --- a/docs/ai-coder/agents/platform-controls/mcp-servers.md +++ b/docs/ai-coder/agents/platform-controls/mcp-servers.md @@ -199,7 +199,7 @@ Each server has a group and user ACL that controls which members can see and use it. New servers grant read access to the organization's **Everyone** group, so all members have access by default. Admins can remove the Everyone entry and grant specific groups or users instead through the API -(`GET`/`PATCH /api/experimental/organizations/{organization}/mcp-servers/{id}/acl`); there is no ACL editor +(`GET`/`PATCH /api/v2/organizations/{organization}/mcp-servers/{id}/acl`); there is no ACL editor in the settings page. ACL management is available in all editions and does not require an enterprise entitlement. ACL changes are recorded in the audit log. diff --git a/docs/ai-coder/agents/platform-controls/organizations.md b/docs/ai-coder/agents/platform-controls/organizations.md index 48a2ada5a7..57d5033819 100644 --- a/docs/ai-coder/agents/platform-controls/organizations.md +++ b/docs/ai-coder/agents/platform-controls/organizations.md @@ -102,8 +102,8 @@ Refer to [Manage model permissions](../models.md#manage-model-permissions) for t The **MCP servers** page has no access list editor. Change an MCP server access list through the API instead: -- `GET /api/experimental/organizations/{organization}/mcp-servers/{mcpserverconfig}/acl` -- `PATCH /api/experimental/organizations/{organization}/mcp-servers/{mcpserverconfig}/acl` +- `GET /api/v2/organizations/{organization}/mcp-servers/{mcpserverconfig}/acl` +- `PATCH /api/v2/organizations/{organization}/mcp-servers/{mcpserverconfig}/acl` ## Related pages diff --git a/docs/ai-coder/agents/tasks-to-chats-migration.md b/docs/ai-coder/agents/tasks-to-chats-migration.md index 9f9de00024..597410837e 100644 --- a/docs/ai-coder/agents/tasks-to-chats-migration.md +++ b/docs/ai-coder/agents/tasks-to-chats-migration.md @@ -47,21 +47,21 @@ Before mapping individual endpoints, understand the structural changes: The table below maps each Tasks API endpoint to its Chats API equivalent. -| Operation | Tasks API | Chats API | -|-------------------|-------------------------------------------|-------------------------------------------------------------------| -| List | `GET /api/v2/tasks` | `GET /api/v2/chats` | -| Create | `POST /api/v2/tasks/{user}` | `POST /api/v2/chats` | -| Get by ID | `GET /api/v2/tasks/{user}/{task}` | `GET /api/v2/chats/{chat}` | -| Delete | `DELETE /api/v2/tasks/{user}/{task}` | `PATCH /api/v2/chats/{chat}` with `{"archived": true}` | -| Send follow-up | `POST /api/v2/tasks/{user}/{task}/send` | `POST /api/v2/chats/{chat}/messages` | -| Update input | `PATCH /api/v2/tasks/{user}/{task}/input` | `PATCH /api/v2/chats/{chat}/messages/{message}` | -| Get logs / stream | `GET /api/v2/tasks/{user}/{task}/logs` | `GET /api/v2/chats/{chat}/stream` (WebSocket) | -| Pause | `POST /api/v2/tasks/{user}/{task}/pause` | `POST /api/v2/chats/{chat}/interrupt` | -| Resume | `POST /api/v2/tasks/{user}/{task}/resume` | `POST /api/v2/chats/{chat}/messages` (send a new message) | -| Watch all | n/a | `GET /api/v2/chats/watch` (WebSocket) | -| Get messages | n/a | `GET /api/v2/chats/{chat}/messages` | -| List models | n/a | `GET /api/experimental/organizations/{organization}/chats/models` | -| Upload file | n/a | `POST /api/v2/chats/files` | +| Operation | Tasks API | Chats API | +|-------------------|-------------------------------------------|-----------------------------------------------------------| +| List | `GET /api/v2/tasks` | `GET /api/v2/chats` | +| Create | `POST /api/v2/tasks/{user}` | `POST /api/v2/chats` | +| Get by ID | `GET /api/v2/tasks/{user}/{task}` | `GET /api/v2/chats/{chat}` | +| Delete | `DELETE /api/v2/tasks/{user}/{task}` | `PATCH /api/v2/chats/{chat}` with `{"archived": true}` | +| Send follow-up | `POST /api/v2/tasks/{user}/{task}/send` | `POST /api/v2/chats/{chat}/messages` | +| Update input | `PATCH /api/v2/tasks/{user}/{task}/input` | `PATCH /api/v2/chats/{chat}/messages/{message}` | +| Get logs / stream | `GET /api/v2/tasks/{user}/{task}/logs` | `GET /api/v2/chats/{chat}/stream` (WebSocket) | +| Pause | `POST /api/v2/tasks/{user}/{task}/pause` | `POST /api/v2/chats/{chat}/interrupt` | +| Resume | `POST /api/v2/tasks/{user}/{task}/resume` | `POST /api/v2/chats/{chat}/messages` (send a new message) | +| Watch all | n/a | `GET /api/v2/chats/watch` (WebSocket) | +| Get messages | n/a | `GET /api/v2/chats/{chat}/messages` | +| List models | n/a | `GET /api/v2/organizations/{organization}/chats/models` | +| Upload file | n/a | `POST /api/v2/chats/files` | ## Migration steps @@ -513,7 +513,7 @@ confirm the Chats API integration is working end-to-end. List the available models in an organization to verify at least one provider is configured and reachable: ```sh -curl -s https://coder.example.com/api/experimental/organizations/$CODER_ORGANIZATION/chats/models \ +curl -s https://coder.example.com/api/v2/organizations/$CODER_ORGANIZATION/chats/models \ -H "Coder-Session-Token: $CODER_SESSION_TOKEN" | jq '.models[].display_name' ``` diff --git a/docs/reference/api/chats.md b/docs/reference/api/chats.md index 4c78079249..7b331c5db9 100644 --- a/docs/reference/api/chats.md +++ b/docs/reference/api/chats.md @@ -10,27 +10,57 @@ state: Programmatic API for Coder Agents (the user-facing "Coder Agents" / "Chats" product). Use these endpoints to create, list, and manage AI coding agent sessions. +## Connect to chat workspace desktop via WebSockets + +### Code samples + +```sh +# Example request using curl +curl -X GET http://coder-server:8080/api/experimental/chats/{chat}/stream/desktop \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /api/experimental/chats/{chat}/stream/desktop` + +Raw binary WebSocket stream of the chat workspace desktop. +Experimental: this endpoint is subject to change. + +### Parameters + +| Name | In | Type | Required | Description | +|--------|------|--------------|----------|-------------| +| `chat` | path | string(uuid) | true | Chat ID | + +### Responses + +| Status | Meaning | Description | Schema | +|--------|--------------------------------------------------------------------------|---------------------|--------| +| 101 | [Switching Protocols](https://tools.ietf.org/html/rfc7231#section-6.2.2) | Switching Protocols | | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + ## List chats ### Code samples ```sh # Example request using curl -curl -X GET http://coder-server:8080/api/experimental/chats \ +curl -X GET http://coder-server:8080/api/v2/chats \ -H 'Accept: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`GET /api/experimental/chats` - -Experimental: this endpoint is subject to change. +`GET /api/v2/chats` ### Parameters -| Name | In | Type | Required | Description | -|---------|-------|--------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `q` | query | string | false | Search query. Supports `title:` (case-insensitive, quote multi-word values), `archived:bool`, `has_unread:bool`, `pr_status:` as repeated or comma-separated values, `source:`, `diff_url:` (quote values containing colons), `pr:` (exact PR number match), `repo:` (case-insensitive substring match against git remote origin or URL), `pr_title:` (case-insensitive PR title substring), `search:` (full-text search across chat titles, PR titles, PR numbers, and message bodies; message bodies match English word stems, e.g. `refactor` matches `refactoring`, and ignore English stopwords; titles and PR titles match whole words case-insensitively without stemming; quote multi-word values; cannot be combined with title, pr_title, or pr; a value that tokenizes to no searchable words, e.g. punctuation only, returns an empty list). Bare terms are not supported; use `title:` or `search:`. | -| `label` | query | string | false | Filter by label as key:value. Repeat for multiple (AND logic). | +| Name | In | Type | Required | Description | +|------------|-------|---------------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `q` | query | string | false | Search query. Supports `title:` (case-insensitive, quote multi-word values), `archived:bool`, `has_unread:bool`, `pr_status:` as repeated or comma-separated values, `source:`, `diff_url:` (quote values containing colons), `pr:` (exact PR number match), `repo:` (case-insensitive substring match against git remote origin or URL), `pr_title:` (case-insensitive PR title substring), `search:` (full-text search across chat titles, PR titles, PR numbers, and message bodies; message bodies match English word stems, e.g. `refactor` matches `refactoring`, and ignore English stopwords; titles and PR titles match whole words case-insensitively without stemming; quote multi-word values; cannot be combined with title, pr_title, or pr; a value that tokenizes to no searchable words, e.g. punctuation only, returns an empty list). Bare terms are not supported; use `title:` or `search:`. | +| `label` | query | array[string] | false | Filter by label as key:value. Repeat for multiple (AND logic). | +| `after_id` | query | string(uuid) | false | After ID | +| `limit` | query | integer | false | Page limit | +| `offset` | query | integer | false | Page offset | ### Example responses @@ -254,15 +284,13 @@ To perform this operation, you must be authenticated. [Learn more](authenticatio ```sh # Example request using curl -curl -X POST http://coder-server:8080/api/experimental/chats \ +curl -X POST http://coder-server:8080/api/v2/chats \ -H 'Content-Type: application/json' \ -H 'Accept: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`POST /api/experimental/chats` - -Experimental: this endpoint is subject to change. +`POST /api/v2/chats` > Body parameter @@ -528,26 +556,678 @@ Experimental: this endpoint is subject to change. To perform this operation, you must be authenticated. [Learn more](authentication.md). +## List chats by workspace + +### Code samples + +```sh +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/chats/by-workspace \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /api/v2/chats/by-workspace` + +### Parameters + +| Name | In | Type | Required | Description | +|-----------------|-------|--------|----------|-------------------------------| +| `workspace_ids` | query | string | false | Comma-separated workspace IDs | + +### Example responses + +> 200 Response + +```json +{ + "property1": "string", + "property2": "string" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +|--------|---------------------------------------------------------|-------------|------------------------------------------------------------------------------| +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [coderd.chatsByWorkspaceResponse](schemas.md#coderdchatsbyworkspaceresponse) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Get chat auto archive days + +### Code samples + +```sh +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/chats/config/auto-archive-days \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /api/v2/chats/config/auto-archive-days` + +### Example responses + +> 200 Response + +```json +{ + "auto_archive_days": 0 +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +|--------|---------------------------------------------------------|-------------|----------------------------------------------------------------------------------------| +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.ChatAutoArchiveDaysResponse](schemas.md#codersdkchatautoarchivedaysresponse) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Update chat auto archive days + +### Code samples + +```sh +# Example request using curl +curl -X PUT http://coder-server:8080/api/v2/chats/config/auto-archive-days \ + -H 'Content-Type: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`PUT /api/v2/chats/config/auto-archive-days` + +> Body parameter + +```json +{ + "auto_archive_days": 0 +} +``` + +### Parameters + +| Name | In | Type | Required | Description | +|--------|------|--------------------------------------------------------------------------------------------------|----------|--------------| +| `body` | body | [codersdk.UpdateChatAutoArchiveDaysRequest](schemas.md#codersdkupdatechatautoarchivedaysrequest) | true | Request body | + +### Responses + +| Status | Meaning | Description | Schema | +|--------|-----------------------------------------------------------------|-------------|--------| +| 204 | [No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5) | No Content | | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Get chat debug logging setting + +### Code samples + +```sh +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/chats/config/debug-logging \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /api/v2/chats/config/debug-logging` + +### Example responses + +> 200 Response + +```json +{ + "allow_users": true, + "forced_by_deployment": true +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +|--------|---------------------------------------------------------|-------------|--------------------------------------------------------------------------------------------| +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.ChatDebugLoggingAdminSettings](schemas.md#codersdkchatdebugloggingadminsettings) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Update chat debug logging setting + +### Code samples + +```sh +# Example request using curl +curl -X PUT http://coder-server:8080/api/v2/chats/config/debug-logging \ + -H 'Content-Type: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`PUT /api/v2/chats/config/debug-logging` + +> Body parameter + +```json +{ + "allow_users": true +} +``` + +### Parameters + +| Name | In | Type | Required | Description | +|--------|------|----------------------------------------------------------------------------------------------------------------|----------|--------------| +| `body` | body | [codersdk.UpdateChatDebugLoggingAllowUsersRequest](schemas.md#codersdkupdatechatdebugloggingallowusersrequest) | true | Request body | + +### Responses + +| Status | Meaning | Description | Schema | +|--------|-----------------------------------------------------------------|-------------|--------| +| 204 | [No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5) | No Content | | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Get chat debug retention days + +### Code samples + +```sh +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/chats/config/debug-retention-days \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /api/v2/chats/config/debug-retention-days` + +### Example responses + +> 200 Response + +```json +{ + "debug_retention_days": 0 +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +|--------|---------------------------------------------------------|-------------|----------------------------------------------------------------------------------------------| +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.ChatDebugRetentionDaysResponse](schemas.md#codersdkchatdebugretentiondaysresponse) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Update chat debug retention days + +### Code samples + +```sh +# Example request using curl +curl -X PUT http://coder-server:8080/api/v2/chats/config/debug-retention-days \ + -H 'Content-Type: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`PUT /api/v2/chats/config/debug-retention-days` + +> Body parameter + +```json +{ + "debug_retention_days": 0 +} +``` + +### Parameters + +| Name | In | Type | Required | Description | +|--------|------|--------------------------------------------------------------------------------------------------------|----------|--------------| +| `body` | body | [codersdk.UpdateChatDebugRetentionDaysRequest](schemas.md#codersdkupdatechatdebugretentiondaysrequest) | true | Request body | + +### Responses + +| Status | Meaning | Description | Schema | +|--------|-----------------------------------------------------------------|-------------|--------| +| 204 | [No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5) | No Content | | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Get chat personal model override settings + +### Code samples + +```sh +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/chats/config/personal-model-overrides \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /api/v2/chats/config/personal-model-overrides` + +### Example responses + +> 200 Response + +```json +{ + "allow_users": true +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +|--------|---------------------------------------------------------|-------------|----------------------------------------------------------------------------------------------------------------| +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.ChatPersonalModelOverridesAdminSettings](schemas.md#codersdkchatpersonalmodeloverridesadminsettings) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Update chat personal model override settings + +### Code samples + +```sh +# Example request using curl +curl -X PUT http://coder-server:8080/api/v2/chats/config/personal-model-overrides \ + -H 'Content-Type: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`PUT /api/v2/chats/config/personal-model-overrides` + +> Body parameter + +```json +{ + "allow_users": true +} +``` + +### Parameters + +| Name | In | Type | Required | Description | +|--------|------|------------------------------------------------------------------------------------------------------------------------------------------|----------|--------------| +| `body` | body | [codersdk.UpdateChatPersonalModelOverridesAdminSettingsRequest](schemas.md#codersdkupdatechatpersonalmodeloverridesadminsettingsrequest) | true | Request body | + +### Responses + +| Status | Meaning | Description | Schema | +|--------|-----------------------------------------------------------------|-------------|--------| +| 204 | [No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5) | No Content | | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Get chat plan mode instructions + +### Code samples + +```sh +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/chats/config/plan-mode-instructions \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /api/v2/chats/config/plan-mode-instructions` + +### Example responses + +> 200 Response + +```json +{ + "plan_mode_instructions": "string" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +|--------|---------------------------------------------------------|-------------|--------------------------------------------------------------------------------------------------| +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.ChatPlanModeInstructionsResponse](schemas.md#codersdkchatplanmodeinstructionsresponse) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Update chat plan mode instructions + +### Code samples + +```sh +# Example request using curl +curl -X PUT http://coder-server:8080/api/v2/chats/config/plan-mode-instructions \ + -H 'Content-Type: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`PUT /api/v2/chats/config/plan-mode-instructions` + +> Body parameter + +```json +{ + "plan_mode_instructions": "string" +} +``` + +### Parameters + +| Name | In | Type | Required | Description | +|--------|------|------------------------------------------------------------------------------------------------------------|----------|--------------| +| `body` | body | [codersdk.UpdateChatPlanModeInstructionsRequest](schemas.md#codersdkupdatechatplanmodeinstructionsrequest) | true | Request body | + +### Responses + +| Status | Meaning | Description | Schema | +|--------|-----------------------------------------------------------------|-------------|--------| +| 204 | [No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5) | No Content | | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Get chat system prompt + +### Code samples + +```sh +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/chats/config/system-prompt \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /api/v2/chats/config/system-prompt` + +### Example responses + +> 200 Response + +```json +{ + "default_system_prompt": "string", + "include_default_system_prompt": true, + "system_prompt": "string" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +|--------|---------------------------------------------------------|-------------|----------------------------------------------------------------------------------| +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.ChatSystemPromptResponse](schemas.md#codersdkchatsystempromptresponse) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Update chat system prompt + +### Code samples + +```sh +# Example request using curl +curl -X PUT http://coder-server:8080/api/v2/chats/config/system-prompt \ + -H 'Content-Type: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`PUT /api/v2/chats/config/system-prompt` + +> Body parameter + +```json +{ + "include_default_system_prompt": true, + "system_prompt": "string" +} +``` + +### Parameters + +| Name | In | Type | Required | Description | +|--------|------|--------------------------------------------------------------------------------------------|----------|--------------| +| `body` | body | [codersdk.UpdateChatSystemPromptRequest](schemas.md#codersdkupdatechatsystempromptrequest) | true | Request body | + +### Responses + +| Status | Meaning | Description | Schema | +|--------|-----------------------------------------------------------------|-------------|--------| +| 204 | [No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5) | No Content | | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Get user chat debug logging setting + +### Code samples + +```sh +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/chats/config/user-debug-logging \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /api/v2/chats/config/user-debug-logging` + +### Example responses + +> 200 Response + +```json +{ + "debug_logging_enabled": true, + "forced_by_deployment": true, + "user_toggle_allowed": true +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +|--------|---------------------------------------------------------|-------------|------------------------------------------------------------------------------------------| +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.UserChatDebugLoggingSettings](schemas.md#codersdkuserchatdebugloggingsettings) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Update user chat debug logging setting + +### Code samples + +```sh +# Example request using curl +curl -X PUT http://coder-server:8080/api/v2/chats/config/user-debug-logging \ + -H 'Content-Type: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`PUT /api/v2/chats/config/user-debug-logging` + +> Body parameter + +```json +{ + "debug_logging_enabled": true +} +``` + +### Parameters + +| Name | In | Type | Required | Description | +|--------|------|----------------------------------------------------------------------------------------------------|----------|--------------| +| `body` | body | [codersdk.UpdateUserChatDebugLoggingRequest](schemas.md#codersdkupdateuserchatdebugloggingrequest) | true | Request body | + +### Responses + +| Status | Meaning | Description | Schema | +|--------|-----------------------------------------------------------------|-------------|--------| +| 204 | [No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5) | No Content | | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Get user chat custom prompt + +### Code samples + +```sh +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/chats/config/user-prompt \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /api/v2/chats/config/user-prompt` + +### Example responses + +> 200 Response + +```json +{ + "custom_prompt": "string" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +|--------|---------------------------------------------------------|-------------|--------------------------------------------------------------------------| +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.UserChatCustomPrompt](schemas.md#codersdkuserchatcustomprompt) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Update user chat custom prompt + +### Code samples + +```sh +# Example request using curl +curl -X PUT http://coder-server:8080/api/v2/chats/config/user-prompt \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`PUT /api/v2/chats/config/user-prompt` + +> Body parameter + +```json +{ + "custom_prompt": "string" +} +``` + +### Parameters + +| Name | In | Type | Required | Description | +|--------|------|--------------------------------------------------------------------------|----------|--------------| +| `body` | body | [codersdk.UserChatCustomPrompt](schemas.md#codersdkuserchatcustomprompt) | true | Request body | + +### Example responses + +> 200 Response + +```json +{ + "custom_prompt": "string" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +|--------|---------------------------------------------------------|-------------|--------------------------------------------------------------------------| +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.UserChatCustomPrompt](schemas.md#codersdkuserchatcustomprompt) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Get chat workspace time to live + +### Code samples + +```sh +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/chats/config/workspace-ttl \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /api/v2/chats/config/workspace-ttl` + +### Example responses + +> 200 Response + +```json +{ + "workspace_ttl_ms": 0 +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +|--------|---------------------------------------------------------|-------------|----------------------------------------------------------------------------------| +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.ChatWorkspaceTTLResponse](schemas.md#codersdkchatworkspacettlresponse) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Update chat workspace time to live + +### Code samples + +```sh +# Example request using curl +curl -X PUT http://coder-server:8080/api/v2/chats/config/workspace-ttl \ + -H 'Content-Type: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`PUT /api/v2/chats/config/workspace-ttl` + +> Body parameter + +```json +{ + "workspace_ttl_ms": 0 +} +``` + +### Parameters + +| Name | In | Type | Required | Description | +|--------|------|--------------------------------------------------------------------------------------------|----------|--------------| +| `body` | body | [codersdk.UpdateChatWorkspaceTTLRequest](schemas.md#codersdkupdatechatworkspacettlrequest) | true | Request body | + +### Responses + +| Status | Meaning | Description | Schema | +|--------|-----------------------------------------------------------------|-------------|--------| +| 204 | [No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5) | No Content | | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + ## Upload chat file ### Code samples ```sh # Example request using curl -curl -X POST http://coder-server:8080/api/experimental/chats/files?organization=497f6eca-6276-4993-bfeb-53cbbbba6f08 \ +curl -X POST http://coder-server:8080/api/v2/chats/files?organization=497f6eca-6276-4993-bfeb-53cbbbba6f08 \ + -H 'Content-Type: image/png' \ -H 'Accept: application/json' \ - -H 'Coder-Session-Token: API_KEY' + -H 'Content-Disposition: attachment; filename="image.png"' \ + -H 'Coder-Session-Token: API_KEY' \ + --data-binary '@image.png' ``` -`POST /api/experimental/chats/files` - -Experimental: this endpoint is subject to change. +`POST /api/v2/chats/files` ### Parameters -| Name | In | Type | Required | Description | -|----------------|-------|--------------|----------|-----------------| -| `organization` | query | string(uuid) | true | Organization ID | +| Name | In | Type | Required | Description | +|-----------------------|--------|--------------|----------|-----------------------------------------------| +| `organization` | query | string(uuid) | true | Organization ID | +| `Content-Disposition` | header | string | true | Attachment disposition carrying the file name | +| `body` | body | string | true | Raw file binary data | ### Example responses @@ -574,13 +1254,11 @@ To perform this operation, you must be authenticated. [Learn more](authenticatio ```sh # Example request using curl -curl -X GET http://coder-server:8080/api/experimental/chats/files/{file} \ +curl -X GET http://coder-server:8080/api/v2/chats/files/{file} \ -H 'Coder-Session-Token: API_KEY' ``` -`GET /api/experimental/chats/files/{file}` - -Experimental: this endpoint is subject to change. +`GET /api/v2/chats/files/{file}` ### Parameters @@ -602,14 +1280,12 @@ To perform this operation, you must be authenticated. [Learn more](authenticatio ```sh # Example request using curl -curl -X GET http://coder-server:8080/api/experimental/chats/watch \ +curl -X GET http://coder-server:8080/api/v2/chats/watch \ -H 'Accept: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`GET /api/experimental/chats/watch` - -Experimental: this endpoint is subject to change. +`GET /api/v2/chats/watch` ### Example responses @@ -744,14 +1420,12 @@ To perform this operation, you must be authenticated. [Learn more](authenticatio ```sh # Example request using curl -curl -X GET http://coder-server:8080/api/experimental/chats/{chat} \ +curl -X GET http://coder-server:8080/api/v2/chats/{chat} \ -H 'Accept: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`GET /api/experimental/chats/{chat}` - -Experimental: this endpoint is subject to change. +`GET /api/v2/chats/{chat}` ### Parameters @@ -981,14 +1655,12 @@ To perform this operation, you must be authenticated. [Learn more](authenticatio ```sh # Example request using curl -curl -X PATCH http://coder-server:8080/api/experimental/chats/{chat} \ +curl -X PATCH http://coder-server:8080/api/v2/chats/{chat} \ -H 'Content-Type: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`PATCH /api/experimental/chats/{chat}` - -Experimental: this endpoint is subject to change. +`PATCH /api/v2/chats/{chat}` > Body parameter @@ -1027,14 +1699,12 @@ To perform this operation, you must be authenticated. [Learn more](authenticatio ```sh # Example request using curl -curl -X PUT http://coder-server:8080/api/experimental/chats/{chat}/context \ +curl -X PUT http://coder-server:8080/api/v2/chats/{chat}/context \ -H 'Accept: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`PUT /api/experimental/chats/{chat}/context` - -Experimental: this endpoint is subject to change. +`PUT /api/v2/chats/{chat}/context` ### Parameters @@ -1264,14 +1934,12 @@ To perform this operation, you must be authenticated. [Learn more](authenticatio ```sh # Example request using curl -curl -X GET http://coder-server:8080/api/experimental/chats/{chat}/cost \ +curl -X GET http://coder-server:8080/api/v2/chats/{chat}/cost \ -H 'Accept: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`GET /api/experimental/chats/{chat}/cost` - -Experimental: this endpoint is subject to change. +`GET /api/v2/chats/{chat}/cost` Cost covers the whole chat tree: the root chat plus every subagent chat beneath it. Requesting cost for a subagent chat @@ -1316,14 +1984,12 @@ To perform this operation, you must be authenticated. [Learn more](authenticatio ```sh # Example request using curl -curl -X GET http://coder-server:8080/api/experimental/chats/{chat}/diff \ +curl -X GET http://coder-server:8080/api/v2/chats/{chat}/diff \ -H 'Accept: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`GET /api/experimental/chats/{chat}/diff` - -Experimental: this endpoint is subject to change. +`GET /api/v2/chats/{chat}/diff` ### Parameters @@ -1360,14 +2026,12 @@ To perform this operation, you must be authenticated. [Learn more](authenticatio ```sh # Example request using curl -curl -X POST http://coder-server:8080/api/experimental/chats/{chat}/interrupt \ +curl -X POST http://coder-server:8080/api/v2/chats/{chat}/interrupt \ -H 'Accept: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`POST /api/experimental/chats/{chat}/interrupt` - -Experimental: this endpoint is subject to change. +`POST /api/v2/chats/{chat}/interrupt` ### Parameters @@ -1597,14 +2261,12 @@ To perform this operation, you must be authenticated. [Learn more](authenticatio ```sh # Example request using curl -curl -X GET http://coder-server:8080/api/experimental/chats/{chat}/messages \ +curl -X GET http://coder-server:8080/api/v2/chats/{chat}/messages \ -H 'Accept: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`GET /api/experimental/chats/{chat}/messages` - -Experimental: this endpoint is subject to change. +`GET /api/v2/chats/{chat}/messages` ### Parameters @@ -1794,15 +2456,13 @@ To perform this operation, you must be authenticated. [Learn more](authenticatio ```sh # Example request using curl -curl -X POST http://coder-server:8080/api/experimental/chats/{chat}/messages \ +curl -X POST http://coder-server:8080/api/v2/chats/{chat}/messages \ -H 'Content-Type: application/json' \ -H 'Accept: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`POST /api/experimental/chats/{chat}/messages` - -Experimental: this endpoint is subject to change. +`POST /api/v2/chats/{chat}/messages` > Body parameter @@ -2097,15 +2757,13 @@ To perform this operation, you must be authenticated. [Learn more](authenticatio ```sh # Example request using curl -curl -X PATCH http://coder-server:8080/api/experimental/chats/{chat}/messages/{message} \ +curl -X PATCH http://coder-server:8080/api/v2/chats/{chat}/messages/{message} \ -H 'Content-Type: application/json' \ -H 'Accept: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`PATCH /api/experimental/chats/{chat}/messages/{message}` - -Experimental: this endpoint is subject to change. +`PATCH /api/v2/chats/{chat}/messages/{message}` > Body parameter @@ -2331,14 +2989,12 @@ To perform this operation, you must be authenticated. [Learn more](authenticatio ```sh # Example request using curl -curl -X GET http://coder-server:8080/api/experimental/chats/{chat}/prompts \ +curl -X GET http://coder-server:8080/api/v2/chats/{chat}/prompts \ -H 'Accept: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`GET /api/experimental/chats/{chat}/prompts` - -Experimental: this endpoint is subject to change. +`GET /api/v2/chats/{chat}/prompts` Returns the user-authored prompts in a chat, newest first, with each prompt's text parts concatenated in the order they @@ -2376,20 +3032,90 @@ message in the chat. To perform this operation, you must be authenticated. [Learn more](authentication.md). +## Delete chat queued message + +### Code samples + +```sh +# Example request using curl +curl -X DELETE http://coder-server:8080/api/v2/chats/{chat}/queue/{queuedMessage} \ + -H 'Coder-Session-Token: API_KEY' +``` + +`DELETE /api/v2/chats/{chat}/queue/{queuedMessage}` + +### Parameters + +| Name | In | Type | Required | Description | +|-----------------|------|--------------|----------|-------------------| +| `chat` | path | string(uuid) | true | Chat ID | +| `queuedMessage` | path | integer | true | Queued message ID | + +### Responses + +| Status | Meaning | Description | Schema | +|--------|-----------------------------------------------------------------|-------------|--------| +| 204 | [No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5) | No Content | | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Promote chat queued message + +### Code samples + +```sh +# Example request using curl +curl -X POST http://coder-server:8080/api/v2/chats/{chat}/queue/{queuedMessage}/promote \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`POST /api/v2/chats/{chat}/queue/{queuedMessage}/promote` + +### Parameters + +| Name | In | Type | Required | Description | +|-----------------|------|--------------|----------|-------------------| +| `chat` | path | string(uuid) | true | Chat ID | +| `queuedMessage` | path | integer | true | Queued message ID | + +### Example responses + +> 202 Response + +```json +{ + "detail": "string", + "message": "string", + "validations": [ + { + "detail": "string", + "field": "string" + } + ] +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +|--------|---------------------------------------------------------------|-------------|--------------------------------------------------| +| 202 | [Accepted](https://tools.ietf.org/html/rfc7231#section-6.3.3) | Accepted | [codersdk.Response](schemas.md#codersdkresponse) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + ## Reconcile invalid chat state ### Code samples ```sh # Example request using curl -curl -X POST http://coder-server:8080/api/experimental/chats/{chat}/reconcile-invalid \ +curl -X POST http://coder-server:8080/api/v2/chats/{chat}/reconcile-invalid \ -H 'Accept: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`POST /api/experimental/chats/{chat}/reconcile-invalid` - -Experimental: this endpoint is subject to change. +`POST /api/v2/chats/{chat}/reconcile-invalid` ### Parameters @@ -2619,196 +3345,46 @@ To perform this operation, you must be authenticated. [Learn more](authenticatio ```sh # Example request using curl -curl -X GET http://coder-server:8080/api/experimental/chats/{chat}/stream \ +curl -X GET http://coder-server:8080/api/v2/chats/{chat}/stream \ -H 'Accept: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`GET /api/experimental/chats/{chat}/stream` - -Experimental: this endpoint is subject to change. +`GET /api/v2/chats/{chat}/stream` ### Parameters -| Name | In | Type | Required | Description | -|--------|------|--------------|----------|-------------| -| `chat` | path | string(uuid) | true | Chat ID | +| Name | In | Type | Required | Description | +|------------|-------|--------------|----------|---------------------------------------------------------| +| `chat` | path | string(uuid) | true | Chat ID | +| `after_id` | query | integer | false | Skip snapshot messages with id at or before this cursor | ### Example responses > 200 Response ```json -{ - "action_required": { - "tool_calls": [ - { - "args": "string", - "tool_call_id": "string", - "tool_name": "string" - } - ] - }, - "chat_id": "efc9fe20-a1e5-4a8c-9c48-f1b30c1e4f86", - "error": { - "detail": "string", - "kind": "generic", - "message": "string", - "provider": "string", - "retryable": true, - "status_code": 0 - }, - "message": { - "chat_id": "efc9fe20-a1e5-4a8c-9c48-f1b30c1e4f86", - "content": [ - { - "args": [ - 0 - ], - "args_delta": "string", - "completed_at": "2019-08-24T14:15:22Z", - "content": "string", - "context_file_agent_id": { - "uuid": "string", - "valid": true - }, - "context_file_content": "string", - "context_file_directory": "string", - "context_file_os": "string", - "context_file_path": "string", - "context_file_skill_meta_file": "string", - "context_file_truncated": true, - "created_at": "2019-08-24T14:15:22Z", - "data": [ - 0 - ], - "end_line": 0, - "file_id": { - "uuid": "string", - "valid": true - }, - "file_name": "string", - "hook_rewritten": true, - "is_error": true, - "is_media": true, - "mcp_server_config_id": { - "uuid": "string", - "valid": true - }, - "media_type": "string", - "name": "string", - "parsed_commands": [ - [ - "string" - ] - ], - "provider_executed": true, - "provider_metadata": [ - 0 - ], - "result": [ - 0 - ], - "result_delta": "string", - "result_reset": true, - "skill_description": "string", - "skill_dir": "string", - "skill_name": "string", - "source_id": "string", - "start_line": 0, - "text": "string", - "title": "string", - "tool_call_id": "string", - "tool_name": "string", - "type": "text", - "url": "string" - } - ], - "created_at": "2019-08-24T14:15:22Z", - "created_by": "ee824cad-d7a6-4f48-87dc-e8461a9201c4", - "id": 0, - "model_config_id": "f5fb4d91-62ca-4377-9ee6-5d43ba00d205", - "role": "system", - "usage": { - "cache_creation_tokens": 0, - "cache_read_tokens": 0, - "context_limit": 0, - "input_tokens": 0, - "output_tokens": 0, - "reasoning_tokens": 0, - "total_tokens": 0 - } - }, - "message_part": { - "generation_attempt": 0, - "history_version": 0, - "part": { - "args": [ - 0 - ], - "args_delta": "string", - "completed_at": "2019-08-24T14:15:22Z", - "content": "string", - "context_file_agent_id": { - "uuid": "string", - "valid": true - }, - "context_file_content": "string", - "context_file_directory": "string", - "context_file_os": "string", - "context_file_path": "string", - "context_file_skill_meta_file": "string", - "context_file_truncated": true, - "created_at": "2019-08-24T14:15:22Z", - "data": [ - 0 - ], - "end_line": 0, - "file_id": { - "uuid": "string", - "valid": true - }, - "file_name": "string", - "hook_rewritten": true, - "is_error": true, - "is_media": true, - "mcp_server_config_id": { - "uuid": "string", - "valid": true - }, - "media_type": "string", - "name": "string", - "parsed_commands": [ - [ - "string" - ] - ], - "provider_executed": true, - "provider_metadata": [ - 0 - ], - "result": [ - 0 - ], - "result_delta": "string", - "result_reset": true, - "skill_description": "string", - "skill_dir": "string", - "skill_name": "string", - "source_id": "string", - "start_line": 0, - "text": "string", - "title": "string", - "tool_call_id": "string", - "tool_name": "string", - "type": "text", - "url": "string" +[ + { + "action_required": { + "tool_calls": [ + { + "args": "string", + "tool_call_id": "string", + "tool_name": "string" + } + ] }, - "role": "system", - "seq": 0 - }, - "queued_messages": [ - { + "chat_id": "efc9fe20-a1e5-4a8c-9c48-f1b30c1e4f86", + "error": { + "detail": "string", + "kind": "generic", + "message": "string", + "provider": "string", + "retryable": true, + "status_code": 0 + }, + "message": { "chat_id": "efc9fe20-a1e5-4a8c-9c48-f1b30c1e4f86", "content": [ { @@ -2875,60 +3451,296 @@ Experimental: this endpoint is subject to change. } ], "created_at": "2019-08-24T14:15:22Z", + "created_by": "ee824cad-d7a6-4f48-87dc-e8461a9201c4", "id": 0, - "model_config_id": "f5fb4d91-62ca-4377-9ee6-5d43ba00d205" - } - ], - "retry": { - "attempt": 0, - "delay_ms": 0, - "error": "string", - "kind": "generic", - "provider": "string", - "retrying_at": "2019-08-24T14:15:22Z", - "status_code": 0 - }, - "status": { - "status": "waiting" - }, - "type": "message_part" -} + "model_config_id": "f5fb4d91-62ca-4377-9ee6-5d43ba00d205", + "role": "system", + "usage": { + "cache_creation_tokens": 0, + "cache_read_tokens": 0, + "context_limit": 0, + "input_tokens": 0, + "output_tokens": 0, + "reasoning_tokens": 0, + "total_tokens": 0 + } + }, + "message_part": { + "generation_attempt": 0, + "history_version": 0, + "part": { + "args": [ + 0 + ], + "args_delta": "string", + "completed_at": "2019-08-24T14:15:22Z", + "content": "string", + "context_file_agent_id": { + "uuid": "string", + "valid": true + }, + "context_file_content": "string", + "context_file_directory": "string", + "context_file_os": "string", + "context_file_path": "string", + "context_file_skill_meta_file": "string", + "context_file_truncated": true, + "created_at": "2019-08-24T14:15:22Z", + "data": [ + 0 + ], + "end_line": 0, + "file_id": { + "uuid": "string", + "valid": true + }, + "file_name": "string", + "hook_rewritten": true, + "is_error": true, + "is_media": true, + "mcp_server_config_id": { + "uuid": "string", + "valid": true + }, + "media_type": "string", + "name": "string", + "parsed_commands": [ + [ + "string" + ] + ], + "provider_executed": true, + "provider_metadata": [ + 0 + ], + "result": [ + 0 + ], + "result_delta": "string", + "result_reset": true, + "skill_description": "string", + "skill_dir": "string", + "skill_name": "string", + "source_id": "string", + "start_line": 0, + "text": "string", + "title": "string", + "tool_call_id": "string", + "tool_name": "string", + "type": "text", + "url": "string" + }, + "role": "system", + "seq": 0 + }, + "queued_messages": [ + { + "chat_id": "efc9fe20-a1e5-4a8c-9c48-f1b30c1e4f86", + "content": [ + { + "args": [ + 0 + ], + "args_delta": "string", + "completed_at": "2019-08-24T14:15:22Z", + "content": "string", + "context_file_agent_id": { + "uuid": "string", + "valid": true + }, + "context_file_content": "string", + "context_file_directory": "string", + "context_file_os": "string", + "context_file_path": "string", + "context_file_skill_meta_file": "string", + "context_file_truncated": true, + "created_at": "2019-08-24T14:15:22Z", + "data": [ + 0 + ], + "end_line": 0, + "file_id": { + "uuid": "string", + "valid": true + }, + "file_name": "string", + "hook_rewritten": true, + "is_error": true, + "is_media": true, + "mcp_server_config_id": { + "uuid": "string", + "valid": true + }, + "media_type": "string", + "name": "string", + "parsed_commands": [ + [ + "string" + ] + ], + "provider_executed": true, + "provider_metadata": [ + 0 + ], + "result": [ + 0 + ], + "result_delta": "string", + "result_reset": true, + "skill_description": "string", + "skill_dir": "string", + "skill_name": "string", + "source_id": "string", + "start_line": 0, + "text": "string", + "title": "string", + "tool_call_id": "string", + "tool_name": "string", + "type": "text", + "url": "string" + } + ], + "created_at": "2019-08-24T14:15:22Z", + "id": 0, + "model_config_id": "f5fb4d91-62ca-4377-9ee6-5d43ba00d205" + } + ], + "retry": { + "attempt": 0, + "delay_ms": 0, + "error": "string", + "kind": "generic", + "provider": "string", + "retrying_at": "2019-08-24T14:15:22Z", + "status_code": 0 + }, + "status": { + "status": "waiting" + }, + "type": "message_part" + } +] ``` ### Responses -| Status | Meaning | Description | Schema | -|--------|---------------------------------------------------------|-------------|----------------------------------------------------------------| -| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.ChatStreamEvent](schemas.md#codersdkchatstreamevent) | +| Status | Meaning | Description | Schema | +|--------|---------------------------------------------------------|-------------|-------------------------------------------------------------------------| +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | array of [codersdk.ChatStreamEvent](schemas.md#codersdkchatstreamevent) | -To perform this operation, you must be authenticated. [Learn more](authentication.md). +

Response Schema

-## Connect to chat workspace desktop via WebSockets +Status Code **200** -### Code samples +| Name | Type | Required | Restrictions | Description | +|------------------------------------|----------------------------------------------------------------------------------|----------|--------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `[array item]` | array | false | | | +| `» action_required` | [codersdk.ChatStreamActionRequired](schemas.md#codersdkchatstreamactionrequired) | false | | | +| `»» tool_calls` | array | false | | | +| `»»» args` | string | false | | | +| `»»» tool_call_id` | string | false | | | +| `»»» tool_name` | string | false | | | +| `» chat_id` | string(uuid) | false | | | +| `» error` | [codersdk.ChatError](schemas.md#codersdkchaterror) | false | | | +| `»» detail` | string | false | | Detail is optional provider-specific context shown alongside the normalized error message when available. | +| `»» kind` | [codersdk.ChatErrorKind](schemas.md#codersdkchaterrorkind) | false | | Kind classifies the error for consistent client rendering. | +| `»» message` | string | false | | Message is the normalized, user-facing error message. | +| `»» provider` | string | false | | Provider identifies the upstream model provider when known. | +| `»» retryable` | boolean | false | | Retryable reports whether the underlying error is transient. | +| `»» status_code` | integer | false | | Status code is the best-effort upstream HTTP status code. | +| `» message` | [codersdk.ChatMessage](schemas.md#codersdkchatmessage) | false | | | +| `»» chat_id` | string(uuid) | false | | | +| `»» content` | array | false | | | +| `»»» args` | array | false | | | +| `»»» args_delta` | string | false | | | +| `»»» completed_at` | string(date-time) | false | | Completed at is the time a reasoning part finished streaming, so reasoning duration can be computed as completed_at minus created_at. For interrupted reasoning, this is the interruption time. Absent when reasoning timestamp data was not recorded (e.g. messages persisted before this feature was added). | +| `»»» content` | string | false | | The code content from the diff that was commented on. | +| `»»» context_file_agent_id` | [uuid.NullUUID](schemas.md#uuidnulluuid) | false | | Context file agent ID is the workspace agent that provided this context file. Used to detect when the agent changes (e.g. workspace rebuilt) so instruction files can be re-persisted with fresh content. | +| `»»»» uuid` | string | false | | | +| `»»»» valid` | boolean | false | | Valid is true if UUID is not NULL | +| `»»» context_file_content` | string | false | | Context file content holds the file content sent to the LLM. Internal only: stripped before API responses to keep payloads small. The backend reads it when building the prompt via partsToMessageParts. | +| `»»» context_file_directory` | string | false | | Context file directory is the working directory of the workspace agent. Internal only: same purpose as ContextFileOS. | +| `»»» context_file_os` | string | false | | Context file os is the operating system of the workspace agent. Internal only: used during prompt expansion so the LLM knows the OS even on turns where InsertSystem is not called. | +| `»»» context_file_path` | string | false | | Context file path is the absolute path of a file loaded into the LLM context (e.g. an AGENTS.md instruction file). | +| `»»» context_file_skill_meta_file` | string | false | | Context file skill meta file is the basename of the skill meta file (e.g. "SKILL.md") at the time of persistence. Internal only: restored on subsequent turns so the read_skill tool uses the correct filename even when the agent configured a non-default value. | +| `»»» context_file_truncated` | boolean | false | | Context file truncated indicates the file exceeded the 64KiB instruction file limit and was truncated. | +| `»»» created_at` | string(date-time) | false | | Created at is the timestamp this part carries. The semantics depend on the part type: for tool-call and tool-result parts it is the time the call was emitted or the result was produced (tool duration is the result's created_at minus the call's created_at); for reasoning parts it is the time reasoning started streaming. | +| `»»» data` | array | false | | | +| `»»» end_line` | integer | false | | | +| `»»» file_id` | [uuid.NullUUID](schemas.md#uuidnulluuid) | false | | | +| `»»»» uuid` | string | false | | | +| `»»»» valid` | boolean | false | | Valid is true if UUID is not NULL | +| `»»» file_name` | string | false | | | +| `»»» hook_rewritten` | boolean | false | | Hook rewritten indicates that a lifecycle hook replaced model-proposed tool input. | +| `»»» is_error` | boolean | false | | | +| `»»» is_media` | boolean | false | | | +| `»»» mcp_server_config_id` | [uuid.NullUUID](schemas.md#uuidnulluuid) | false | | | +| `»»»» uuid` | string | false | | | +| `»»»» valid` | boolean | false | | Valid is true if UUID is not NULL | +| `»»» media_type` | string | false | | | +| `»»» name` | string | false | | | +| `»»» parsed_commands` | array | false | | Parsed commands holds parsed programs from an execute tool call's shell command, one entry per simple command in source order. Each entry is [program] or [program, arg] where arg is the first non-flag positional argument. Program names are normalized to their base name (e.g. /usr/bin/go becomes go). Only populated when ToolName is "execute" and the command parses successfully; nil otherwise. | +| `»»» provider_executed` | boolean | false | | Provider executed indicates the tool call was executed by the provider (e.g. Anthropic computer use). | +| `»»» provider_metadata` | array | false | | Provider metadata holds provider-specific response metadata (e.g. Anthropic cache control hints) as raw JSON. Internal only: stripped by db2sdk before API responses. | +| `»»» result` | array | false | | | +| `»»» result_delta` | string | false | | | +| `»»» result_reset` | boolean | false | | | +| `»»» skill_description` | string | false | | Skill description is the short description from the skill's SKILL.md frontmatter. | +| `»»» skill_dir` | string | false | | Skill dir is the absolute path to the skill directory inside the workspace filesystem. Internal only: used by read_skill/read_skill_file tools to locate skill files. | +| `»»» skill_name` | string | false | | Skill name is the kebab-case name of a discovered skill from the workspace's .agents/skills/ directory. | +| `»»» source_id` | string | false | | | +| `»»» start_line` | integer | false | | | +| `»»» text` | string | false | | | +| `»»» title` | string | false | | | +| `»»» tool_call_id` | string | false | | | +| `»»» tool_name` | string | false | | | +| `»»» type` | [codersdk.ChatMessagePartType](schemas.md#codersdkchatmessageparttype) | false | | | +| `»»» url` | string | false | | | +| `»» created_at` | string(date-time) | false | | | +| `»» created_by` | string(uuid) | false | | | +| `»» id` | integer | false | | | +| `»» model_config_id` | string(uuid) | false | | | +| `»» role` | [codersdk.ChatMessageRole](schemas.md#codersdkchatmessagerole) | false | | | +| `»» usage` | [codersdk.ChatMessageUsage](schemas.md#codersdkchatmessageusage) | false | | | +| `»»» cache_creation_tokens` | integer | false | | | +| `»»» cache_read_tokens` | integer | false | | | +| `»»» context_limit` | integer | false | | | +| `»»» input_tokens` | integer | false | | | +| `»»» output_tokens` | integer | false | | | +| `»»» reasoning_tokens` | integer | false | | | +| `»»» total_tokens` | integer | false | | | +| `» message_part` | [codersdk.ChatStreamMessagePart](schemas.md#codersdkchatstreammessagepart) | false | | | +| `»» generation_attempt` | integer | false | | | +| `»» history_version` | integer | false | | | +| `»» part` | [codersdk.ChatMessagePart](schemas.md#codersdkchatmessagepart) | false | | | +| `»» role` | [codersdk.ChatMessageRole](schemas.md#codersdkchatmessagerole) | false | | | +| `»» seq` | integer | false | | | +| `» queued_messages` | array | false | | | +| `»» chat_id` | string(uuid) | false | | | +| `»» content` | array | false | | | +| `»» created_at` | string(date-time) | false | | | +| `»» id` | integer | false | | | +| `»» model_config_id` | string(uuid) | false | | | +| `» retry` | [codersdk.ChatStreamRetry](schemas.md#codersdkchatstreamretry) | false | | | +| `»» attempt` | integer | false | | Attempt is the 1-indexed retry attempt number. | +| `»» delay_ms` | integer | false | | Delay ms is the backoff delay in milliseconds before the retry. | +| `»» error` | string | false | | Error is the normalized error message from the failed attempt. | +| `»» kind` | [codersdk.ChatErrorKind](schemas.md#codersdkchaterrorkind) | false | | Kind classifies the retry reason for consistent client rendering. | +| `»» provider` | string | false | | Provider identifies the upstream model provider when known. | +| `»» retrying_at` | string(date-time) | false | | Retrying at is the timestamp when the retry will be attempted. | +| `»» status_code` | integer | false | | Status code is the best-effort upstream HTTP status code. | +| `» status` | [codersdk.ChatStreamStatus](schemas.md#codersdkchatstreamstatus) | false | | | +| `»» status` | [codersdk.ChatStatus](schemas.md#codersdkchatstatus) | false | | | +| `» type` | [codersdk.ChatStreamEventType](schemas.md#codersdkchatstreameventtype) | false | | | -```sh -# Example request using curl -curl -X GET http://coder-server:8080/api/experimental/chats/{chat}/stream/desktop \ - -H 'Coder-Session-Token: API_KEY' -``` +#### Enumerated Values -`GET /api/experimental/chats/{chat}/stream/desktop` - -Raw binary WebSocket stream of the chat workspace desktop. -Experimental: this endpoint is subject to change. - -### Parameters - -| Name | In | Type | Required | Description | -|--------|------|--------------|----------|-------------| -| `chat` | path | string(uuid) | true | Chat ID | - -### Responses - -| Status | Meaning | Description | Schema | -|--------|--------------------------------------------------------------------------|---------------------|--------| -| 101 | [Switching Protocols](https://tools.ietf.org/html/rfc7231#section-6.2.2) | Switching Protocols | | +| Property | Value(s) | +|----------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `kind` | `auth`, `config`, `content_filter`, `generic`, `hook_denied`, `hook_dispatch_failed`, `missing_key`, `overloaded`, `provider_disabled`, `rate_limit`, `stream_silence_timeout`, `timeout`, `usage_limit` | +| `type` | `action_required`, `context-file`, `error`, `file`, `file-reference`, `history_reset`, `hook-context`, `hook-notice`, `message`, `message_part`, `preview_reset`, `queue_update`, `reasoning`, `retry`, `skill`, `source`, `status`, `text`, `tool-call`, `tool-result` | +| `role` | `assistant`, `system`, `tool`, `user` | +| `status` | `error`, `interrupting`, `requires_action`, `running`, `waiting` | To perform this operation, you must be authenticated. [Learn more](authentication.md). @@ -2938,14 +3750,12 @@ To perform this operation, you must be authenticated. [Learn more](authenticatio ```sh # Example request using curl -curl -X GET http://coder-server:8080/api/experimental/chats/{chat}/stream/git \ +curl -X GET http://coder-server:8080/api/v2/chats/{chat}/stream/git \ -H 'Accept: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`GET /api/experimental/chats/{chat}/stream/git` - -Experimental: this endpoint is subject to change. +`GET /api/v2/chats/{chat}/stream/git` ### Parameters @@ -2988,14 +3798,12 @@ To perform this operation, you must be authenticated. [Learn more](authenticatio ```sh # Example request using curl -curl -X POST http://coder-server:8080/api/experimental/chats/{chat}/title/propose \ +curl -X POST http://coder-server:8080/api/v2/chats/{chat}/title/propose \ -H 'Accept: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`POST /api/experimental/chats/{chat}/title/propose` - -Experimental: this endpoint is subject to change. +`POST /api/v2/chats/{chat}/title/propose` ### Parameters @@ -3020,3 +3828,208 @@ Experimental: this endpoint is subject to change. | 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.ProposeChatTitleResponse](schemas.md#codersdkproposechattitleresponse) | To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Submit chat tool results + +### Code samples + +```sh +# Example request using curl +curl -X POST http://coder-server:8080/api/v2/chats/{chat}/tool-results \ + -H 'Content-Type: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`POST /api/v2/chats/{chat}/tool-results` + +> Body parameter + +```json +{ + "results": [ + { + "is_error": true, + "output": [ + 0 + ], + "tool_call_id": "string" + } + ] +} +``` + +### Parameters + +| Name | In | Type | Required | Description | +|--------|------|----------------------------------------------------------------------------------|----------|--------------| +| `chat` | path | string(uuid) | true | Chat ID | +| `body` | body | [codersdk.SubmitToolResultsRequest](schemas.md#codersdksubmittoolresultsrequest) | true | Request body | + +### Responses + +| Status | Meaning | Description | Schema | +|--------|-----------------------------------------------------------------|-------------|--------| +| 204 | [No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5) | No Content | | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## List user AI provider key configurations + +### Code samples + +```sh +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/users/{user}/ai-provider-keys \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /api/v2/users/{user}/ai-provider-keys` + +### Parameters + +| Name | In | Type | Required | Description | +|--------|------|--------|----------|--------------------------| +| `user` | path | string | true | User ID, username, or me | + +### Example responses + +> 200 Response + +```json +[ + { + "byok_enabled": true, + "has_provider_api_key": true, + "has_user_api_key": true, + "provider": { + "deleted": true, + "display_name": "string", + "enabled": true, + "icon": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "name": "string", + "type": "openai" + } + } +] +``` + +### Responses + +| Status | Meaning | Description | Schema | +|--------|---------------------------------------------------------|-------------|-----------------------------------------------------------------------------------------| +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | array of [codersdk.UserAIProviderKeyConfig](schemas.md#codersdkuseraiproviderkeyconfig) | + +

Response Schema

+ +Status Code **200** + +| Name | Type | Required | Restrictions | Description | +|--------------------------|--------------------------------------------------------------------|----------|--------------|-------------| +| `[array item]` | array | false | | | +| `» byok_enabled` | boolean | false | | | +| `» has_provider_api_key` | boolean | false | | | +| `» has_user_api_key` | boolean | false | | | +| `» provider` | [codersdk.AIProviderSummary](schemas.md#codersdkaiprovidersummary) | false | | | +| `»» deleted` | boolean | false | | | +| `»» display_name` | string | false | | | +| `»» enabled` | boolean | false | | | +| `»» icon` | string | false | | | +| `»» id` | string(uuid) | false | | | +| `»» name` | string | false | | | +| `»» type` | [codersdk.AIProviderType](schemas.md#codersdkaiprovidertype) | false | | | + +#### Enumerated Values + +| Property | Value(s) | +|----------|---------------------------------------------------------------------------------------------------------| +| `type` | `anthropic`, `azure`, `bedrock`, `copilot`, `google`, `openai`, `openai-compat`, `openrouter`, `vercel` | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Update user AI provider key + +### Code samples + +```sh +# Example request using curl +curl -X PUT http://coder-server:8080/api/v2/users/{user}/ai-provider-keys/{aiProvider} \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`PUT /api/v2/users/{user}/ai-provider-keys/{aiProvider}` + +> Body parameter + +```json +{ + "api_key": "string" +} +``` + +### Parameters + +| Name | In | Type | Required | Description | +|--------------|------|----------------------------------------------------------------------------------------------|----------|--------------------------| +| `user` | path | string | true | User ID, username, or me | +| `aiProvider` | path | string(uuid) | true | AI provider ID | +| `body` | body | [codersdk.CreateUserAIProviderKeyRequest](schemas.md#codersdkcreateuseraiproviderkeyrequest) | true | Request body | + +### Example responses + +> 200 Response + +```json +{ + "byok_enabled": true, + "has_provider_api_key": true, + "has_user_api_key": true, + "provider": { + "deleted": true, + "display_name": "string", + "enabled": true, + "icon": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "name": "string", + "type": "openai" + } +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +|--------|---------------------------------------------------------|-------------|--------------------------------------------------------------------------------| +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.UserAIProviderKeyConfig](schemas.md#codersdkuseraiproviderkeyconfig) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Delete user AI provider key + +### Code samples + +```sh +# Example request using curl +curl -X DELETE http://coder-server:8080/api/v2/users/{user}/ai-provider-keys/{aiProvider} \ + -H 'Coder-Session-Token: API_KEY' +``` + +`DELETE /api/v2/users/{user}/ai-provider-keys/{aiProvider}` + +### Parameters + +| Name | In | Type | Required | Description | +|--------------|------|--------------|----------|--------------------------| +| `user` | path | string | true | User ID, username, or me | +| `aiProvider` | path | string(uuid) | true | AI provider ID | + +### Responses + +| Status | Meaning | Description | Schema | +|--------|-----------------------------------------------------------------|-------------|--------| +| 204 | [No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5) | No Content | | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). diff --git a/docs/reference/api/schemas.md b/docs/reference/api/schemas.md index e8b89c5c59..45668e1216 100644 --- a/docs/reference/api/schemas.md +++ b/docs/reference/api/schemas.md @@ -227,6 +227,21 @@ title: Schemas |--------------------| | `prebuild_claimed` | +## coderd.chatsByWorkspaceResponse + +```json +{ + "property1": "string", + "property2": "string" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +|------------------|--------|----------|--------------|-------------| +| `[any property]` | string | false | | | + ## coderd.cspViolation ```json @@ -1336,6 +1351,32 @@ None |------------|-----------------|----------|--------------|-------------| | `warnings` | array of string | false | | | +## codersdk.AIProviderSummary + +```json +{ + "deleted": true, + "display_name": "string", + "enabled": true, + "icon": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "name": "string", + "type": "openai" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +|----------------|----------------------------------------------------|----------|--------------|-------------| +| `deleted` | boolean | false | | | +| `display_name` | string | false | | | +| `enabled` | boolean | false | | | +| `icon` | string | false | | | +| `id` | string | false | | | +| `name` | string | false | | | +| `type` | [codersdk.AIProviderType](#codersdkaiprovidertype) | false | | | + ## codersdk.AIProviderType ```json @@ -2522,6 +2563,20 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in | `groups` | array of [codersdk.ChatGroup](#codersdkchatgroup) | false | | | | `users` | array of [codersdk.ChatUser](#codersdkchatuser) | false | | | +## codersdk.ChatAutoArchiveDaysResponse + +```json +{ + "auto_archive_days": 0 +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +|---------------------|---------|----------|--------------|-------------| +| `auto_archive_days` | integer | false | | | + ## codersdk.ChatBusyBehavior ```json @@ -2721,6 +2776,36 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in | `total_cost_micros` | integer | false | | | | `unpriced_request_count` | integer | false | | | +## codersdk.ChatDebugLoggingAdminSettings + +```json +{ + "allow_users": true, + "forced_by_deployment": true +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +|------------------------|---------|----------|--------------|-------------| +| `allow_users` | boolean | false | | | +| `forced_by_deployment` | boolean | false | | | + +## codersdk.ChatDebugRetentionDaysResponse + +```json +{ + "debug_retention_days": 0 +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +|------------------------|---------|----------|--------------|-------------| +| `debug_retention_days` | integer | false | | | + ## codersdk.ChatDiffContents ```json @@ -4538,6 +4623,20 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in |-----------------------------------------------| | `chat_default`, `deployment_default`, `model` | +## codersdk.ChatPersonalModelOverridesAdminSettings + +```json +{ + "allow_users": true +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +|---------------|---------|----------|--------------|-------------| +| `allow_users` | boolean | false | | | + ## codersdk.ChatPlanMode ```json @@ -4552,6 +4651,20 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in |----------| | `plan` | +## codersdk.ChatPlanModeInstructionsResponse + +```json +{ + "plan_mode_instructions": "string" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +|--------------------------|--------|----------|--------------|-------------| +| `plan_mode_instructions` | string | false | | | + ## codersdk.ChatPrompt ```json @@ -5162,6 +5275,24 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in | `tool_call_id` | string | false | | | | `tool_name` | string | false | | | +## codersdk.ChatSystemPromptResponse + +```json +{ + "default_system_prompt": "string", + "include_default_system_prompt": true, + "system_prompt": "string" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +|---------------------------------|---------|----------|--------------|-------------| +| `default_system_prompt` | string | false | | | +| `include_default_system_prompt` | boolean | false | | | +| `system_prompt` | string | false | | | + ## codersdk.ChatUnsupportedProvider ```json @@ -5345,6 +5476,20 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in |----------------------------------------------------------------------------------------------------------------------------------------------------------| | `action_required`, `chat_summary_change`, `context_dirty`, `created`, `deleted`, `diff_status_change`, `status_change`, `summary_change`, `title_change` | +## codersdk.ChatWorkspaceTTLResponse + +```json +{ + "workspace_ttl_ms": 0 +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +|--------------------|---------|----------|--------------|--------------------------------------------------------------------------------------------------------------------------| +| `workspace_ttl_ms` | integer | false | | Workspace ttl ms is the workspace TTL in milliseconds. Zero means disabled; the template's own autostop setting applies. | + ## codersdk.ClusterConfig ```json @@ -6681,6 +6826,20 @@ This is required on creation to enable a user-flow of validating a template work | `phone_number` | string | true | | | | `source` | [codersdk.PremiumFunnelSource](#codersdkpremiumfunnelsource) | false | | Source is the premium paywall the request came from, for telemetry. It is not forwarded to the licensor. Omit it to report "direct". | +## codersdk.CreateUserAIProviderKeyRequest + +```json +{ + "api_key": "string" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +|-----------|--------|----------|--------------|-------------| +| `api_key` | string | false | | | + ## codersdk.CreateUserRequestWithOrgs ```json @@ -13492,6 +13651,28 @@ Git clone makes use of this by parsing the URL from: 'Username for "https://gith |---------------|--------------------------------------------------------|----------|--------------|-------------| | `usage_stats` | [codersdk.UsageStatsConfig](#codersdkusagestatsconfig) | false | | | +## codersdk.SubmitToolResultsRequest + +```json +{ + "results": [ + { + "is_error": true, + "output": [ + 0 + ], + "tool_call_id": "string" + } + ] +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +|-----------|-----------------------------------------------------|----------|--------------|-------------| +| `results` | array of [codersdk.ToolResult](#codersdktoolresult) | false | | | + ## codersdk.SupportConfig ```json @@ -14999,6 +15180,26 @@ Restarts will only happen on weekdays in this list on weeks which line up with W |----------------------|---------|----------|--------------|-------------| | `max_token_lifetime` | integer | false | | | +## codersdk.ToolResult + +```json +{ + "is_error": true, + "output": [ + 0 + ], + "tool_call_id": "string" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +|----------------|------------------|----------|--------------|-------------| +| `is_error` | boolean | false | | | +| `output` | array of integer | false | | | +| `tool_call_id` | string | false | | | + ## codersdk.TraceConfig ```json @@ -15132,6 +15333,48 @@ Restarts will only happen on weekdays in this list on weeks which line up with W | `user_roles` | object | false | | | | » `[any property]` | [codersdk.ChatRole](#codersdkchatrole) | false | | | +## codersdk.UpdateChatAutoArchiveDaysRequest + +```json +{ + "auto_archive_days": 0 +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +|---------------------|---------|----------|--------------|-------------| +| `auto_archive_days` | integer | false | | | + +## codersdk.UpdateChatDebugLoggingAllowUsersRequest + +```json +{ + "allow_users": true +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +|---------------|---------|----------|--------------|-------------| +| `allow_users` | boolean | false | | | + +## codersdk.UpdateChatDebugRetentionDaysRequest + +```json +{ + "debug_retention_days": 0 +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +|------------------------|---------|----------|--------------|-------------| +| `debug_retention_days` | integer | false | | | + ## codersdk.UpdateChatModelACLRequest ```json @@ -15352,6 +15595,34 @@ Restarts will only happen on weekdays in this list on weeks which line up with W | `model` | string | false | | | | `model_config` | [codersdk.ChatModelCallConfig](#codersdkchatmodelcallconfig) | false | | | +## codersdk.UpdateChatPersonalModelOverridesAdminSettingsRequest + +```json +{ + "allow_users": true +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +|---------------|---------|----------|--------------|-------------| +| `allow_users` | boolean | false | | | + +## codersdk.UpdateChatPlanModeInstructionsRequest + +```json +{ + "plan_mode_instructions": "string" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +|--------------------------|--------|----------|--------------|-------------| +| `plan_mode_instructions` | string | false | | | + ## codersdk.UpdateChatRequest ```json @@ -15394,6 +15665,36 @@ Restarts will only happen on weekdays in this list on weeks which line up with W |------------------|---------|----------|--------------|-------------| | `retention_days` | integer | false | | | +## codersdk.UpdateChatSystemPromptRequest + +```json +{ + "include_default_system_prompt": true, + "system_prompt": "string" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +|---------------------------------|---------|----------|--------------|-------------| +| `include_default_system_prompt` | boolean | false | | | +| `system_prompt` | string | false | | | + +## codersdk.UpdateChatWorkspaceTTLRequest + +```json +{ + "workspace_ttl_ms": 0 +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +|--------------------|---------|----------|--------------|--------------------------------------------------------------------------------------------------------------------------| +| `workspace_ttl_ms` | integer | false | | Workspace ttl ms is the workspace TTL in milliseconds. Zero means disabled; the template's own autostop setting applies. | + ## codersdk.UpdateCheckResponse ```json @@ -15677,6 +15978,34 @@ Restarts will only happen on weekdays in this list on weeks which line up with W | `theme_light` | `dark`, `dark-protan-deuter`, `dark-tritan`, `light`, `light-protan-deuter`, `light-tritan` | | `theme_mode` | `single`, `sync` | +## codersdk.UpdateUserChatCompactionThresholdRequest + +```json +{ + "threshold_percent": 100 +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +|---------------------|---------|----------|--------------|-------------| +| `threshold_percent` | integer | false | | | + +## codersdk.UpdateUserChatDebugLoggingRequest + +```json +{ + "debug_logging_enabled": true +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +|-------------------------|---------|----------|--------------|-------------| +| `debug_logging_enabled` | boolean | false | | | + ## codersdk.UpdateUserChatPersonalModelOverrideRequest ```json @@ -16185,6 +16514,34 @@ If the schedule is empty, the user will be updated to use the default schedule.| | `updated_at` | string | false | | | | `user_id` | string | false | | | +## codersdk.UserAIProviderKeyConfig + +```json +{ + "byok_enabled": true, + "has_provider_api_key": true, + "has_user_api_key": true, + "provider": { + "deleted": true, + "display_name": "string", + "enabled": true, + "icon": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "name": "string", + "type": "openai" + } +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +|------------------------|----------------------------------------------------------|----------|--------------|-------------| +| `byok_enabled` | boolean | false | | | +| `has_provider_api_key` | boolean | false | | | +| `has_user_api_key` | boolean | false | | | +| `provider` | [codersdk.AIProviderSummary](#codersdkaiprovidersummary) | false | | | + ## codersdk.UserAISpendStatus ```json @@ -16321,6 +16678,73 @@ If the schedule is empty, the user will be updated to use the default schedule.| | `theme_mode` | [codersdk.ThemeMode](#codersdkthememode) | false | | | | `theme_preference` | string | false | | Theme preference is the legacy single-field appearance setting. In "single" mode it mirrors the active theme. In "sync" mode modern clients normally mirror the active OS slot, but older clients can update only this field, so it may diverge from ThemeLight or ThemeDark until a modern client saves the full appearance state again. | +## codersdk.UserChatCompactionThreshold + +```json +{ + "model_config_id": "f5fb4d91-62ca-4377-9ee6-5d43ba00d205", + "threshold_percent": 0 +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +|---------------------|---------|----------|--------------|-------------| +| `model_config_id` | string | false | | | +| `threshold_percent` | integer | false | | | + +## codersdk.UserChatCompactionThresholds + +```json +{ + "thresholds": [ + { + "model_config_id": "f5fb4d91-62ca-4377-9ee6-5d43ba00d205", + "threshold_percent": 0 + } + ] +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +|--------------|---------------------------------------------------------------------------------------|----------|--------------|-------------| +| `thresholds` | array of [codersdk.UserChatCompactionThreshold](#codersdkuserchatcompactionthreshold) | false | | | + +## codersdk.UserChatCustomPrompt + +```json +{ + "custom_prompt": "string" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +|-----------------|--------|----------|--------------|-------------| +| `custom_prompt` | string | false | | | + +## codersdk.UserChatDebugLoggingSettings + +```json +{ + "debug_logging_enabled": true, + "forced_by_deployment": true, + "user_toggle_allowed": true +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +|-------------------------|---------|----------|--------------|-------------| +| `debug_logging_enabled` | boolean | false | | | +| `forced_by_deployment` | boolean | false | | | +| `user_toggle_allowed` | boolean | false | | | + ## codersdk.UserChatPersonalModelOverridesResponse ```json diff --git a/scripts/apidocgen/markdown-template/code_sh.dot b/scripts/apidocgen/markdown-template/code_sh.dot index 7300ae4ab2..7b1fe793ab 100644 --- a/scripts/apidocgen/markdown-template/code_sh.dot +++ b/scripts/apidocgen/markdown-template/code_sh.dot @@ -1,8 +1,8 @@ -# Example request using curl +{{ const rawBodyFile = data.operation['x-apidocgen'] && data.operation['x-apidocgen'].rawBodyFile; }}# Example request using curl curl -X {{=data.methodUpper}} http://coder-server:8080{{=data.url}}{{=data.requiredQueryString}}{{?data.allHeaders.length}} \{{?}} {{~data.allHeaders :p:index}}{{ if (p.name == "Content-Type" && p.exampleValues.object == "application/x-www-form-urlencoded") { continue; } -}} -H '{{=p.name}}: {{=p.exampleValues.object}}'{{?index < data.allHeaders.length-1}} \ -{{?}}{{~}} +}} -H '{{=p.name}}: {{=p.exampleValues.object}}'{{?index < data.allHeaders.length-1 || rawBodyFile}} \ +{{?}}{{~}}{{?rawBodyFile}} --data-binary '@{{=rawBodyFile}}'{{?}} diff --git a/scripts/apidocgen/markdown-template/operation.dot b/scripts/apidocgen/markdown-template/operation.dot index 57856444b3..1348c2d37e 100644 --- a/scripts/apidocgen/markdown-template/operation.dot +++ b/scripts/apidocgen/markdown-template/operation.dot @@ -45,7 +45,7 @@ {{= renderDescription(data)}} -{{? data.operation.requestBody}} +{{? data.operation.requestBody && !(data.operation['x-apidocgen'] && data.operation['x-apidocgen'].rawBodyFile)}} > Body parameter {{? data.bodyParameter.exampleValues.description }} diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 7d113ada47..3c51923223 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -3657,7 +3657,7 @@ export const ChatWatchEventKinds: ChatWatchEventKind[] = [ export interface ChatWorkspaceTTLResponse { /** * WorkspaceTTLMillis is the workspace TTL in milliseconds. - * Zero means disabled — the template's own autostop setting applies. + * Zero means disabled; the template's own autostop setting applies. */ readonly workspace_ttl_ms: number; } @@ -4720,7 +4720,7 @@ export const DefaultChatDebugRetentionDays = 30; // From codersdk/chats.go /** * DefaultChatWorkspaceTTL is the default TTL for chat workspaces. - * Zero means disabled — the template's own autostop setting applies. + * Zero means disabled; the template's own autostop setting applies. */ export const DefaultChatWorkspaceTTL = 0; @@ -9925,7 +9925,7 @@ export interface UpdateChatSystemPromptRequest { export interface UpdateChatWorkspaceTTLRequest { /** * WorkspaceTTLMillis is the workspace TTL in milliseconds. - * Zero means disabled — the template's own autostop setting applies. + * Zero means disabled; the template's own autostop setting applies. */ readonly workspace_ttl_ms: number; }