From dd113a48f0cef3c8c65a5481253ca4b8ef1f41b3 Mon Sep 17 00:00:00 2001 From: Michael Suchacz <203725896+ibetitsmike@users.noreply.github.com> Date: Mon, 24 Aug 2026 16:18:36 +0000 Subject: [PATCH 01/15] feat: mount chat API routes under /api/v2 --- coderd/apidoc/docs.go | 6443 +++++++++++++---------- coderd/apidoc/swagger.json | 5155 ++++++++++-------- coderd/chat_routes.go | 166 + coderd/chat_routes_test.go | 185 + coderd/coderd.go | 195 +- coderd/coderdtest/swaggerparser.go | 14 +- coderd/exp_chats.go | 452 +- coderd/exp_chats_acl.go | 12 +- coderd/exp_chats_model_acl.go | 14 +- coderd/mcp.go | 46 +- coderd/mcp_acl.go | 10 +- codersdk/chats.go | 8 +- docs/reference/api/chats.md | 7779 ++++++++++++++++++++-------- docs/reference/api/mcp.md | 633 +++ docs/reference/api/schemas.md | 424 ++ site/src/api/typesGenerated.ts | 6 +- 16 files changed, 14370 insertions(+), 7172 deletions(-) create mode 100644 coderd/chat_routes.go create mode 100644 coderd/chat_routes_test.go diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 94c41bf60ec..f29e0cf8bf9 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -154,29 +154,56 @@ const docTemplate = `{ } } }, - "/api/experimental/chats": { + "/api/experimental/chats/{chat}/stream/desktop": { "get": { - "description": "Experimental: this endpoint is subject to change.", + "description": "Raw binary WebSocket stream of the chat workspace desktop.\nExperimental: this endpoint is subject to change.", "produces": [ - "application/json" + "application/octet-stream" ], "tags": [ "Chats" ], - "summary": "List chats", - "operationId": "list-chats", + "summary": "Connect to chat workspace desktop via WebSockets", + "operationId": "connect-to-chat-workspace-desktop-via-websockets", "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" - }, + "format": "uuid", + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true + } + ], + "responses": { + "101": { + "description": "Switching Protocols" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/experimental/users/{user}/skills": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "List user skills", + "operationId": "list-user-skills", + "parameters": [ { "type": "string", - "description": "Filter by label as key:value. Repeat for multiple (AND logic).", - "name": "label", - "in": "query" + "description": "User ID, username, or me", + "name": "user", + "in": "path", + "required": true } ], "responses": { @@ -185,7 +212,7 @@ const docTemplate = `{ "schema": { "type": "array", "items": { - "$ref": "#/definitions/codersdk.Chat" + "$ref": "#/definitions/codersdk.UserSkillMetadata" } } } @@ -194,10 +221,12 @@ const docTemplate = `{ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } }, "post": { - "description": "Experimental: this endpoint is subject to change.", "consumes": [ "application/json" ], @@ -205,18 +234,25 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Chats" + "Users" ], - "summary": "Create chat", - "operationId": "create-chat", + "summary": "Create a user skill", + "operationId": "create-a-user-skill", "parameters": [ { - "description": "Create chat request", + "type": "string", + "description": "User ID, username, or me", + "name": "user", + "in": "path", + "required": true + }, + { + "description": "Create user skill request", "name": "request", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/codersdk.CreateChatRequest" + "$ref": "#/definitions/codersdk.CreateUserSkillRequest" } } ], @@ -224,13 +260,7 @@ const docTemplate = `{ "201": { "description": "Created", "schema": { - "$ref": "#/definitions/codersdk.Chat" - } - }, - "413": { - "description": "Request body exceeds 256 KiB", - "schema": { - "$ref": "#/definitions/codersdk.Response" + "$ref": "#/definitions/codersdk.UserSkill" } } }, @@ -238,24 +268,43 @@ const docTemplate = `{ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } } }, - "/api/experimental/chats/config/retention-days": { + "/api/experimental/users/{user}/skills/{skillName}": { "get": { "produces": [ "application/json" ], "tags": [ - "Chats" + "Users" + ], + "summary": "Get a user skill by name", + "operationId": "get-a-user-skill-by-name", + "parameters": [ + { + "type": "string", + "description": "User ID, username, or me", + "name": "user", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Skill name", + "name": "skillName", + "in": "path", + "required": true + } ], - "summary": "Get chat retention days", - "operationId": "get-chat-retention-days", "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.ChatRetentionDaysResponse" + "$ref": "#/definitions/codersdk.UserSkill" } } }, @@ -268,24 +317,26 @@ const docTemplate = `{ "skip": true } }, - "put": { - "consumes": [ - "application/json" - ], + "delete": { "tags": [ - "Chats" + "Users" ], - "summary": "Update chat retention days", - "operationId": "update-chat-retention-days", + "summary": "Delete a user skill", + "operationId": "delete-a-user-skill", "parameters": [ { - "description": "Request body", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.UpdateChatRetentionDaysRequest" - } + "type": "string", + "description": "User ID, username, or me", + "name": "user", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Skill name", + "name": "skillName", + "in": "path", + "required": true } ], "responses": { @@ -301,51 +352,49 @@ const docTemplate = `{ "x-apidocgen": { "skip": true } - } - }, - "/api/experimental/chats/files": { - "post": { - "description": "Experimental: this endpoint is subject to change.", + }, + "patch": { "consumes": [ - "image/png", - "image/jpeg", - "image/gif", - "image/webp", - "text/plain", - "text/markdown", - "text/csv", - "application/json", - "application/pdf" + "application/json" ], "produces": [ "application/json" ], "tags": [ - "Chats" + "Users" ], - "summary": "Upload chat file", - "operationId": "upload-chat-file", + "summary": "Update a user skill", + "operationId": "update-a-user-skill", "parameters": [ { "type": "string", - "format": "uuid", - "description": "Organization ID", - "name": "organization", - "in": "query", + "description": "User ID, username, or me", + "name": "user", + "in": "path", "required": true + }, + { + "type": "string", + "description": "Skill name", + "name": "skillName", + "in": "path", + "required": true + }, + { + "description": "Update user skill request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateUserSkillRequest" + } } ], "responses": { - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/codersdk.UploadChatFileResponse" - } - }, - "413": { - "description": "Request body exceeds 10 MiB", + "200": { + "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.Response" + "$ref": "#/definitions/codersdk.UserSkill" } } }, @@ -353,113 +402,73 @@ const docTemplate = `{ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } } }, - "/api/experimental/chats/files/{file}": { + "/api/experimental/watch-all-workspacebuilds": { "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" + "application/json" ], "tags": [ - "Chats" - ], - "summary": "Get chat file", - "operationId": "get-chat-file", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "File ID", - "name": "file", - "in": "path", - "required": true - } + "Workspaces" ], + "summary": "Watch all workspace builds", + "operationId": "watch-all-workspace-builds", "responses": { - "200": { - "description": "OK" + "101": { + "description": "Switching Protocols" } }, "security": [ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } } }, - "/api/experimental/chats/files/{file}/download": { + "/api/v2/": { "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" + "application/json" ], "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 - } + "General" ], + "summary": "API root handler", + "operationId": "api-root-handler", "responses": { "200": { - "description": "OK" + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.Response" + } } - }, - "x-apidocgen": { - "skip": true } } }, - "/api/experimental/chats/files/{file}/download-url": { - "post": { - "description": "Experimental: this endpoint is subject to change.", + "/api/v2/agent-firewall/sessions/{id}": { + "get": { "produces": [ "application/json" ], "tags": [ - "Chats" + "Enterprise" ], - "summary": "Create chat file download URL", - "operationId": "create-chat-file-download-url", + "summary": "Get agent firewall session by ID", + "operationId": "get-agent-firewall-session-by-id", "parameters": [ { "type": "string", "format": "uuid", - "description": "File ID", - "name": "file", + "description": "Agent firewall session ID", + "name": "id", "in": "path", "required": true } @@ -468,36 +477,7 @@ const docTemplate = `{ "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" + "$ref": "#/definitions/codersdk.AgentFirewallSession" } } }, @@ -508,32 +488,49 @@ const docTemplate = `{ ] } }, - "/api/experimental/chats/{chat}": { + "/api/v2/agent-firewall/sessions/{id}/logs": { "get": { - "description": "Experimental: this endpoint is subject to change.", "produces": [ "application/json" ], "tags": [ - "Chats" + "Enterprise" ], - "summary": "Get chat by ID", - "operationId": "get-chat-by-id", + "summary": "Get agent firewall session logs", + "operationId": "get-agent-firewall-session-logs", "parameters": [ { "type": "string", "format": "uuid", - "description": "Chat ID", - "name": "chat", + "description": "Agent firewall session ID", + "name": "id", "in": "path", "required": true + }, + { + "type": "integer", + "description": "Inclusive lower bound on sequence number", + "name": "seq_after", + "in": "query" + }, + { + "type": "integer", + "description": "Exclusive upper bound on sequence number", + "name": "seq_before", + "in": "query" + }, + { + "type": "integer", + "description": "Maximum number of logs to return (default 100)", + "name": "limit", + "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.Chat" + "$ref": "#/definitions/codersdk.AgentFirewallSessionLogsResponse" } } }, @@ -542,40 +539,29 @@ const docTemplate = `{ "CoderSessionToken": [] } ] - }, - "patch": { - "description": "Experimental: this endpoint is subject to change.", - "consumes": [ + } + }, + "/api/v2/ai-gateway/clients": { + "get": { + "description": "Alias: also available at /api/v2/aibridge/clients for backward compatibility.", + "produces": [ "application/json" ], "tags": [ - "Chats" + "AI Gateway" ], - "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, + "summary": "List AI Gateway clients", + "operationId": "list-ai-gateway-clients", + "responses": { + "200": { + "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.UpdateChatRequest" + "type": "array", + "items": { + "type": "string" + } } } - ], - "responses": { - "204": { - "description": "No Content" - } }, "security": [ { @@ -584,32 +570,24 @@ const docTemplate = `{ ] } }, - "/api/experimental/chats/{chat}/acl": { + "/api/v2/ai-gateway/keys": { "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 - } + "Enterprise" ], + "summary": "List AI Gateway keys", + "operationId": "list-ai-gateway-keys", "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.ChatACL" + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.AIGatewayKey" + } } } }, @@ -617,120 +595,94 @@ const docTemplate = `{ { "CoderSessionToken": [] } - ], - "x-apidocgen": { - "skip": true - } + ] }, - "patch": { - "description": "Experimental: this endpoint is subject to change.", + "post": { "consumes": [ "application/json" ], + "produces": [ + "application/json" + ], "tags": [ - "Chats" + "Enterprise" ], - "summary": "Update chat ACL", - "operationId": "update-chat-acl", + "summary": "Create AI Gateway key", + "operationId": "create-ai-gateway-key", "parameters": [ { - "type": "string", - "format": "uuid", - "description": "Chat ID", - "name": "chat", - "in": "path", - "required": true - }, - { - "description": "Update chat ACL request", + "description": "Create AI Gateway key request", "name": "request", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/codersdk.UpdateChatACL" + "$ref": "#/definitions/codersdk.CreateAIGatewayKeyRequest" } } ], "responses": { - "204": { - "description": "No Content" + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/codersdk.CreateAIGatewayKeyResponse" + } } }, "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" - ], + "/api/v2/ai-gateway/keys/{key}": { + "delete": { "tags": [ - "Chats" + "Enterprise" ], - "summary": "Compact chat", - "operationId": "compact-chat", + "summary": "Delete AI Gateway key", + "operationId": "delete-ai-gateway-key", "parameters": [ { "type": "string", "format": "uuid", - "description": "Chat ID", - "name": "chat", + "description": "Key ID", + "name": "key", "in": "path", "required": true } ], "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.Chat" - } + "204": { + "description": "No Content" } }, "security": [ { "CoderSessionToken": [] } - ], - "x-apidocgen": { - "skip": true - } + ] } }, - "/api/experimental/chats/{chat}/context": { - "put": { - "description": "Experimental: this endpoint is subject to change.", + "/api/v2/ai-gateway/models": { + "get": { + "description": "Alias: also available at /api/v2/aibridge/models for backward compatibility.", "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 - } + "AI Gateway" ], + "summary": "List AI Gateway models", + "operationId": "list-ai-gateway-models", "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.Chat" + "type": "array", + "items": { + "type": "string" + } } } }, @@ -741,104 +693,67 @@ const docTemplate = `{ ] } }, - "/api/experimental/chats/{chat}/cost": { + "/api/v2/ai-gateway/serve": { "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 - } + "Enterprise" ], + "summary": "AI Gateway serve", + "operationId": "ai-gateway-serve", "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.ChatCost" - } + "101": { + "description": "Switching Protocols" } }, "security": [ { - "CoderSessionToken": [] + "AIGatewayKey": [] } ] } }, - "/api/experimental/chats/{chat}/diff": { + "/api/v2/ai-gateway/sessions": { "get": { - "description": "Experimental: this endpoint is subject to change.", + "description": "Alias: also available at /api/v2/aibridge/sessions for backward compatibility.", "produces": [ "application/json" ], "tags": [ - "Chats" + "AI Gateway" ], - "summary": "Get chat diff contents", - "operationId": "get-chat-diff-contents", + "summary": "List AI Gateway sessions", + "operationId": "list-ai-gateway-sessions", "parameters": [ { "type": "string", - "format": "uuid", - "description": "Chat ID", - "name": "chat", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.ChatDiffContents" - } - } - }, - "security": [ + "description": "Search query in the format ` + "`" + `key:value` + "`" + `. Available keys are: initiator, provider, provider_name, model, client, session_id, started_after, started_before.", + "name": "q", + "in": "query" + }, { - "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": "integer", + "description": "Page limit", + "name": "limit", + "in": "query" + }, { "type": "string", - "format": "uuid", - "description": "Chat ID", - "name": "chat", - "in": "path", - "required": true + "description": "Cursor pagination after session ID (cannot be used with offset)", + "name": "after_session_id", + "in": "query" + }, + { + "type": "integer", + "description": "Offset pagination (cannot be used with after_session_id)", + "name": "offset", + "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.Chat" + "$ref": "#/definitions/codersdk.AIBridgeListSessionsResponse" } } }, @@ -849,41 +764,40 @@ const docTemplate = `{ ] } }, - "/api/experimental/chats/{chat}/messages": { + "/api/v2/ai-gateway/sessions/{session_id}": { "get": { - "description": "Experimental: this endpoint is subject to change.", + "description": "Alias: also available at /api/v2/aibridge/sessions/{session_id} for backward compatibility.", "produces": [ "application/json" ], "tags": [ - "Chats" + "AI Gateway" ], - "summary": "List chat messages", - "operationId": "list-chat-messages", + "summary": "Get AI Gateway session threads", + "operationId": "get-ai-gateway-session-threads", "parameters": [ { "type": "string", - "format": "uuid", - "description": "Chat ID", - "name": "chat", + "description": "Session ID (client_session_id or interception UUID)", + "name": "session_id", "in": "path", "required": true }, { - "type": "integer", - "description": "Return messages with id \u003c before_id", - "name": "before_id", + "type": "string", + "description": "Thread pagination cursor (forward/older)", + "name": "after_id", "in": "query" }, { - "type": "integer", - "description": "Return messages with id \u003e after_id", - "name": "after_id", + "type": "string", + "description": "Thread pagination cursor (backward/newer)", + "name": "before_id", "in": "query" }, { "type": "integer", - "description": "Page size, 1 to 200. Defaults to 50.", + "description": "Number of threads per page (default 50)", "name": "limit", "in": "query" } @@ -892,7 +806,7 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.ChatMessagesResponse" + "$ref": "#/definitions/codersdk.AIBridgeSessionThreadsResponse" } } }, @@ -901,44 +815,26 @@ const docTemplate = `{ "CoderSessionToken": [] } ] - }, - "post": { - "description": "Experimental: this endpoint is subject to change.", - "consumes": [ - "application/json" - ], + } + }, + "/api/v2/ai/providers": { + "get": { "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" - } - } + "AI Providers" ], + "summary": "List AI providers", + "operationId": "list-ai-providers", "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.CreateChatMessageResponse" + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.AIProvider" + } } } }, @@ -947,11 +843,8 @@ const docTemplate = `{ "CoderSessionToken": [] } ] - } - }, - "/api/experimental/chats/{chat}/messages/{message}": { - "patch": { - "description": "Experimental: this endpoint is subject to change.", + }, + "post": { "consumes": [ "application/json" ], @@ -959,41 +852,26 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Chats" + "AI Providers" ], - "summary": "Edit chat message", - "operationId": "edit-chat-message", + "summary": "Create an AI provider", + "operationId": "create-an-ai-provider", "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", + "description": "Create AI provider request", "name": "request", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/codersdk.EditChatMessageRequest" + "$ref": "#/definitions/codersdk.CreateAIProviderRequest" } } ], "responses": { - "200": { - "description": "OK", + "201": { + "description": "Created", "schema": { - "$ref": "#/definitions/codersdk.EditChatMessageResponse" + "$ref": "#/definitions/codersdk.AIProvider" } } }, @@ -1004,38 +882,30 @@ const docTemplate = `{ ] } }, - "/api/experimental/chats/{chat}/prompts": { + "/api/v2/ai/providers/{idOrName}": { "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" + "AI Providers" ], - "summary": "List chat user prompts", - "operationId": "list-chat-user-prompts", + "summary": "Get an AI provider", + "operationId": "get-an-ai-provider", "parameters": [ { "type": "string", - "format": "uuid", - "description": "Chat ID", - "name": "chat", + "description": "Provider ID or name", + "name": "idOrName", "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" + "$ref": "#/definitions/codersdk.AIProvider" } } }, @@ -1044,35 +914,25 @@ const docTemplate = `{ "CoderSessionToken": [] } ] - } - }, - "/api/experimental/chats/{chat}/reconcile-invalid": { - "post": { - "description": "Experimental: this endpoint is subject to change.", - "produces": [ - "application/json" - ], + }, + "delete": { "tags": [ - "Chats" + "AI Providers" ], - "summary": "Reconcile invalid chat state", - "operationId": "reconcile-invalid-chat-state", + "summary": "Delete an AI provider", + "operationId": "delete-an-ai-provider", "parameters": [ { "type": "string", - "format": "uuid", - "description": "Chat ID", - "name": "chat", + "description": "Provider ID or name", + "name": "idOrName", "in": "path", "required": true } ], "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.Chat" - } + "204": { + "description": "No Content" } }, "security": [ @@ -1080,34 +940,42 @@ const docTemplate = `{ "CoderSessionToken": [] } ] - } - }, - "/api/experimental/chats/{chat}/stream": { - "get": { - "description": "Experimental: this endpoint is subject to change.", + }, + "patch": { + "consumes": [ + "application/json" + ], "produces": [ "application/json" ], "tags": [ - "Chats" + "AI Providers" ], - "summary": "Stream chat events via WebSockets", - "operationId": "stream-chat-events-via-websockets", + "summary": "Update an AI provider", + "operationId": "update-an-ai-provider", "parameters": [ { "type": "string", - "format": "uuid", - "description": "Chat ID", - "name": "chat", + "description": "Provider ID or name", + "name": "idOrName", "in": "path", "required": true + }, + { + "description": "Update AI provider request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateAIProviderRequest" + } } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.ChatStreamEvent" + "$ref": "#/definitions/codersdk.AIProvider" } } }, @@ -1118,30 +986,22 @@ const docTemplate = `{ ] } }, - "/api/experimental/chats/{chat}/stream/desktop": { + "/api/v2/appearance": { "get": { - "description": "Raw binary WebSocket stream of the chat workspace desktop.\nExperimental: this endpoint is subject to change.", "produces": [ - "application/octet-stream" + "application/json" ], "tags": [ - "Chats" - ], - "summary": "Connect to chat workspace desktop via WebSockets", - "operationId": "connect-to-chat-workspace-desktop-via-websockets", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Chat ID", - "name": "chat", - "in": "path", - "required": true - } + "Enterprise" ], + "summary": "Get appearance", + "operationId": "get-appearance", "responses": { - "101": { - "description": "Switching Protocols" + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.AppearanceConfig" + } } }, "security": [ @@ -1149,34 +1009,35 @@ const docTemplate = `{ "CoderSessionToken": [] } ] - } - }, - "/api/experimental/chats/{chat}/stream/git": { - "get": { - "description": "Experimental: this endpoint is subject to change.", + }, + "put": { + "consumes": [ + "application/json" + ], "produces": [ "application/json" ], "tags": [ - "Chats" + "Enterprise" ], - "summary": "Watch chat workspace git state via WebSockets", - "operationId": "watch-chat-workspace-git-state-via-websockets", + "summary": "Update appearance", + "operationId": "update-appearance", "parameters": [ { - "type": "string", - "format": "uuid", - "description": "Chat ID", - "name": "chat", - "in": "path", - "required": true + "description": "Update appearance request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateAppearanceConfig" + } } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.WorkspaceAgentGitServerMessage" + "$ref": "#/definitions/codersdk.UpdateAppearanceConfig" } } }, @@ -1187,71 +1048,49 @@ const docTemplate = `{ ] } }, - "/api/experimental/chats/{chat}/stream/parts": { + "/api/v2/applications/auth-redirect": { "get": { - "description": "Experimental: this endpoint is subject to change.", - "produces": [ - "application/json" - ], "tags": [ - "Chats" + "Applications" ], - "summary": "Stream chat parts via WebSockets", - "operationId": "stream-chat-parts-via-websockets", + "summary": "Redirect to URI with encrypted API key", + "operationId": "redirect-to-uri-with-encrypted-api-key", "parameters": [ { "type": "string", - "format": "uuid", - "description": "Chat ID", - "name": "chat", - "in": "path", - "required": true + "description": "Redirect destination", + "name": "redirect_uri", + "in": "query" } ], "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.ChatStreamEvent" - } + "307": { + "description": "Temporary Redirect" } }, "security": [ { "CoderSessionToken": [] } - ], - "x-apidocgen": { - "skip": true - } + ] } }, - "/api/experimental/chats/{chat}/title/propose": { - "post": { - "description": "Experimental: this endpoint is subject to change.", + "/api/v2/applications/host": { + "get": { "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 - } + "Applications" ], + "summary": "Get applications host", + "operationId": "get-applications-host", + "deprecated": true, "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.ProposeChatTitleResponse" + "$ref": "#/definitions/codersdk.AppHostResponse" } } }, @@ -1262,53 +1101,36 @@ const docTemplate = `{ ] } }, - "/api/experimental/mcp/servers/{mcpServer}/oauth2/callback": { - "get": { + "/api/v2/applications/reconnecting-pty-signed-token": { + "post": { + "consumes": [ + "application/json" + ], "produces": [ - "text/html" + "application/json" ], "tags": [ - "MCP" + "Enterprise" ], - "summary": "Handle MCP server OAuth2 callback", - "operationId": "handle-mcp-server-oauth2-callback", + "summary": "Issue signed app token for reconnecting PTY", + "operationId": "issue-signed-app-token-for-reconnecting-pty", "parameters": [ { - "type": "string", - "format": "uuid", - "description": "MCP server config ID", - "name": "mcpServer", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Authorization code issued by the provider. Required together with state on success.", - "name": "code", - "in": "query" - }, - { - "type": "string", - "description": "Opaque state issued by the connect endpoint. Required together with code on success.", - "name": "state", - "in": "query" - }, - { - "type": "string", - "description": "Provider error code. Present instead of code when authorization fails.", - "name": "error", - "in": "query" - }, - { - "type": "string", - "description": "Provider error description accompanying error.", - "name": "error_description", - "in": "query" + "description": "Issue reconnecting PTY signed token request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.IssueReconnectingPTYSignedTokenRequest" + } } ], "responses": { "200": { - "description": "OK" + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.IssueReconnectingPTYSignedTokenResponse" + } } }, "security": [ @@ -1321,31 +1143,42 @@ const docTemplate = `{ } } }, - "/api/experimental/mcp/servers/{mcpServer}/oauth2/disconnect": { - "delete": { + "/api/v2/audit": { + "get": { "produces": [ "application/json" ], "tags": [ - "MCP" + "Audit" ], - "summary": "Disconnect MCP server OAuth2 token", - "operationId": "disconnect-mcp-server-oauth2-token", + "summary": "Get audit logs", + "operationId": "get-audit-logs", "parameters": [ { "type": "string", - "format": "uuid", - "description": "MCP server config ID", - "name": "mcpServer", - "in": "path", + "description": "Search query", + "name": "q", + "in": "query" + }, + { + "type": "integer", + "description": "Page limit", + "name": "limit", + "in": "query", "required": true + }, + { + "type": "integer", + "description": "Page offset", + "name": "offset", + "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.MCPServerOAuth2DisconnectResponse" + "$ref": "#/definitions/codersdk.AuditLogResponse" } } }, @@ -1353,37 +1186,33 @@ const docTemplate = `{ { "CoderSessionToken": [] } - ], - "x-apidocgen": { - "skip": true - } + ] } }, - "/api/experimental/organizations/{organization}/chats/model-overrides": { - "get": { - "produces": [ + "/api/v2/audit/testgenerate": { + "post": { + "consumes": [ "application/json" ], "tags": [ - "Chats" + "Audit" ], - "summary": "List organization chat model overrides", - "operationId": "list-organization-chat-model-overrides", + "summary": "Generate fake audit log", + "operationId": "generate-fake-audit-log", "parameters": [ { - "type": "string", - "description": "Organization name or ID", - "name": "organization", - "in": "path", - "required": true + "description": "Audit log request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.CreateTestAuditLogRequest" + } } ], "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.ChatModelOverridesResponse" - } + "204": { + "description": "No Content" } }, "security": [ @@ -1396,48 +1225,47 @@ const docTemplate = `{ } } }, - "/api/experimental/organizations/{organization}/chats/model-overrides/{context}": { - "put": { - "consumes": [ - "application/json" - ], + "/api/v2/auth/scopes": { + "get": { "produces": [ "application/json" ], "tags": [ - "Chats" + "Authorization" ], - "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 - }, + "summary": "List API key scopes", + "operationId": "list-api-key-scopes", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.ExternalAPIKeyScopes" + } + } + } + } + }, + "/api/v2/authcheck": { + "post": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Authorization" + ], + "summary": "Check authorization", + "operationId": "check-authorization", + "parameters": [ { - "description": "Model override", + "description": "Authorization request", "name": "request", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/codersdk.UpdateChatModelOverrideRequest" + "$ref": "#/definitions/codersdk.AuthorizationRequest" } } ], @@ -1445,7 +1273,7 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.ChatModelOverrideResponse" + "$ref": "#/definitions/codersdk.AuthorizationResponse" } } }, @@ -1453,13 +1281,30 @@ const docTemplate = `{ { "CoderSessionToken": [] } + ] + } + }, + "/api/v2/buildinfo": { + "get": { + "produces": [ + "application/json" ], - "x-apidocgen": { - "skip": true + "tags": [ + "General" + ], + "summary": "Build info", + "operationId": "build-info", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.BuildInfoResponse" + } + } } } }, - "/api/experimental/organizations/{organization}/chats/models": { + "/api/v2/chats": { "get": { "produces": [ "application/json" @@ -1467,22 +1312,30 @@ const docTemplate = `{ "tags": [ "Chats" ], - "summary": "List AI models and provider descriptors in an organization", - "operationId": "list-ai-models-by-organization", + "summary": "List chats", + "operationId": "list-chats", "parameters": [ { "type": "string", - "description": "Organization name or ID", - "name": "organization", - "in": "path", - "required": true + "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; quote multi-word values; cannot be combined with title, pr_title, or pr; a value that tokenizes to no searchable words 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": { - "$ref": "#/definitions/codersdk.OrganizationChatModelsResponse" + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.Chat" + } } } }, @@ -1490,10 +1343,7 @@ const docTemplate = `{ { "CoderSessionToken": [] } - ], - "x-apidocgen": { - "skip": true - } + ] }, "post": { "consumes": [ @@ -1505,23 +1355,16 @@ const docTemplate = `{ "tags": [ "Chats" ], - "summary": "Create an AI model in an organization", - "operationId": "create-ai-model", + "summary": "Create chat", + "operationId": "create-chat", "parameters": [ { - "type": "string", - "description": "Organization name or ID", - "name": "organization", - "in": "path", - "required": true - }, - { - "description": "Model", + "description": "Create chat request", "name": "request", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/codersdk.CreateChatModelRequest" + "$ref": "#/definitions/codersdk.CreateChatRequest" } } ], @@ -1529,7 +1372,13 @@ const docTemplate = `{ "201": { "description": "Created", "schema": { - "$ref": "#/definitions/codersdk.ChatModel" + "$ref": "#/definitions/codersdk.Chat" + } + }, + "413": { + "description": "Request body exceeds 256 KiB", + "schema": { + "$ref": "#/definitions/codersdk.Response" } } }, @@ -1537,13 +1386,10 @@ const docTemplate = `{ { "CoderSessionToken": [] } - ], - "x-apidocgen": { - "skip": true - } + ] } }, - "/api/experimental/organizations/{organization}/chats/models/{model}": { + "/api/v2/chats/by-workspace": { "get": { "produces": [ "application/json" @@ -1551,29 +1397,21 @@ const docTemplate = `{ "tags": [ "Chats" ], - "summary": "Get an AI model", - "operationId": "get-ai-model", + "summary": "List chats by workspace", + "operationId": "list-chats-by-workspace", "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": "Comma-separated workspace IDs", + "name": "workspace_ids", + "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.ChatModel" + "$ref": "#/definitions/coderd.chatsByWorkspaceResponse" } } }, @@ -1581,103 +1419,66 @@ const docTemplate = `{ { "CoderSessionToken": [] } + ] + } + }, + "/api/v2/chats/config/auto-archive-days": { + "get": { + "produces": [ + "application/json" ], - "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 - } - ], + "summary": "Get chat auto archive days", + "operationId": "get-chat-auto-archive-days", "responses": { - "204": { - "description": "No Content" + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.ChatAutoArchiveDaysResponse" + } } }, "security": [ { "CoderSessionToken": [] } - ], - "x-apidocgen": { - "skip": true - } + ] }, - "patch": { + "put": { "consumes": [ "application/json" ], - "produces": [ - "application/json" - ], "tags": [ "Chats" ], - "summary": "Update an AI model", - "operationId": "update-ai-model", + "summary": "Update chat auto archive days", + "operationId": "update-chat-auto-archive-days", "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", + "description": "Request body", "name": "request", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/codersdk.UpdateChatModelRequest" + "$ref": "#/definitions/codersdk.UpdateChatAutoArchiveDaysRequest" } } ], "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.ChatModel" - } + "204": { + "description": "No Content" } }, "security": [ { "CoderSessionToken": [] } - ], - "x-apidocgen": { - "skip": true - } + ] } }, - "/api/experimental/organizations/{organization}/chats/models/{model}/acl": { + "/api/v2/chats/config/debug-logging": { "get": { "produces": [ "application/json" @@ -1685,30 +1486,13 @@ const docTemplate = `{ "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 - } - ], + "summary": "Get chat debug logging setting", + "operationId": "get-chat-debug-logging-setting", "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.ChatModelACL" + "$ref": "#/definitions/codersdk.ChatDebugLoggingAdminSettings" } } }, @@ -1716,43 +1500,25 @@ const docTemplate = `{ { "CoderSessionToken": [] } - ], - "x-apidocgen": { - "skip": true - } + ] }, - "patch": { + "put": { "consumes": [ "application/json" ], "tags": [ "Chats" ], - "summary": "Update an AI model ACL", - "operationId": "update-ai-model-acl", + "summary": "Update chat debug logging setting", + "operationId": "update-chat-debug-logging-setting", "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", + "description": "Request body", "name": "request", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/codersdk.UpdateChatModelACLRequest" + "$ref": "#/definitions/codersdk.UpdateChatDebugLoggingAllowUsersRequest" } } ], @@ -1765,40 +1531,24 @@ const docTemplate = `{ { "CoderSessionToken": [] } - ], - "x-apidocgen": { - "skip": true - } + ] } }, - "/api/experimental/organizations/{organization}/mcp-servers": { + "/api/v2/chats/config/debug-retention-days": { "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 - } + "Chats" ], + "summary": "Get chat debug retention days", + "operationId": "get-chat-debug-retention-days", "responses": { "200": { "description": "OK", "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/codersdk.MCPServerConfig" - } + "$ref": "#/definitions/codersdk.ChatDebugRetentionDaysResponse" } } }, @@ -1806,93 +1556,55 @@ const docTemplate = `{ { "CoderSessionToken": [] } - ], - "x-apidocgen": { - "skip": true - } + ] }, - "post": { + "put": { "consumes": [ "application/json" ], - "produces": [ - "application/json" - ], "tags": [ - "MCP" + "Chats" ], - "summary": "Create MCP server config", - "operationId": "create-mcp-server-config", + "summary": "Update chat debug retention days", + "operationId": "update-chat-debug-retention-days", "parameters": [ { - "type": "string", - "format": "uuid", - "description": "Organization ID", - "name": "organization", - "in": "path", - "required": true - }, - { - "description": "Create MCP server config request", + "description": "Request body", "name": "request", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/codersdk.CreateMCPServerConfigRequest" + "$ref": "#/definitions/codersdk.UpdateChatDebugRetentionDaysRequest" } } ], "responses": { - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/codersdk.MCPServerConfig" - } + "204": { + "description": "No Content" } }, "security": [ { "CoderSessionToken": [] } - ], - "x-apidocgen": { - "skip": true - } + ] } }, - "/api/experimental/organizations/{organization}/mcp-servers/{mcpserverconfig}": { + "/api/v2/chats/config/personal-model-overrides": { "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 - } + "Chats" ], + "summary": "Get chat personal model override settings", + "operationId": "get-chat-personal-model-override-settings", "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.MCPServerConfig" + "$ref": "#/definitions/codersdk.ChatPersonalModelOverridesAdminSettings" } } }, @@ -1900,33 +1612,26 @@ const docTemplate = `{ { "CoderSessionToken": [] } - ], - "x-apidocgen": { - "skip": true - } + ] }, - "delete": { + "put": { + "consumes": [ + "application/json" + ], "tags": [ - "MCP" + "Chats" ], - "summary": "Delete MCP server config", - "operationId": "delete-mcp-server-config", + "summary": "Update chat personal model override settings", + "operationId": "update-chat-personal-model-override-settings", "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": "Request body", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateChatPersonalModelOverridesAdminSettingsRequest" + } } ], "responses": { @@ -1938,101 +1643,80 @@ const docTemplate = `{ { "CoderSessionToken": [] } + ] + } + }, + "/api/v2/chats/config/plan-mode-instructions": { + "get": { + "produces": [ + "application/json" ], - "x-apidocgen": { - "skip": true - } + "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": [] + } + ] }, - "patch": { + "put": { "consumes": [ "application/json" ], - "produces": [ - "application/json" - ], "tags": [ - "MCP" + "Chats" ], - "summary": "Update MCP server config", - "operationId": "update-mcp-server-config", + "summary": "Update chat plan mode instructions", + "operationId": "update-chat-plan-mode-instructions", "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", + "description": "Request body", "name": "request", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/codersdk.UpdateMCPServerConfigRequest" + "$ref": "#/definitions/codersdk.UpdateChatPlanModeInstructionsRequest" } } ], "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.MCPServerConfig" - } + "204": { + "description": "No Content" } }, "security": [ { "CoderSessionToken": [] } - ], - "x-apidocgen": { - "skip": true - } + ] } }, - "/api/experimental/organizations/{organization}/mcp-servers/{mcpserverconfig}/acl": { + "/api/v2/chats/config/retention-days": { "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 - } + "Chats" ], + "summary": "Get chat retention days", + "operationId": "get-chat-retention-days", "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.MCPServerConfigACL" + "$ref": "#/definitions/codersdk.ChatRetentionDaysResponse" } } }, @@ -2040,44 +1724,25 @@ const docTemplate = `{ { "CoderSessionToken": [] } - ], - "x-apidocgen": { - "skip": true - } + ] }, - "patch": { + "put": { "consumes": [ "application/json" ], "tags": [ - "MCP" + "Chats" ], - "summary": "Update MCP server config ACL", - "operationId": "update-mcp-server-config-acl", + "summary": "Update chat retention days", + "operationId": "update-chat-retention-days", "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", + "description": "Request body", "name": "request", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/codersdk.UpdateMCPServerConfigACLRequest" + "$ref": "#/definitions/codersdk.UpdateChatRetentionDaysRequest" } } ], @@ -2090,53 +1755,66 @@ const docTemplate = `{ { "CoderSessionToken": [] } - ], - "x-apidocgen": { - "skip": true - } + ] } }, - "/api/experimental/organizations/{organization}/mcp-servers/{mcpserverconfig}/oauth2/connect": { + "/api/v2/chats/config/system-prompt": { "get": { + "produces": [ + "application/json" + ], "tags": [ - "MCP" + "Chats" ], - "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 - }, + "summary": "Get chat system prompt", + "operationId": "get-chat-system-prompt", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.ChatSystemPromptResponse" + } + } + }, + "security": [ { - "type": "string", - "format": "uuid", - "description": "MCP server config ID", - "name": "mcpserverconfig", - "in": "path", - "required": true + "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": { - "307": { - "description": "Temporary Redirect" + "204": { + "description": "No Content" } }, "security": [ { "CoderSessionToken": [] } - ], - "x-apidocgen": { - "skip": true - } + ] } }, - "/api/experimental/organizations/{organization}/members/{user}/chats/model-overrides": { + "/api/v2/chats/config/user-compaction-thresholds": { "get": { "produces": [ "application/json" @@ -2144,29 +1822,13 @@ const docTemplate = `{ "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 - } - ], + "summary": "Get user chat compaction thresholds", + "operationId": "get-user-chat-compaction-thresholds", "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.UserChatPersonalModelOverridesResponse" + "$ref": "#/definitions/codersdk.UserChatCompactionThresholds" } } }, @@ -2174,58 +1836,68 @@ const docTemplate = `{ { "CoderSessionToken": [] } - ], - "x-apidocgen": { - "skip": true - } + ] } }, - "/api/experimental/organizations/{organization}/members/{user}/chats/model-overrides/{context}": { + "/api/v2/chats/config/user-compaction-thresholds/{modelConfig}": { "put": { "consumes": [ "application/json" ], + "produces": [ + "application/json" + ], "tags": [ "Chats" ], - "summary": "Update organization member chat model override", - "operationId": "update-organization-member-chat-model-override", + "summary": "Update user chat compaction threshold", + "operationId": "update-user-chat-compaction-threshold", "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", + "description": "Model config ID", + "name": "modelConfig", "in": "path", "required": true }, { - "description": "Personal model override", + "description": "Request body", "name": "request", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/codersdk.UpdateUserChatPersonalModelOverrideRequest" + "$ref": "#/definitions/codersdk.UpdateUserChatCompactionThresholdRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.UserChatCompactionThreshold" } } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + }, + "delete": { + "tags": [ + "Chats" + ], + "summary": "Delete user chat compaction threshold", + "operationId": "delete-user-chat-compaction-threshold", + "parameters": [ + { + "type": "string", + "description": "Model config ID", + "name": "modelConfig", + "in": "path", + "required": true + } ], "responses": { "204": { @@ -2236,39 +1908,24 @@ const docTemplate = `{ { "CoderSessionToken": [] } - ], - "x-apidocgen": { - "skip": true - } + ] } }, - "/api/experimental/users/{user}/skills": { + "/api/v2/chats/config/user-debug-logging": { "get": { "produces": [ "application/json" ], "tags": [ - "Users" - ], - "summary": "List user skills", - "operationId": "list-user-skills", - "parameters": [ - { - "type": "string", - "description": "User ID, username, or me", - "name": "user", - "in": "path", - "required": true - } + "Chats" ], + "summary": "Get user chat debug logging setting", + "operationId": "get-user-chat-debug-logging-setting", "responses": { "200": { "description": "OK", "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/codersdk.UserSkillMetadata" - } + "$ref": "#/definitions/codersdk.UserChatDebugLoggingSettings" } } }, @@ -2276,90 +1933,55 @@ const docTemplate = `{ { "CoderSessionToken": [] } - ], - "x-apidocgen": { - "skip": true - } + ] }, - "post": { + "put": { "consumes": [ "application/json" ], - "produces": [ - "application/json" - ], "tags": [ - "Users" + "Chats" ], - "summary": "Create a user skill", - "operationId": "create-a-user-skill", + "summary": "Update user chat debug logging setting", + "operationId": "update-user-chat-debug-logging-setting", "parameters": [ { - "type": "string", - "description": "User ID, username, or me", - "name": "user", - "in": "path", - "required": true - }, - { - "description": "Create user skill request", + "description": "Request body", "name": "request", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/codersdk.CreateUserSkillRequest" + "$ref": "#/definitions/codersdk.UpdateUserChatDebugLoggingRequest" } } ], "responses": { - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/codersdk.UserSkill" - } + "204": { + "description": "No Content" } }, "security": [ { "CoderSessionToken": [] } - ], - "x-apidocgen": { - "skip": true - } + ] } }, - "/api/experimental/users/{user}/skills/{skillName}": { + "/api/v2/chats/config/user-prompt": { "get": { "produces": [ "application/json" ], "tags": [ - "Users" - ], - "summary": "Get a user skill by name", - "operationId": "get-a-user-skill-by-name", - "parameters": [ - { - "type": "string", - "description": "User ID, username, or me", - "name": "user", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Skill name", - "name": "skillName", - "in": "path", - "required": true - } + "Chats" ], + "summary": "Get user chat custom prompt", + "operationId": "get-user-chat-custom-prompt", "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.UserSkill" + "$ref": "#/definitions/codersdk.UserChatCustomPrompt" } } }, @@ -2367,173 +1989,174 @@ const docTemplate = `{ { "CoderSessionToken": [] } - ], - "x-apidocgen": { - "skip": true - } + ] }, - "delete": { + "put": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], "tags": [ - "Users" + "Chats" ], - "summary": "Delete a user skill", - "operationId": "delete-a-user-skill", + "summary": "Update user chat custom prompt", + "operationId": "update-user-chat-custom-prompt", "parameters": [ { - "type": "string", - "description": "User ID, username, or me", - "name": "user", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Skill name", - "name": "skillName", - "in": "path", - "required": true + "description": "Request body", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UserChatCustomPrompt" + } } ], "responses": { - "204": { - "description": "No Content" + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.UserChatCustomPrompt" + } } }, "security": [ { "CoderSessionToken": [] } + ] + } + }, + "/api/v2/chats/config/workspace-ttl": { + "get": { + "produces": [ + "application/json" ], - "x-apidocgen": { - "skip": true - } + "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": [] + } + ] }, - "patch": { + "put": { "consumes": [ "application/json" ], - "produces": [ - "application/json" - ], "tags": [ - "Users" + "Chats" ], - "summary": "Update a user skill", - "operationId": "update-a-user-skill", + "summary": "Update chat workspace time to live", + "operationId": "update-chat-workspace-time-to-live", "parameters": [ { - "type": "string", - "description": "User ID, username, or me", - "name": "user", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Skill name", - "name": "skillName", - "in": "path", - "required": true - }, - { - "description": "Update user skill request", + "description": "Request body", "name": "request", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/codersdk.UpdateUserSkillRequest" + "$ref": "#/definitions/codersdk.UpdateChatWorkspaceTTLRequest" } } ], "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.UserSkill" - } + "204": { + "description": "No Content" } }, "security": [ { "CoderSessionToken": [] } - ], - "x-apidocgen": { - "skip": true - } + ] } }, - "/api/experimental/watch-all-workspacebuilds": { - "get": { + "/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": [ - "Workspaces" + "Chats" ], - "summary": "Watch all workspace builds", - "operationId": "watch-all-workspace-builds", - "responses": { - "101": { - "description": "Switching Protocols" - } - }, - "security": [ + "summary": "Upload chat file", + "operationId": "upload-chat-file", + "parameters": [ { - "CoderSessionToken": [] + "type": "string", + "format": "uuid", + "description": "Organization ID", + "name": "organization", + "in": "query", + "required": true } ], - "x-apidocgen": { - "skip": true - } - } - }, - "/api/v2/": { - "get": { - "produces": [ - "application/json" - ], - "tags": [ - "General" - ], - "summary": "API root handler", - "operationId": "api-root-handler", "responses": { - "200": { - "description": "OK", + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/codersdk.UploadChatFileResponse" + } + }, + "413": { + "description": "Request body exceeds 10 MiB", "schema": { "$ref": "#/definitions/codersdk.Response" } } - } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] } }, - "/api/v2/agent-firewall/sessions/{id}": { + "/api/v2/chats/files/{file}": { "get": { - "produces": [ - "application/json" - ], "tags": [ - "Enterprise" + "Chats" ], - "summary": "Get agent firewall session by ID", - "operationId": "get-agent-firewall-session-by-id", + "summary": "Get chat file", + "operationId": "get-chat-file", "parameters": [ { "type": "string", "format": "uuid", - "description": "Agent firewall session ID", - "name": "id", + "description": "File ID", + "name": "file", "in": "path", "required": true } ], "responses": { "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.AgentFirewallSession" - } + "description": "OK" } }, "security": [ @@ -2543,49 +2166,62 @@ const docTemplate = `{ ] } }, - "/api/v2/agent-firewall/sessions/{id}/logs": { + "/api/v2/chats/files/{file}/download": { "get": { - "produces": [ - "application/json" - ], "tags": [ - "Enterprise" + "Chats" ], - "summary": "Get agent firewall session logs", - "operationId": "get-agent-firewall-session-logs", + "summary": "Download chat file with signed token", + "operationId": "download-chat-file-with-signed-token", "parameters": [ { "type": "string", "format": "uuid", - "description": "Agent firewall session ID", - "name": "id", + "description": "File ID", + "name": "file", "in": "path", "required": true }, { - "type": "integer", - "description": "Inclusive lower bound on sequence number", - "name": "seq_after", - "in": "query" - }, - { - "type": "integer", - "description": "Exclusive upper bound on sequence number", - "name": "seq_before", - "in": "query" - }, + "type": "string", + "description": "Signed download token", + "name": "token", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/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": "integer", - "description": "Maximum number of logs to return (default 100)", - "name": "limit", - "in": "query" + "type": "string", + "format": "uuid", + "description": "File ID", + "name": "file", + "in": "path", + "required": true } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.AgentFirewallSessionLogsResponse" + "$ref": "#/definitions/codersdk.ChatFileDownloadURLResponse" } } }, @@ -2596,25 +2232,21 @@ const docTemplate = `{ ] } }, - "/api/v2/ai-gateway/clients": { + "/api/v2/chats/models": { "get": { - "description": "Alias: also available at /api/v2/aibridge/clients for backward compatibility.", "produces": [ "application/json" ], "tags": [ - "AI Gateway" + "Chats" ], - "summary": "List AI Gateway clients", - "operationId": "list-ai-gateway-clients", + "summary": "List AI models and provider descriptors in the default organization", + "operationId": "list-ai-models-and-provider-descriptors-in-the-default-organization", "responses": { "200": { "description": "OK", "schema": { - "type": "array", - "items": { - "type": "string" - } + "$ref": "#/definitions/codersdk.OrganizationChatModelsResponse" } } }, @@ -2625,24 +2257,21 @@ const docTemplate = `{ ] } }, - "/api/v2/ai-gateway/keys": { + "/api/v2/chats/watch": { "get": { "produces": [ "application/json" ], "tags": [ - "Enterprise" + "Chats" ], - "summary": "List AI Gateway keys", - "operationId": "list-ai-gateway-keys", + "summary": "Watch chat events for a user via WebSockets", + "operationId": "watch-chat-events-for-a-user-via-websockets", "responses": { "200": { "description": "OK", "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/codersdk.AIGatewayKey" - } + "$ref": "#/definitions/codersdk.ChatWatchEvent" } } }, @@ -2651,35 +2280,33 @@ const docTemplate = `{ "CoderSessionToken": [] } ] - }, - "post": { - "consumes": [ - "application/json" - ], + } + }, + "/api/v2/chats/{chat}": { + "get": { "produces": [ "application/json" ], "tags": [ - "Enterprise" + "Chats" ], - "summary": "Create AI Gateway key", - "operationId": "create-ai-gateway-key", + "summary": "Get chat by ID", + "operationId": "get-chat-by-id", "parameters": [ { - "description": "Create AI Gateway key request", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.CreateAIGatewayKeyRequest" - } + "type": "string", + "format": "uuid", + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true } ], "responses": { - "201": { - "description": "Created", + "200": { + "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.CreateAIGatewayKeyResponse" + "$ref": "#/definitions/codersdk.Chat" } } }, @@ -2688,23 +2315,33 @@ const docTemplate = `{ "CoderSessionToken": [] } ] - } - }, - "/api/v2/ai-gateway/keys/{key}": { - "delete": { + }, + "patch": { + "consumes": [ + "application/json" + ], "tags": [ - "Enterprise" + "Chats" ], - "summary": "Delete AI Gateway key", - "operationId": "delete-ai-gateway-key", + "summary": "Update chat", + "operationId": "update-chat", "parameters": [ { "type": "string", "format": "uuid", - "description": "Key ID", - "name": "key", + "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": { @@ -2719,25 +2356,31 @@ const docTemplate = `{ ] } }, - "/api/v2/ai-gateway/models": { + "/api/v2/chats/{chat}/acl": { "get": { - "description": "Alias: also available at /api/v2/aibridge/models for backward compatibility.", "produces": [ "application/json" ], "tags": [ - "AI Gateway" + "Chats" + ], + "summary": "Get chat ACLs", + "operationId": "get-chat-acls", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true + } ], - "summary": "List AI Gateway models", - "operationId": "list-ai-gateway-models", "responses": { "200": { "description": "OK", "schema": { - "type": "array", - "items": { - "type": "string" - } + "$ref": "#/definitions/codersdk.ChatACL" } } }, @@ -2746,69 +2389,73 @@ const docTemplate = `{ "CoderSessionToken": [] } ] - } - }, - "/api/v2/ai-gateway/serve": { - "get": { + }, + "patch": { + "consumes": [ + "application/json" + ], "tags": [ - "Enterprise" + "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" + } + } ], - "summary": "AI Gateway serve", - "operationId": "ai-gateway-serve", "responses": { - "101": { - "description": "Switching Protocols" + "204": { + "description": "No Content" } }, "security": [ { - "AIGatewayKey": [] + "CoderSessionToken": [] } ] } }, - "/api/v2/ai-gateway/sessions": { - "get": { - "description": "Alias: also available at /api/v2/aibridge/sessions for backward compatibility.", + "/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": [ - "AI Gateway" + "Chats" ], - "summary": "List AI Gateway sessions", - "operationId": "list-ai-gateway-sessions", + "summary": "Compact chat", + "operationId": "compact-chat", "parameters": [ { "type": "string", - "description": "Search query in the format ` + "`" + `key:value` + "`" + `. Available keys are: initiator, provider, provider_name, model, client, session_id, started_after, started_before.", - "name": "q", - "in": "query" - }, - { - "type": "integer", - "description": "Page limit", - "name": "limit", - "in": "query" - }, - { - "type": "string", - "description": "Cursor pagination after session ID (cannot be used with offset)", - "name": "after_session_id", - "in": "query" - }, - { - "type": "integer", - "description": "Offset pagination (cannot be used with after_session_id)", - "name": "offset", - "in": "query" + "format": "uuid", + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.AIBridgeListSessionsResponse" + "$ref": "#/definitions/codersdk.Chat" } } }, @@ -2819,49 +2466,31 @@ const docTemplate = `{ ] } }, - "/api/v2/ai-gateway/sessions/{session_id}": { - "get": { - "description": "Alias: also available at /api/v2/aibridge/sessions/{session_id} for backward compatibility.", + "/api/v2/chats/{chat}/context": { + "put": { "produces": [ "application/json" ], "tags": [ - "AI Gateway" + "Chats" ], - "summary": "Get AI Gateway session threads", - "operationId": "get-ai-gateway-session-threads", + "summary": "Refresh chat context", + "operationId": "refresh-chat-context", "parameters": [ { "type": "string", - "description": "Session ID (client_session_id or interception UUID)", - "name": "session_id", + "format": "uuid", + "description": "Chat ID", + "name": "chat", "in": "path", "required": true - }, - { - "type": "string", - "description": "Thread pagination cursor (forward/older)", - "name": "after_id", - "in": "query" - }, - { - "type": "string", - "description": "Thread pagination cursor (backward/newer)", - "name": "before_id", - "in": "query" - }, - { - "type": "integer", - "description": "Number of threads per page (default 50)", - "name": "limit", - "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.AIBridgeSessionThreadsResponse" + "$ref": "#/definitions/codersdk.Chat" } } }, @@ -2872,24 +2501,32 @@ const docTemplate = `{ ] } }, - "/api/v2/ai/providers": { + "/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": [ - "AI Providers" + "Chats" + ], + "summary": "Get chat cost", + "operationId": "get-chat-cost", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true + } ], - "summary": "List AI providers", - "operationId": "list-ai-providers", "responses": { "200": { "description": "OK", "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/codersdk.AIProvider" - } + "$ref": "#/definitions/codersdk.ChatCost" } } }, @@ -2898,35 +2535,33 @@ const docTemplate = `{ "CoderSessionToken": [] } ] - }, - "post": { - "consumes": [ - "application/json" - ], + } + }, + "/api/v2/chats/{chat}/diff": { + "get": { "produces": [ "application/json" ], "tags": [ - "AI Providers" + "Chats" ], - "summary": "Create an AI provider", - "operationId": "create-an-ai-provider", + "summary": "Get chat diff contents", + "operationId": "get-chat-diff-contents", "parameters": [ { - "description": "Create AI provider request", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.CreateAIProviderRequest" - } + "type": "string", + "format": "uuid", + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true } ], "responses": { - "201": { - "description": "Created", + "200": { + "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.AIProvider" + "$ref": "#/definitions/codersdk.ChatDiffContents" } } }, @@ -2937,21 +2572,22 @@ const docTemplate = `{ ] } }, - "/api/v2/ai/providers/{idOrName}": { - "get": { + "/api/v2/chats/{chat}/interrupt": { + "post": { "produces": [ "application/json" ], "tags": [ - "AI Providers" + "Chats" ], - "summary": "Get an AI provider", - "operationId": "get-an-ai-provider", + "summary": "Interrupt chat", + "operationId": "interrupt-chat", "parameters": [ { "type": "string", - "description": "Provider ID or name", - "name": "idOrName", + "format": "uuid", + "description": "Chat ID", + "name": "chat", "in": "path", "required": true } @@ -2960,7 +2596,7 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.AIProvider" + "$ref": "#/definitions/codersdk.Chat" } } }, @@ -2969,25 +2605,52 @@ const docTemplate = `{ "CoderSessionToken": [] } ] - }, - "delete": { + } + }, + "/api/v2/chats/{chat}/messages": { + "get": { + "produces": [ + "application/json" + ], "tags": [ - "AI Providers" + "Chats" ], - "summary": "Delete an AI provider", - "operationId": "delete-an-ai-provider", + "summary": "List chat messages", + "operationId": "list-chat-messages", "parameters": [ { "type": "string", - "description": "Provider ID or name", - "name": "idOrName", + "format": "uuid", + "description": "Chat ID", + "name": "chat", "in": "path", "required": true - } - ], - "responses": { - "204": { - "description": "No Content" + }, + { + "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": [ @@ -2996,7 +2659,7 @@ const docTemplate = `{ } ] }, - "patch": { + "post": { "consumes": [ "application/json" ], @@ -3004,25 +2667,26 @@ const docTemplate = `{ "application/json" ], "tags": [ - "AI Providers" + "Chats" ], - "summary": "Update an AI provider", - "operationId": "update-an-ai-provider", + "summary": "Send chat message", + "operationId": "send-chat-message", "parameters": [ { "type": "string", - "description": "Provider ID or name", - "name": "idOrName", + "format": "uuid", + "description": "Chat ID", + "name": "chat", "in": "path", "required": true }, { - "description": "Update AI provider request", + "description": "Create chat message request", "name": "request", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/codersdk.UpdateAIProviderRequest" + "$ref": "#/definitions/codersdk.CreateChatMessageRequest" } } ], @@ -3030,7 +2694,7 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.AIProvider" + "$ref": "#/definitions/codersdk.CreateChatMessageResponse" } } }, @@ -3041,21 +2705,50 @@ const docTemplate = `{ ] } }, - "/api/v2/appearance": { - "get": { + "/api/v2/chats/{chat}/messages/{message}": { + "patch": { + "consumes": [ + "application/json" + ], "produces": [ "application/json" ], "tags": [ - "Enterprise" + "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" + } + } ], - "summary": "Get appearance", - "operationId": "get-appearance", "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.AppearanceConfig" + "$ref": "#/definitions/codersdk.EditChatMessageResponse" } } }, @@ -3064,35 +2757,40 @@ const docTemplate = `{ "CoderSessionToken": [] } ] - }, - "put": { - "consumes": [ - "application/json" - ], + } + }, + "/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": [ - "Enterprise" + "Chats" ], - "summary": "Update appearance", - "operationId": "update-appearance", + "summary": "List chat user prompts", + "operationId": "list-chat-user-prompts", "parameters": [ { - "description": "Update appearance request", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.UpdateAppearanceConfig" - } + "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.UpdateAppearanceConfig" + "$ref": "#/definitions/codersdk.ChatPromptsResponse" } } }, @@ -3103,24 +2801,32 @@ const docTemplate = `{ ] } }, - "/api/v2/applications/auth-redirect": { - "get": { + "/api/v2/chats/{chat}/queue/{queuedMessage}": { + "delete": { "tags": [ - "Applications" + "Chats" ], - "summary": "Redirect to URI with encrypted API key", - "operationId": "redirect-to-uri-with-encrypted-api-key", + "summary": "Delete chat queued message", + "operationId": "delete-chat-queued-message", "parameters": [ { "type": "string", - "description": "Redirect destination", - "name": "redirect_uri", - "in": "query" + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Queued message ID", + "name": "queuedMessage", + "in": "path", + "required": true } ], "responses": { - "307": { - "description": "Temporary Redirect" + "204": { + "description": "No Content" } }, "security": [ @@ -3130,22 +2836,37 @@ const docTemplate = `{ ] } }, - "/api/v2/applications/host": { - "get": { + "/api/v2/chats/{chat}/queue/{queuedMessage}/promote": { + "post": { "produces": [ "application/json" ], "tags": [ - "Applications" + "Chats" + ], + "summary": "Promote chat queued message", + "operationId": "promote-chat-queued-message", + "parameters": [ + { + "type": "string", + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Queued message ID", + "name": "queuedMessage", + "in": "path", + "required": true + } ], - "summary": "Get applications host", - "operationId": "get-applications-host", - "deprecated": true, "responses": { - "200": { - "description": "OK", + "202": { + "description": "Accepted", "schema": { - "$ref": "#/definitions/codersdk.AppHostResponse" + "$ref": "#/definitions/codersdk.Response" } } }, @@ -3156,35 +2877,31 @@ const docTemplate = `{ ] } }, - "/api/v2/applications/reconnecting-pty-signed-token": { + "/api/v2/chats/{chat}/reconcile-invalid": { "post": { - "consumes": [ - "application/json" - ], "produces": [ "application/json" ], "tags": [ - "Enterprise" + "Chats" ], - "summary": "Issue signed app token for reconnecting PTY", - "operationId": "issue-signed-app-token-for-reconnecting-pty", + "summary": "Reconcile invalid chat state", + "operationId": "reconcile-invalid-chat-state", "parameters": [ { - "description": "Issue reconnecting PTY signed token request", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.IssueReconnectingPTYSignedTokenRequest" - } + "type": "string", + "format": "uuid", + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.IssueReconnectingPTYSignedTokenResponse" + "$ref": "#/definitions/codersdk.Chat" } } }, @@ -3192,48 +2909,34 @@ const docTemplate = `{ { "CoderSessionToken": [] } - ], - "x-apidocgen": { - "skip": true - } + ] } }, - "/api/v2/audit": { + "/api/v2/chats/{chat}/stream": { "get": { "produces": [ "application/json" ], "tags": [ - "Audit" + "Chats" ], - "summary": "Get audit logs", - "operationId": "get-audit-logs", + "summary": "Stream chat events via WebSockets", + "operationId": "stream-chat-events-via-websockets", "parameters": [ { "type": "string", - "description": "Search query", - "name": "q", - "in": "query" - }, - { - "type": "integer", - "description": "Page limit", - "name": "limit", - "in": "query", + "format": "uuid", + "description": "Chat ID", + "name": "chat", + "in": "path", "required": true - }, - { - "type": "integer", - "description": "Page offset", - "name": "offset", - "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.AuditLogResponse" + "$ref": "#/definitions/codersdk.ChatStreamEvent" } } }, @@ -3244,91 +2947,100 @@ const docTemplate = `{ ] } }, - "/api/v2/audit/testgenerate": { - "post": { - "consumes": [ + "/api/v2/chats/{chat}/stream/git": { + "get": { + "produces": [ "application/json" ], "tags": [ - "Audit" + "Chats" ], - "summary": "Generate fake audit log", - "operationId": "generate-fake-audit-log", + "summary": "Watch chat workspace git state via WebSockets", + "operationId": "watch-chat-workspace-git-state-via-websockets", "parameters": [ { - "description": "Audit log request", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.CreateTestAuditLogRequest" - } + "type": "string", + "format": "uuid", + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true } ], "responses": { - "204": { - "description": "No Content" + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.WorkspaceAgentGitServerMessage" + } } }, "security": [ { "CoderSessionToken": [] } - ], - "x-apidocgen": { - "skip": true - } + ] } }, - "/api/v2/auth/scopes": { + "/api/v2/chats/{chat}/stream/parts": { "get": { "produces": [ "application/json" ], "tags": [ - "Authorization" + "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 + } ], - "summary": "List API key scopes", - "operationId": "list-api-key-scopes", "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.ExternalAPIKeyScopes" + "$ref": "#/definitions/codersdk.ChatStreamEvent" } } - } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] } }, - "/api/v2/authcheck": { + "/api/v2/chats/{chat}/title/propose": { "post": { - "consumes": [ - "application/json" - ], "produces": [ "application/json" ], "tags": [ - "Authorization" + "Chats" ], - "summary": "Check authorization", - "operationId": "check-authorization", + "summary": "Propose chat title", + "operationId": "propose-chat-title", "parameters": [ { - "description": "Authorization request", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.AuthorizationRequest" - } + "type": "string", + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.AuthorizationResponse" + "$ref": "#/definitions/codersdk.ProposeChatTitleResponse" } } }, @@ -3339,24 +3051,79 @@ const docTemplate = `{ ] } }, - "/api/v2/buildinfo": { - "get": { + "/api/v2/chats/{chat}/title/regenerate": { + "post": { "produces": [ "application/json" ], "tags": [ - "General" + "Chats" + ], + "summary": "Regenerate chat title", + "operationId": "regenerate-chat-title", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true + } ], - "summary": "Build info", - "operationId": "build-info", "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.BuildInfoResponse" + "$ref": "#/definitions/codersdk.Chat" } } - } + }, + "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", + "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": { @@ -3939,21 +3706,959 @@ const docTemplate = `{ ] } }, - "/api/v2/deployment/stats": { + "/api/v2/deployment/stats": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "General" + ], + "summary": "Get deployment stats", + "operationId": "get-deployment-stats", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.DeploymentStats" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/derp-map": { + "get": { + "tags": [ + "Agents" + ], + "summary": "Get DERP map updates", + "operationId": "get-derp-map-updates", + "responses": { + "101": { + "description": "Switching Protocols" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/entitlements": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Enterprise" + ], + "summary": "Get entitlements", + "operationId": "get-entitlements", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.Entitlements" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/experiments": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "General" + ], + "summary": "Get enabled experiments", + "operationId": "get-enabled-experiments", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.Experiment" + } + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/experiments/available": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "General" + ], + "summary": "Get safe experiments", + "operationId": "get-safe-experiments", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.Experiment" + } + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/external-auth": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Git" + ], + "summary": "Get user external auths", + "operationId": "get-user-external-auths", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.ExternalAuthLink" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/external-auth/{externalauth}": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Git" + ], + "summary": "Get external auth by ID", + "operationId": "get-external-auth-by-id", + "parameters": [ + { + "type": "string", + "format": "string", + "description": "Git Provider ID", + "name": "externalauth", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.ExternalAuth" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + }, + "delete": { + "produces": [ + "application/json" + ], + "tags": [ + "Git" + ], + "summary": "Delete external auth user link by ID", + "operationId": "delete-external-auth-user-link-by-id", + "parameters": [ + { + "type": "string", + "format": "string", + "description": "Git Provider ID", + "name": "externalauth", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.DeleteExternalAuthByIDResponse" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/external-auth/{externalauth}/device": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Git" + ], + "summary": "Get external auth device by ID.", + "operationId": "get-external-auth-device-by-id", + "parameters": [ + { + "type": "string", + "format": "string", + "description": "Git Provider ID", + "name": "externalauth", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.ExternalAuthDevice" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + }, + "post": { + "tags": [ + "Git" + ], + "summary": "Post external auth device by ID", + "operationId": "post-external-auth-device-by-id", + "parameters": [ + { + "type": "string", + "format": "string", + "description": "External Provider ID", + "name": "externalauth", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/files": { + "post": { + "description": "Swagger notice: Swagger 2.0 doesn't support file upload with a ` + "`" + `content-type` + "`" + ` different than ` + "`" + `application/x-www-form-urlencoded` + "`" + `.", + "consumes": [ + "application/x-tar" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Files" + ], + "summary": "Upload file", + "operationId": "upload-file", + "parameters": [ + { + "type": "string", + "default": "application/x-tar", + "description": "Content-Type must be ` + "`" + `application/x-tar` + "`" + ` or ` + "`" + `application/zip` + "`" + `", + "name": "Content-Type", + "in": "header", + "required": true + }, + { + "type": "file", + "description": "File to be uploaded. If using tar format, file must conform to ustar (pax may cause problems).", + "name": "file", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "Returns existing file if duplicate", + "schema": { + "$ref": "#/definitions/codersdk.UploadResponse" + } + }, + "201": { + "description": "Returns newly created file", + "schema": { + "$ref": "#/definitions/codersdk.UploadResponse" + } + }, + "413": { + "description": "Request body exceeds 100 MiB, or a .zip archive exceeds it once expanded", + "schema": { + "$ref": "#/definitions/codersdk.Response" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/files/{fileID}": { + "get": { + "tags": [ + "Files" + ], + "summary": "Get file by ID", + "operationId": "get-file-by-id", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "File ID", + "name": "fileID", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/groups": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Enterprise" + ], + "summary": "Get groups", + "operationId": "get-groups", + "parameters": [ + { + "type": "string", + "description": "Organization ID or name", + "name": "organization", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "User ID or name", + "name": "has_member", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "Comma separated list of group IDs", + "name": "group_ids", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.Group" + } + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/groups/{group}": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Enterprise" + ], + "summary": "Get group by ID", + "operationId": "get-group-by-id", + "parameters": [ + { + "type": "string", + "description": "Group id", + "name": "group", + "in": "path", + "required": true + }, + { + "type": "boolean", + "description": "Exclude members from the response", + "name": "exclude_members", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.Group" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + }, + "delete": { + "produces": [ + "application/json" + ], + "tags": [ + "Enterprise" + ], + "summary": "Delete group by name", + "operationId": "delete-group-by-name", + "parameters": [ + { + "type": "string", + "description": "Group name", + "name": "group", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.Group" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + }, + "patch": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Enterprise" + ], + "summary": "Update group by name", + "operationId": "update-group-by-name", + "parameters": [ + { + "type": "string", + "description": "Group name", + "name": "group", + "in": "path", + "required": true + }, + { + "description": "Patch group request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.PatchGroupRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.Group" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/groups/{group}/ai/budget": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Enterprise" + ], + "summary": "Get group AI budget", + "operationId": "get-group-ai-budget", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Group ID", + "name": "group", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.GroupAIBudget" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + }, + "put": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Enterprise" + ], + "summary": "Upsert group AI budget", + "operationId": "upsert-group-ai-budget", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Group ID", + "name": "group", + "in": "path", + "required": true + }, + { + "description": "Upsert group AI budget request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpsertGroupAIBudgetRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.GroupAIBudget" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + }, + "delete": { + "tags": [ + "Enterprise" + ], + "summary": "Delete group AI budget", + "operationId": "delete-group-ai-budget", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Group ID", + "name": "group", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/groups/{group}/ai/spend": { + "get": { + "description": "Returns the AI spend limit and aggregate spend for the group.", + "produces": [ + "application/json" + ], + "tags": [ + "Enterprise" + ], + "summary": "Get group AI spend", + "operationId": "get-group-ai-spend", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Group ID", + "name": "group", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.GroupAISpend" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/groups/{group}/members": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Enterprise" + ], + "summary": "Get group members by group ID", + "operationId": "get-group-members-by-group-id", + "parameters": [ + { + "type": "string", + "description": "Group id", + "name": "group", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Member search query", + "name": "q", + "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": { + "$ref": "#/definitions/codersdk.GroupMembersResponse" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/groups/{group}/members/ai/spend": { + "get": { + "description": "Returns aggregate AI spend attributed to the group per requested user.\nA maximum of 100 user IDs may be requested per call, and requests with more are rejected, so callers are expected to batch across multiple requests.\nUser IDs that are not members of the group, or that the caller has no read access to, are silently omitted.", + "produces": [ + "application/json" + ], + "tags": [ + "Enterprise" + ], + "summary": "Get group members AI spend", + "operationId": "get-group-members-ai-spend", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Group ID", + "name": "group", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Comma-separated list of user IDs (maximum 100)", + "name": "user_ids", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.GroupMembersAISpend" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/init-script/{os}/{arch}": { + "get": { + "produces": [ + "text/plain" + ], + "tags": [ + "InitScript" + ], + "summary": "Get agent init script", + "operationId": "get-agent-init-script", + "parameters": [ + { + "type": "string", + "description": "Operating system", + "name": "os", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Architecture", + "name": "arch", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "Success" + } + } + } + }, + "/api/v2/insights/daus": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Insights" + ], + "summary": "Get deployment DAUs", + "operationId": "get-deployment-daus", + "parameters": [ + { + "type": "integer", + "description": "Time-zone offset (e.g. -2)", + "name": "tz_offset", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.DAUsResponse" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/insights/templates": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Insights" + ], + "summary": "Get insights about templates", + "operationId": "get-insights-about-templates", + "parameters": [ + { + "type": "string", + "format": "date-time", + "description": "Start time", + "name": "start_time", + "in": "query", + "required": true + }, + { + "type": "string", + "format": "date-time", + "description": "End time", + "name": "end_time", + "in": "query", + "required": true + }, + { + "enum": [ + "week", + "day" + ], + "type": "string", + "description": "Interval", + "name": "interval", + "in": "query", + "required": true + }, + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "csv", + "description": "Template IDs", + "name": "template_ids", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.TemplateInsightsResponse" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/insights/user-activity": { "get": { "produces": [ "application/json" ], "tags": [ - "General" + "Insights" + ], + "summary": "Get insights about user activity", + "operationId": "get-insights-about-user-activity", + "parameters": [ + { + "type": "string", + "format": "date-time", + "description": "Start time", + "name": "start_time", + "in": "query", + "required": true + }, + { + "type": "string", + "format": "date-time", + "description": "End time", + "name": "end_time", + "in": "query", + "required": true + }, + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "csv", + "description": "Template IDs", + "name": "template_ids", + "in": "query" + } ], - "summary": "Get deployment stats", - "operationId": "get-deployment-stats", "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.DeploymentStats" + "$ref": "#/definitions/codersdk.UserActivityInsightsResponse" } } }, @@ -3964,16 +4669,50 @@ const docTemplate = `{ ] } }, - "/api/v2/derp-map": { + "/api/v2/insights/user-latency": { "get": { + "produces": [ + "application/json" + ], "tags": [ - "Agents" + "Insights" + ], + "summary": "Get insights about user latency", + "operationId": "get-insights-about-user-latency", + "parameters": [ + { + "type": "string", + "format": "date-time", + "description": "Start time", + "name": "start_time", + "in": "query", + "required": true + }, + { + "type": "string", + "format": "date-time", + "description": "End time", + "name": "end_time", + "in": "query", + "required": true + }, + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "csv", + "description": "Template IDs", + "name": "template_ids", + "in": "query" + } ], - "summary": "Get DERP map updates", - "operationId": "get-derp-map-updates", "responses": { - "101": { - "description": "Switching Protocols" + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.UserLatencyInsightsResponse" + } } }, "security": [ @@ -3983,21 +4722,35 @@ const docTemplate = `{ ] } }, - "/api/v2/entitlements": { + "/api/v2/insights/user-status-counts": { "get": { "produces": [ "application/json" ], "tags": [ - "Enterprise" + "Insights" + ], + "summary": "Get insights about user status counts", + "operationId": "get-insights-about-user-status-counts", + "parameters": [ + { + "type": "string", + "description": "IANA timezone name (e.g. America/St_Johns)", + "name": "timezone", + "in": "query" + }, + { + "type": "integer", + "description": "Deprecated: Time-zone offset (e.g. -2). Use timezone instead.", + "name": "tz_offset", + "in": "query" + } ], - "summary": "Get entitlements", - "operationId": "get-entitlements", "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.Entitlements" + "$ref": "#/definitions/codersdk.GetUserStatusCountsResponse" } } }, @@ -4008,23 +4761,23 @@ const docTemplate = `{ ] } }, - "/api/v2/experiments": { + "/api/v2/licenses": { "get": { "produces": [ "application/json" ], "tags": [ - "General" + "Enterprise" ], - "summary": "Get enabled experiments", - "operationId": "get-enabled-experiments", + "summary": "Get licenses", + "operationId": "get-licenses", "responses": { "200": { "description": "OK", "schema": { "type": "array", "items": { - "$ref": "#/definitions/codersdk.Experiment" + "$ref": "#/definitions/codersdk.License" } } } @@ -4034,26 +4787,35 @@ const docTemplate = `{ "CoderSessionToken": [] } ] - } - }, - "/api/v2/experiments/available": { - "get": { + }, + "post": { + "consumes": [ + "application/json" + ], "produces": [ "application/json" ], "tags": [ - "General" + "Enterprise" + ], + "summary": "Add new license", + "operationId": "add-new-license", + "parameters": [ + { + "description": "Add license request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.AddLicenseRequest" + } + } ], - "summary": "Get safe experiments", - "operationId": "get-safe-experiments", "responses": { - "200": { - "description": "OK", + "201": { + "description": "Created", "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/codersdk.Experiment" - } + "$ref": "#/definitions/codersdk.License" } } }, @@ -4064,21 +4826,21 @@ const docTemplate = `{ ] } }, - "/api/v2/external-auth": { - "get": { + "/api/v2/licenses/refresh-entitlements": { + "post": { "produces": [ "application/json" ], "tags": [ - "Git" + "Enterprise" ], - "summary": "Get user external auths", - "operationId": "get-user-external-auths", + "summary": "Update license entitlements", + "operationId": "update-license-entitlements", "responses": { - "200": { - "description": "OK", + "201": { + "description": "Created", "schema": { - "$ref": "#/definitions/codersdk.ExternalAuthLink" + "$ref": "#/definitions/codersdk.Response" } } }, @@ -4089,31 +4851,35 @@ const docTemplate = `{ ] } }, - "/api/v2/external-auth/{externalauth}": { - "get": { + "/api/v2/licenses/trial": { + "post": { + "consumes": [ + "application/json" + ], "produces": [ "application/json" ], "tags": [ - "Git" + "Enterprise" ], - "summary": "Get external auth by ID", - "operationId": "get-external-auth-by-id", + "summary": "Request a trial license", + "operationId": "request-a-trial-license", "parameters": [ { - "type": "string", - "format": "string", - "description": "Git Provider ID", - "name": "externalauth", - "in": "path", - "required": true + "description": "Trial license request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.CreateTrialLicenseRequest" + } } ], "responses": { - "200": { - "description": "OK", + "201": { + "description": "Created", "schema": { - "$ref": "#/definitions/codersdk.ExternalAuth" + "$ref": "#/definitions/codersdk.License" } } }, @@ -4122,32 +4888,31 @@ const docTemplate = `{ "CoderSessionToken": [] } ] - }, + } + }, + "/api/v2/licenses/{id}": { "delete": { "produces": [ "application/json" ], "tags": [ - "Git" + "Enterprise" ], - "summary": "Delete external auth user link by ID", - "operationId": "delete-external-auth-user-link-by-id", + "summary": "Delete license", + "operationId": "delete-license", "parameters": [ { "type": "string", - "format": "string", - "description": "Git Provider ID", - "name": "externalauth", + "format": "number", + "description": "License ID", + "name": "id", "in": "path", "required": true } ], "responses": { "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.DeleteExternalAuthByIDResponse" - } + "description": "OK" } }, "security": [ @@ -4157,32 +4922,50 @@ const docTemplate = `{ ] } }, - "/api/v2/external-auth/{externalauth}/device": { + "/api/v2/mcp/servers/{mcpServer}/oauth2/callback": { "get": { - "produces": [ - "application/json" - ], "tags": [ - "Git" + "MCP" ], - "summary": "Get external auth device by ID.", - "operationId": "get-external-auth-device-by-id", + "summary": "Handle MCP server OAuth2 callback", + "operationId": "handle-mcp-server-oauth2-callback", "parameters": [ { "type": "string", - "format": "string", - "description": "Git Provider ID", - "name": "externalauth", + "format": "uuid", + "description": "MCP server config ID", + "name": "mcpServer", "in": "path", "required": true + }, + { + "type": "string", + "description": "Authorization code issued by the provider. Required together with state on success.", + "name": "code", + "in": "query" + }, + { + "type": "string", + "description": "Opaque state issued by the connect endpoint. Required together with code on success.", + "name": "state", + "in": "query" + }, + { + "type": "string", + "description": "Provider error code. Present instead of code when authorization fails.", + "name": "error", + "in": "query" + }, + { + "type": "string", + "description": "Provider error description accompanying error.", + "name": "error_description", + "in": "query" } ], "responses": { "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.ExternalAuthDevice" - } + "description": "OK" } }, "security": [ @@ -4190,26 +4973,34 @@ const docTemplate = `{ "CoderSessionToken": [] } ] - }, - "post": { + } + }, + "/api/v2/mcp/servers/{mcpServer}/oauth2/disconnect": { + "delete": { + "produces": [ + "application/json" + ], "tags": [ - "Git" + "MCP" ], - "summary": "Post external auth device by ID", - "operationId": "post-external-auth-device-by-id", + "summary": "Disconnect MCP server OAuth2 token", + "operationId": "disconnect-mcp-server-oauth2-token", "parameters": [ { "type": "string", - "format": "string", - "description": "External Provider ID", - "name": "externalauth", + "format": "uuid", + "description": "MCP server config ID", + "name": "mcpServer", "in": "path", "required": true } ], "responses": { - "204": { - "description": "No Content" + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.MCPServerOAuth2DisconnectResponse" + } } }, "security": [ @@ -4219,52 +5010,48 @@ const docTemplate = `{ ] } }, - "/api/v2/files": { + "/api/v2/notifications/custom": { "post": { - "description": "Swagger notice: Swagger 2.0 doesn't support file upload with a ` + "`" + `content-type` + "`" + ` different than ` + "`" + `application/x-www-form-urlencoded` + "`" + `.", "consumes": [ - "application/x-tar" + "application/json" ], "produces": [ "application/json" ], "tags": [ - "Files" + "Notifications" ], - "summary": "Upload file", - "operationId": "upload-file", + "summary": "Send a custom notification", + "operationId": "send-a-custom-notification", "parameters": [ { - "type": "string", - "default": "application/x-tar", - "description": "Content-Type must be ` + "`" + `application/x-tar` + "`" + ` or ` + "`" + `application/zip` + "`" + `", - "name": "Content-Type", - "in": "header", - "required": true - }, - { - "type": "file", - "description": "File to be uploaded. If using tar format, file must conform to ustar (pax may cause problems).", - "name": "file", - "in": "formData", - "required": true + "description": "Provide a non-empty title or message", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.CustomNotificationRequest" + } } ], "responses": { - "200": { - "description": "Returns existing file if duplicate", + "204": { + "description": "No Content" + }, + "400": { + "description": "Invalid request body", "schema": { - "$ref": "#/definitions/codersdk.UploadResponse" + "$ref": "#/definitions/codersdk.Response" } }, - "201": { - "description": "Returns newly created file", + "403": { + "description": "System users cannot send custom notifications", "schema": { - "$ref": "#/definitions/codersdk.UploadResponse" + "$ref": "#/definitions/codersdk.Response" } }, - "413": { - "description": "Request body exceeds 100 MiB, or a .zip archive exceeds it once expanded", + "500": { + "description": "Failed to send custom notification", "schema": { "$ref": "#/definitions/codersdk.Response" } @@ -4277,26 +5064,25 @@ const docTemplate = `{ ] } }, - "/api/v2/files/{fileID}": { + "/api/v2/notifications/dispatch-methods": { "get": { - "tags": [ - "Files" + "produces": [ + "application/json" ], - "summary": "Get file by ID", - "operationId": "get-file-by-id", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "File ID", - "name": "fileID", - "in": "path", - "required": true - } + "tags": [ + "Notifications" ], + "summary": "Get notification dispatch methods", + "operationId": "get-notification-dispatch-methods", "responses": { "200": { - "description": "OK" + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.NotificationMethodsResponse" + } + } } }, "security": [ @@ -4306,47 +5092,48 @@ const docTemplate = `{ ] } }, - "/api/v2/groups": { + "/api/v2/notifications/inbox": { "get": { "produces": [ "application/json" ], "tags": [ - "Enterprise" + "Notifications" ], - "summary": "Get groups", - "operationId": "get-groups", + "summary": "List inbox notifications", + "operationId": "list-inbox-notifications", "parameters": [ { "type": "string", - "description": "Organization ID or name", - "name": "organization", - "in": "query", - "required": true + "description": "Comma-separated list of target IDs to filter notifications", + "name": "targets", + "in": "query" }, { "type": "string", - "description": "User ID or name", - "name": "has_member", - "in": "query", - "required": true + "description": "Comma-separated list of template IDs to filter notifications", + "name": "templates", + "in": "query" }, { "type": "string", - "description": "Comma separated list of group IDs", - "name": "group_ids", - "in": "query", - "required": true + "description": "Filter notifications by read status. Possible values: read, unread, all", + "name": "read_status", + "in": "query" + }, + { + "type": "string", + "format": "uuid", + "description": "ID of the last notification from the current page. Notifications returned will be older than the associated one", + "name": "starting_before", + "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/codersdk.Group" - } + "$ref": "#/definitions/codersdk.ListInboxNotificationsResponse" } } }, @@ -4357,37 +5144,16 @@ const docTemplate = `{ ] } }, - "/api/v2/groups/{group}": { - "get": { - "produces": [ - "application/json" - ], + "/api/v2/notifications/inbox/mark-all-as-read": { + "put": { "tags": [ - "Enterprise" - ], - "summary": "Get group by ID", - "operationId": "get-group-by-id", - "parameters": [ - { - "type": "string", - "description": "Group id", - "name": "group", - "in": "path", - "required": true - }, - { - "type": "boolean", - "description": "Exclude members from the response", - "name": "exclude_members", - "in": "query" - } + "Notifications" ], + "summary": "Mark all unread notifications as read", + "operationId": "mark-all-unread-notifications-as-read", "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.Group" - } + "204": { + "description": "No Content" } }, "security": [ @@ -4395,30 +5161,53 @@ const docTemplate = `{ "CoderSessionToken": [] } ] - }, - "delete": { + } + }, + "/api/v2/notifications/inbox/watch": { + "get": { "produces": [ "application/json" ], "tags": [ - "Enterprise" + "Notifications" ], - "summary": "Delete group by name", - "operationId": "delete-group-by-name", + "summary": "Watch for new inbox notifications", + "operationId": "watch-for-new-inbox-notifications", "parameters": [ { "type": "string", - "description": "Group name", - "name": "group", - "in": "path", - "required": true + "description": "Comma-separated list of target IDs to filter notifications", + "name": "targets", + "in": "query" + }, + { + "type": "string", + "description": "Comma-separated list of template IDs to filter notifications", + "name": "templates", + "in": "query" + }, + { + "type": "string", + "description": "Filter notifications by read status. Possible values: read, unread, all", + "name": "read_status", + "in": "query" + }, + { + "enum": [ + "plaintext", + "markdown" + ], + "type": "string", + "description": "Define the output format for notifications title and body.", + "name": "format", + "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.Group" + "$ref": "#/definitions/codersdk.GetInboxNotificationResponse" } } }, @@ -4427,42 +5216,32 @@ const docTemplate = `{ "CoderSessionToken": [] } ] - }, - "patch": { - "consumes": [ - "application/json" - ], + } + }, + "/api/v2/notifications/inbox/{id}/read-status": { + "put": { "produces": [ "application/json" ], "tags": [ - "Enterprise" + "Notifications" ], - "summary": "Update group by name", - "operationId": "update-group-by-name", + "summary": "Update read status of a notification", + "operationId": "update-read-status-of-a-notification", "parameters": [ { "type": "string", - "description": "Group name", - "name": "group", + "description": "id of the notification", + "name": "id", "in": "path", "required": true - }, - { - "description": "Patch group request", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.PatchGroupRequest" - } } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.Group" + "$ref": "#/definitions/codersdk.Response" } } }, @@ -4473,31 +5252,21 @@ const docTemplate = `{ ] } }, - "/api/v2/groups/{group}/ai/budget": { + "/api/v2/notifications/settings": { "get": { "produces": [ "application/json" ], "tags": [ - "Enterprise" - ], - "summary": "Get group AI budget", - "operationId": "get-group-ai-budget", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Group ID", - "name": "group", - "in": "path", - "required": true - } + "Notifications" ], + "summary": "Get notifications settings", + "operationId": "get-notifications-settings", "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.GroupAIBudget" + "$ref": "#/definitions/codersdk.NotificationsSettings" } } }, @@ -4515,26 +5284,18 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Enterprise" + "Notifications" ], - "summary": "Upsert group AI budget", - "operationId": "upsert-group-ai-budget", + "summary": "Update notifications settings", + "operationId": "update-notifications-settings", "parameters": [ { - "type": "string", - "format": "uuid", - "description": "Group ID", - "name": "group", - "in": "path", - "required": true - }, - { - "description": "Upsert group AI budget request", + "description": "Notifications settings request", "name": "request", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/codersdk.UpsertGroupAIBudgetRequest" + "$ref": "#/definitions/codersdk.NotificationsSettings" } } ], @@ -4542,8 +5303,11 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.GroupAIBudget" + "$ref": "#/definitions/codersdk.NotificationsSettings" } + }, + "304": { + "description": "Not Modified" } }, "security": [ @@ -4551,26 +5315,33 @@ const docTemplate = `{ "CoderSessionToken": [] } ] - }, - "delete": { - "tags": [ - "Enterprise" + } + }, + "/api/v2/notifications/templates/custom": { + "get": { + "produces": [ + "application/json" ], - "summary": "Delete group AI budget", - "operationId": "delete-group-ai-budget", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Group ID", - "name": "group", - "in": "path", - "required": true - } + "tags": [ + "Notifications" ], + "summary": "Get custom notification templates", + "operationId": "get-custom-notification-templates", "responses": { - "204": { - "description": "No Content" + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.NotificationTemplate" + } + } + }, + "500": { + "description": "Failed to retrieve 'custom' notifications template", + "schema": { + "$ref": "#/definitions/codersdk.Response" + } } }, "security": [ @@ -4580,32 +5351,30 @@ const docTemplate = `{ ] } }, - "/api/v2/groups/{group}/ai/spend": { + "/api/v2/notifications/templates/system": { "get": { - "description": "Returns the AI spend limit and aggregate spend for the group.", "produces": [ "application/json" ], "tags": [ - "Enterprise" - ], - "summary": "Get group AI spend", - "operationId": "get-group-ai-spend", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Group ID", - "name": "group", - "in": "path", - "required": true - } + "Notifications" ], + "summary": "Get system notification templates", + "operationId": "get-system-notification-templates", "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.GroupAISpend" + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.NotificationTemplate" + } + } + }, + "500": { + "description": "Failed to retrieve 'system' notifications template", + "schema": { + "$ref": "#/definitions/codersdk.Response" } } }, @@ -4616,56 +5385,50 @@ const docTemplate = `{ ] } }, - "/api/v2/groups/{group}/members": { - "get": { + "/api/v2/notifications/templates/{notification_template}/method": { + "put": { "produces": [ "application/json" ], "tags": [ "Enterprise" ], - "summary": "Get group members by group ID", - "operationId": "get-group-members-by-group-id", + "summary": "Update notification template dispatch method", + "operationId": "update-notification-template-dispatch-method", "parameters": [ { "type": "string", - "description": "Group id", - "name": "group", + "description": "Notification template UUID", + "name": "notification_template", "in": "path", "required": true + } + ], + "responses": { + "200": { + "description": "Success" }, + "304": { + "description": "Not modified" + } + }, + "security": [ { - "type": "string", - "description": "Member search query", - "name": "q", - "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" + "CoderSessionToken": [] } + ] + } + }, + "/api/v2/notifications/test": { + "post": { + "tags": [ + "Notifications" ], + "summary": "Send a test notification", + "operationId": "send-a-test-notification", "responses": { "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.GroupMembersResponse" - } + "description": "OK" } }, "security": [ @@ -4675,39 +5438,32 @@ const docTemplate = `{ ] } }, - "/api/v2/groups/{group}/members/ai/spend": { + "/api/v2/oauth2-provider/apps": { "get": { - "description": "Returns aggregate AI spend attributed to the group per requested user.\nA maximum of 100 user IDs may be requested per call, and requests with more are rejected, so callers are expected to batch across multiple requests.\nUser IDs that are not members of the group, or that the caller has no read access to, are silently omitted.", "produces": [ "application/json" ], "tags": [ "Enterprise" ], - "summary": "Get group members AI spend", - "operationId": "get-group-members-ai-spend", + "summary": "Get OAuth2 applications.", + "operationId": "get-oauth2-applications", "parameters": [ { "type": "string", - "format": "uuid", - "description": "Group ID", - "name": "group", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Comma-separated list of user IDs (maximum 100)", - "name": "user_ids", - "in": "query", - "required": true + "description": "Filter by applications authorized for a user", + "name": "user_id", + "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.GroupMembersAISpend" + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.OAuth2ProviderApp" + } } } }, @@ -4716,65 +5472,35 @@ const docTemplate = `{ "CoderSessionToken": [] } ] - } - }, - "/api/v2/init-script/{os}/{arch}": { - "get": { - "produces": [ - "text/plain" - ], - "tags": [ - "InitScript" - ], - "summary": "Get agent init script", - "operationId": "get-agent-init-script", - "parameters": [ - { - "type": "string", - "description": "Operating system", - "name": "os", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Architecture", - "name": "arch", - "in": "path", - "required": true - } + }, + "post": { + "consumes": [ + "application/json" ], - "responses": { - "200": { - "description": "Success" - } - } - } - }, - "/api/v2/insights/daus": { - "get": { "produces": [ "application/json" ], "tags": [ - "Insights" + "Enterprise" ], - "summary": "Get deployment DAUs", - "operationId": "get-deployment-daus", + "summary": "Create OAuth2 application.", + "operationId": "create-oauth2-application", "parameters": [ { - "type": "integer", - "description": "Time-zone offset (e.g. -2)", - "name": "tz_offset", - "in": "query", - "required": true + "description": "The OAuth2 application to create.", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.PostOAuth2ProviderAppRequest" + } } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.DAUsResponse" + "$ref": "#/definitions/codersdk.OAuth2ProviderApp" } } }, @@ -4785,60 +5511,30 @@ const docTemplate = `{ ] } }, - "/api/v2/insights/templates": { + "/api/v2/oauth2-provider/apps/{app}": { "get": { "produces": [ "application/json" ], "tags": [ - "Insights" + "Enterprise" ], - "summary": "Get insights about templates", - "operationId": "get-insights-about-templates", + "summary": "Get OAuth2 application.", + "operationId": "get-oauth2-application", "parameters": [ { "type": "string", - "format": "date-time", - "description": "Start time", - "name": "start_time", - "in": "query", - "required": true - }, - { - "type": "string", - "format": "date-time", - "description": "End time", - "name": "end_time", - "in": "query", - "required": true - }, - { - "enum": [ - "week", - "day" - ], - "type": "string", - "description": "Interval", - "name": "interval", - "in": "query", + "description": "App ID", + "name": "app", + "in": "path", "required": true - }, - { - "type": "array", - "items": { - "type": "string" - }, - "collectionFormat": "csv", - "description": "Template IDs", - "name": "template_ids", - "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.TemplateInsightsResponse" + "$ref": "#/definitions/codersdk.OAuth2ProviderApp" } } }, @@ -4847,51 +5543,42 @@ const docTemplate = `{ "CoderSessionToken": [] } ] - } - }, - "/api/v2/insights/user-activity": { - "get": { + }, + "put": { + "consumes": [ + "application/json" + ], "produces": [ "application/json" ], "tags": [ - "Insights" + "Enterprise" ], - "summary": "Get insights about user activity", - "operationId": "get-insights-about-user-activity", + "summary": "Update OAuth2 application.", + "operationId": "update-oauth2-application", "parameters": [ { "type": "string", - "format": "date-time", - "description": "Start time", - "name": "start_time", - "in": "query", - "required": true - }, - { - "type": "string", - "format": "date-time", - "description": "End time", - "name": "end_time", - "in": "query", + "description": "App ID", + "name": "app", + "in": "path", "required": true }, { - "type": "array", - "items": { - "type": "string" - }, - "collectionFormat": "csv", - "description": "Template IDs", - "name": "template_ids", - "in": "query" + "description": "Update an OAuth2 application.", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.PutOAuth2ProviderAppRequest" + } } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.UserActivityInsightsResponse" + "$ref": "#/definitions/codersdk.OAuth2ProviderApp" } } }, @@ -4900,52 +5587,25 @@ const docTemplate = `{ "CoderSessionToken": [] } ] - } - }, - "/api/v2/insights/user-latency": { - "get": { - "produces": [ - "application/json" - ], + }, + "delete": { "tags": [ - "Insights" + "Enterprise" ], - "summary": "Get insights about user latency", - "operationId": "get-insights-about-user-latency", + "summary": "Delete OAuth2 application.", + "operationId": "delete-oauth2-application", "parameters": [ { "type": "string", - "format": "date-time", - "description": "Start time", - "name": "start_time", - "in": "query", - "required": true - }, - { - "type": "string", - "format": "date-time", - "description": "End time", - "name": "end_time", - "in": "query", + "description": "App ID", + "name": "app", + "in": "path", "required": true - }, - { - "type": "array", - "items": { - "type": "string" - }, - "collectionFormat": "csv", - "description": "Template IDs", - "name": "template_ids", - "in": "query" } ], "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.UserLatencyInsightsResponse" - } + "204": { + "description": "No Content" } }, "security": [ @@ -4955,35 +5615,33 @@ const docTemplate = `{ ] } }, - "/api/v2/insights/user-status-counts": { + "/api/v2/oauth2-provider/apps/{app}/secrets": { "get": { "produces": [ "application/json" ], "tags": [ - "Insights" + "Enterprise" ], - "summary": "Get insights about user status counts", - "operationId": "get-insights-about-user-status-counts", + "summary": "Get OAuth2 application secrets.", + "operationId": "get-oauth2-application-secrets", "parameters": [ { "type": "string", - "description": "IANA timezone name (e.g. America/St_Johns)", - "name": "timezone", - "in": "query" - }, - { - "type": "integer", - "description": "Deprecated: Time-zone offset (e.g. -2). Use timezone instead.", - "name": "tz_offset", - "in": "query" + "description": "App ID", + "name": "app", + "in": "path", + "required": true } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.GetUserStatusCountsResponse" + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.OAuth2ProviderAppSecret" + } } } }, @@ -4992,25 +5650,32 @@ const docTemplate = `{ "CoderSessionToken": [] } ] - } - }, - "/api/v2/licenses": { - "get": { + }, + "post": { "produces": [ "application/json" ], "tags": [ "Enterprise" ], - "summary": "Get licenses", - "operationId": "get-licenses", + "summary": "Create OAuth2 application secret.", + "operationId": "create-oauth2-application-secret", + "parameters": [ + { + "type": "string", + "description": "App ID", + "name": "app", + "in": "path", + "required": true + } + ], "responses": { "200": { "description": "OK", "schema": { "type": "array", "items": { - "$ref": "#/definitions/codersdk.License" + "$ref": "#/definitions/codersdk.OAuth2ProviderAppSecretFull" } } } @@ -5020,36 +5685,34 @@ const docTemplate = `{ "CoderSessionToken": [] } ] - }, - "post": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], + } + }, + "/api/v2/oauth2-provider/apps/{app}/secrets/{secretID}": { + "delete": { "tags": [ "Enterprise" ], - "summary": "Add new license", - "operationId": "add-new-license", + "summary": "Delete OAuth2 application secret.", + "operationId": "delete-oauth2-application-secret", "parameters": [ { - "description": "Add license request", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.AddLicenseRequest" - } + "type": "string", + "description": "App ID", + "name": "app", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Secret ID", + "name": "secretID", + "in": "path", + "required": true } ], "responses": { - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/codersdk.License" - } + "204": { + "description": "No Content" } }, "security": [ @@ -5059,21 +5722,21 @@ const docTemplate = `{ ] } }, - "/api/v2/licenses/refresh-entitlements": { - "post": { + "/api/v2/oauth2-provider/settings": { + "get": { "produces": [ "application/json" ], "tags": [ "Enterprise" ], - "summary": "Update license entitlements", - "operationId": "update-license-entitlements", - "responses": { - "201": { - "description": "Created", + "summary": "Get OAuth2 provider settings.", + "operationId": "get-oauth2-provider-settings", + "responses": { + "200": { + "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.Response" + "$ref": "#/definitions/codersdk.OAuth2ProviderSettings" } } }, @@ -5082,10 +5745,8 @@ const docTemplate = `{ "CoderSessionToken": [] } ] - } - }, - "/api/v2/licenses/trial": { - "post": { + }, + "put": { "consumes": [ "application/json" ], @@ -5095,24 +5756,24 @@ const docTemplate = `{ "tags": [ "Enterprise" ], - "summary": "Request a trial license", - "operationId": "request-a-trial-license", + "summary": "Update OAuth2 provider settings.", + "operationId": "update-oauth2-provider-settings", "parameters": [ { - "description": "Trial license request", + "description": "OAuth2 provider settings request", "name": "request", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/codersdk.CreateTrialLicenseRequest" + "$ref": "#/definitions/codersdk.OAuth2ProviderSettings" } } ], "responses": { - "201": { - "description": "Created", + "200": { + "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.License" + "$ref": "#/definitions/codersdk.OAuth2ProviderSettings" } } }, @@ -5123,29 +5784,25 @@ const docTemplate = `{ ] } }, - "/api/v2/licenses/{id}": { - "delete": { + "/api/v2/organizations": { + "get": { "produces": [ "application/json" ], "tags": [ - "Enterprise" - ], - "summary": "Delete license", - "operationId": "delete-license", - "parameters": [ - { - "type": "string", - "format": "number", - "description": "License ID", - "name": "id", - "in": "path", - "required": true - } + "Organizations" ], + "summary": "Get organizations", + "operationId": "get-organizations", "responses": { "200": { - "description": "OK" + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.Organization" + } + } } }, "security": [ @@ -5153,9 +5810,7 @@ const docTemplate = `{ "CoderSessionToken": [] } ] - } - }, - "/api/v2/notifications/custom": { + }, "post": { "consumes": [ "application/json" @@ -5164,41 +5819,26 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Notifications" + "Organizations" ], - "summary": "Send a custom notification", - "operationId": "send-a-custom-notification", + "summary": "Create organization", + "operationId": "create-organization", "parameters": [ { - "description": "Provide a non-empty title or message", + "description": "Create organization request", "name": "request", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/codersdk.CustomNotificationRequest" + "$ref": "#/definitions/codersdk.CreateOrganizationRequest" } } ], "responses": { - "204": { - "description": "No Content" - }, - "400": { - "description": "Invalid request body", - "schema": { - "$ref": "#/definitions/codersdk.Response" - } - }, - "403": { - "description": "System users cannot send custom notifications", - "schema": { - "$ref": "#/definitions/codersdk.Response" - } - }, - "500": { - "description": "Failed to send custom notification", + "201": { + "description": "Created", "schema": { - "$ref": "#/definitions/codersdk.Response" + "$ref": "#/definitions/codersdk.Organization" } } }, @@ -5209,24 +5849,31 @@ const docTemplate = `{ ] } }, - "/api/v2/notifications/dispatch-methods": { + "/api/v2/organizations/{organization}": { "get": { "produces": [ "application/json" ], "tags": [ - "Notifications" + "Organizations" + ], + "summary": "Get organization by ID", + "operationId": "get-organization-by-id", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Organization ID", + "name": "organization", + "in": "path", + "required": true + } ], - "summary": "Get notification dispatch methods", - "operationId": "get-notification-dispatch-methods", "responses": { "200": { "description": "OK", "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/codersdk.NotificationMethodsResponse" - } + "$ref": "#/definitions/codersdk.Organization" } } }, @@ -5235,50 +5882,30 @@ const docTemplate = `{ "CoderSessionToken": [] } ] - } - }, - "/api/v2/notifications/inbox": { - "get": { + }, + "delete": { "produces": [ "application/json" ], "tags": [ - "Notifications" + "Organizations" ], - "summary": "List inbox notifications", - "operationId": "list-inbox-notifications", + "summary": "Delete organization", + "operationId": "delete-organization", "parameters": [ { "type": "string", - "description": "Comma-separated list of target IDs to filter notifications", - "name": "targets", - "in": "query" - }, - { - "type": "string", - "description": "Comma-separated list of template IDs to filter notifications", - "name": "templates", - "in": "query" - }, - { - "type": "string", - "description": "Filter notifications by read status. Possible values: read, unread, all", - "name": "read_status", - "in": "query" - }, - { - "type": "string", - "format": "uuid", - "description": "ID of the last notification from the current page. Notifications returned will be older than the associated one", - "name": "starting_before", - "in": "query" + "description": "Organization ID or name", + "name": "organization", + "in": "path", + "required": true } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.ListInboxNotificationsResponse" + "$ref": "#/definitions/codersdk.Response" } } }, @@ -5287,72 +5914,42 @@ const docTemplate = `{ "CoderSessionToken": [] } ] - } - }, - "/api/v2/notifications/inbox/mark-all-as-read": { - "put": { - "tags": [ - "Notifications" + }, + "patch": { + "consumes": [ + "application/json" ], - "summary": "Mark all unread notifications as read", - "operationId": "mark-all-unread-notifications-as-read", - "responses": { - "204": { - "description": "No Content" - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ] - } - }, - "/api/v2/notifications/inbox/watch": { - "get": { "produces": [ "application/json" ], "tags": [ - "Notifications" + "Organizations" ], - "summary": "Watch for new inbox notifications", - "operationId": "watch-for-new-inbox-notifications", + "summary": "Update organization", + "operationId": "update-organization", "parameters": [ { "type": "string", - "description": "Comma-separated list of target IDs to filter notifications", - "name": "targets", - "in": "query" - }, - { - "type": "string", - "description": "Comma-separated list of template IDs to filter notifications", - "name": "templates", - "in": "query" - }, - { - "type": "string", - "description": "Filter notifications by read status. Possible values: read, unread, all", - "name": "read_status", - "in": "query" + "description": "Organization ID or name", + "name": "organization", + "in": "path", + "required": true }, { - "enum": [ - "plaintext", - "markdown" - ], - "type": "string", - "description": "Define the output format for notifications title and body.", - "name": "format", - "in": "query" + "description": "Patch organization request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateOrganizationRequest" + } } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.GetInboxNotificationResponse" + "$ref": "#/definitions/codersdk.Organization" } } }, @@ -5363,31 +5960,44 @@ const docTemplate = `{ ] } }, - "/api/v2/notifications/inbox/{id}/read-status": { - "put": { + "/api/v2/organizations/{organization}/ai/spend/export": { + "get": { + "description": "Returns per-user, per-group, per-model, per-provider aggregated AI spend for the organization as CSV, built from raw AI Gateway token usage.\nThe optional period_start and period_end query parameters bound the period and are interpreted as UTC. They must be provided together and span at most 31 days. When both are omitted, the current UTC monthly period is used.\nAn explicit period_start must fall within the configured AI Gateway data retention window, since older token usage is purged. The default period is narrowed to that window instead, and every row echoes the applied bounds.\nRequires organization-level administrator permissions.", "produces": [ - "application/json" + "text/csv" ], "tags": [ - "Notifications" + "Enterprise" ], - "summary": "Update read status of a notification", - "operationId": "update-read-status-of-a-notification", + "summary": "Export organization AI spend as CSV", + "operationId": "export-organization-ai-spend-as-csv", "parameters": [ { "type": "string", - "description": "id of the notification", - "name": "id", + "format": "uuid", + "description": "Organization ID", + "name": "organization", "in": "path", "required": true + }, + { + "type": "string", + "format": "date-time", + "description": "Inclusive lower bound (RFC3339)", + "name": "period_start", + "in": "query" + }, + { + "type": "string", + "format": "date-time", + "description": "Exclusive upper bound (RFC3339)", + "name": "period_end", + "in": "query" } ], "responses": { "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.Response" - } + "description": "OK" } }, "security": [ @@ -5397,21 +6007,30 @@ const docTemplate = `{ ] } }, - "/api/v2/notifications/settings": { + "/api/v2/organizations/{organization}/chats/model-overrides": { "get": { "produces": [ "application/json" ], "tags": [ - "Notifications" + "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 + } ], - "summary": "Get notifications settings", - "operationId": "get-notifications-settings", "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.NotificationsSettings" + "$ref": "#/definitions/codersdk.ChatModelOverridesResponse" } } }, @@ -5420,7 +6039,9 @@ const docTemplate = `{ "CoderSessionToken": [] } ] - }, + } + }, + "/api/v2/organizations/{organization}/chats/model-overrides/{context}": { "put": { "consumes": [ "application/json" @@ -5429,18 +6050,39 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Notifications" + "Chats" ], - "summary": "Update notifications settings", - "operationId": "update-notifications-settings", + "summary": "Update organization chat model override", + "operationId": "update-organization-chat-model-override", "parameters": [ { - "description": "Notifications settings request", + "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.NotificationsSettings" + "$ref": "#/definitions/codersdk.UpdateChatModelOverrideRequest" } } ], @@ -5448,11 +6090,8 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.NotificationsSettings" + "$ref": "#/definitions/codersdk.ChatModelOverrideResponse" } - }, - "304": { - "description": "Not Modified" } }, "security": [ @@ -5462,64 +6101,30 @@ const docTemplate = `{ ] } }, - "/api/v2/notifications/templates/custom": { + "/api/v2/organizations/{organization}/chats/models": { "get": { "produces": [ "application/json" ], "tags": [ - "Notifications" + "Chats" ], - "summary": "Get custom notification templates", - "operationId": "get-custom-notification-templates", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/codersdk.NotificationTemplate" - } - } - }, - "500": { - "description": "Failed to retrieve 'custom' notifications template", - "schema": { - "$ref": "#/definitions/codersdk.Response" - } - } - }, - "security": [ + "summary": "List AI models and provider descriptors in an organization", + "operationId": "list-ai-models-and-provider-descriptors-in-an-organization", + "parameters": [ { - "CoderSessionToken": [] + "type": "string", + "description": "Organization name or ID", + "name": "organization", + "in": "path", + "required": true } - ] - } - }, - "/api/v2/notifications/templates/system": { - "get": { - "produces": [ - "application/json" - ], - "tags": [ - "Notifications" ], - "summary": "Get system notification templates", - "operationId": "get-system-notification-templates", "responses": { "200": { "description": "OK", "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/codersdk.NotificationTemplate" - } - } - }, - "500": { - "description": "Failed to retrieve 'system' notifications template", - "schema": { - "$ref": "#/definitions/codersdk.Response" + "$ref": "#/definitions/codersdk.OrganizationChatModelsResponse" } } }, @@ -5528,52 +6133,43 @@ const docTemplate = `{ "CoderSessionToken": [] } ] - } - }, - "/api/v2/notifications/templates/{notification_template}/method": { - "put": { + }, + "post": { + "consumes": [ + "application/json" + ], "produces": [ "application/json" ], "tags": [ - "Enterprise" + "Chats" ], - "summary": "Update notification template dispatch method", - "operationId": "update-notification-template-dispatch-method", + "summary": "Create an AI model in an organization", + "operationId": "create-an-ai-model-in-an-organization", "parameters": [ { "type": "string", - "description": "Notification template UUID", - "name": "notification_template", + "description": "Organization name or ID", + "name": "organization", "in": "path", "required": true - } - ], - "responses": { - "200": { - "description": "Success" }, - "304": { - "description": "Not modified" - } - }, - "security": [ { - "CoderSessionToken": [] + "description": "Model", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.CreateChatModelRequest" + } } - ] - } - }, - "/api/v2/notifications/test": { - "post": { - "tags": [ - "Notifications" ], - "summary": "Send a test notification", - "operationId": "send-a-test-notification", "responses": { - "200": { - "description": "OK" + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/codersdk.ChatModel" + } } }, "security": [ @@ -5583,32 +6179,37 @@ const docTemplate = `{ ] } }, - "/api/v2/oauth2-provider/apps": { + "/api/v2/organizations/{organization}/chats/models/{model}": { "get": { "produces": [ "application/json" ], "tags": [ - "Enterprise" + "Chats" ], - "summary": "Get OAuth2 applications.", - "operationId": "get-oauth2-applications", + "summary": "Get an AI model", + "operationId": "get-an-ai-model", "parameters": [ { "type": "string", - "description": "Filter by applications authorized for a user", - "name": "user_id", - "in": "query" + "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": { - "type": "array", - "items": { - "$ref": "#/definitions/codersdk.OAuth2ProviderApp" - } + "$ref": "#/definitions/codersdk.ChatModel" } } }, @@ -5618,35 +6219,31 @@ const docTemplate = `{ } ] }, - "post": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], + "delete": { "tags": [ - "Enterprise" + "Chats" ], - "summary": "Create OAuth2 application.", - "operationId": "create-oauth2-application", + "summary": "Delete an AI model", + "operationId": "delete-an-ai-model", "parameters": [ { - "description": "The OAuth2 application to create.", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.PostOAuth2ProviderAppRequest" - } + "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.OAuth2ProviderApp" - } + "204": { + "description": "No Content" } }, "security": [ @@ -5654,32 +6251,49 @@ const docTemplate = `{ "CoderSessionToken": [] } ] - } - }, - "/api/v2/oauth2-provider/apps/{app}": { - "get": { + }, + "patch": { + "consumes": [ + "application/json" + ], "produces": [ "application/json" ], "tags": [ - "Enterprise" + "Chats" ], - "summary": "Get OAuth2 application.", - "operationId": "get-oauth2-application", + "summary": "Update an AI model", + "operationId": "update-an-ai-model", "parameters": [ { "type": "string", - "description": "App ID", - "name": "app", + "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.OAuth2ProviderApp" + "$ref": "#/definitions/codersdk.ChatModel" } } }, @@ -5688,42 +6302,40 @@ const docTemplate = `{ "CoderSessionToken": [] } ] - }, - "put": { - "consumes": [ - "application/json" - ], + } + }, + "/api/v2/organizations/{organization}/chats/models/{model}/acl": { + "get": { "produces": [ "application/json" ], "tags": [ - "Enterprise" + "Chats" ], - "summary": "Update OAuth2 application.", - "operationId": "update-oauth2-application", + "summary": "Get an AI model ACL", + "operationId": "get-an-ai-model-acl", "parameters": [ { "type": "string", - "description": "App ID", - "name": "app", + "description": "Organization name or ID", + "name": "organization", "in": "path", "required": true }, { - "description": "Update an OAuth2 application.", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.PutOAuth2ProviderAppRequest" - } + "type": "string", + "format": "uuid", + "description": "Model ID", + "name": "model", + "in": "path", + "required": true } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.OAuth2ProviderApp" + "$ref": "#/definitions/codersdk.ChatModelACL" } } }, @@ -5733,19 +6345,39 @@ const docTemplate = `{ } ] }, - "delete": { + "patch": { + "consumes": [ + "application/json" + ], "tags": [ - "Enterprise" + "Chats" ], - "summary": "Delete OAuth2 application.", - "operationId": "delete-oauth2-application", + "summary": "Update an AI model ACL", + "operationId": "update-an-ai-model-acl", "parameters": [ { "type": "string", - "description": "App ID", - "name": "app", + "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": { @@ -5760,7 +6392,7 @@ const docTemplate = `{ ] } }, - "/api/v2/oauth2-provider/apps/{app}/secrets": { + "/api/v2/organizations/{organization}/groups": { "get": { "produces": [ "application/json" @@ -5768,13 +6400,14 @@ const docTemplate = `{ "tags": [ "Enterprise" ], - "summary": "Get OAuth2 application secrets.", - "operationId": "get-oauth2-application-secrets", + "summary": "Get groups by organization", + "operationId": "get-groups-by-organization", "parameters": [ { "type": "string", - "description": "App ID", - "name": "app", + "format": "uuid", + "description": "Organization ID", + "name": "organization", "in": "path", "required": true } @@ -5785,7 +6418,7 @@ const docTemplate = `{ "schema": { "type": "array", "items": { - "$ref": "#/definitions/codersdk.OAuth2ProviderAppSecret" + "$ref": "#/definitions/codersdk.Group" } } } @@ -5797,31 +6430,40 @@ const docTemplate = `{ ] }, "post": { + "consumes": [ + "application/json" + ], "produces": [ "application/json" ], "tags": [ "Enterprise" ], - "summary": "Create OAuth2 application secret.", - "operationId": "create-oauth2-application-secret", + "summary": "Create group for organization", + "operationId": "create-group-for-organization", "parameters": [ + { + "description": "Create group request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.CreateGroupRequest" + } + }, { "type": "string", - "description": "App ID", - "name": "app", + "description": "Organization ID", + "name": "organization", "in": "path", "required": true } ], "responses": { - "200": { - "description": "OK", + "201": { + "description": "Created", "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/codersdk.OAuth2ProviderAppSecretFull" - } + "$ref": "#/definitions/codersdk.Group" } } }, @@ -5832,56 +6474,39 @@ const docTemplate = `{ ] } }, - "/api/v2/oauth2-provider/apps/{app}/secrets/{secretID}": { - "delete": { + "/api/v2/organizations/{organization}/groups/ai/spend": { + "get": { + "description": "Returns AI spend limits and aggregate spend for the requested groups.\nA maximum of 100 group IDs may be requested per call, and requests with more are rejected, so callers are expected to batch across multiple requests.\nUnknown or unreadable group IDs are silently omitted.", + "produces": [ + "application/json" + ], "tags": [ "Enterprise" ], - "summary": "Delete OAuth2 application secret.", - "operationId": "delete-oauth2-application-secret", + "summary": "Get organization groups AI spend", + "operationId": "get-organization-groups-ai-spend", "parameters": [ { "type": "string", - "description": "App ID", - "name": "app", + "format": "uuid", + "description": "Organization ID", + "name": "organization", "in": "path", "required": true }, { "type": "string", - "description": "Secret ID", - "name": "secretID", - "in": "path", + "description": "Comma-separated list of group IDs (maximum 100)", + "name": "group_ids", + "in": "query", "required": true } ], - "responses": { - "204": { - "description": "No Content" - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ] - } - }, - "/api/v2/oauth2-provider/settings": { - "get": { - "produces": [ - "application/json" - ], - "tags": [ - "Enterprise" - ], - "summary": "Get OAuth2 provider settings.", - "operationId": "get-oauth2-provider-settings", "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.OAuth2ProviderSettings" + "$ref": "#/definitions/codersdk.OrganizationGroupsAISpend" } } }, @@ -5890,35 +6515,40 @@ const docTemplate = `{ "CoderSessionToken": [] } ] - }, - "put": { - "consumes": [ - "application/json" - ], + } + }, + "/api/v2/organizations/{organization}/groups/{groupName}": { + "get": { "produces": [ "application/json" ], "tags": [ "Enterprise" ], - "summary": "Update OAuth2 provider settings.", - "operationId": "update-oauth2-provider-settings", + "summary": "Get group by organization and group name", + "operationId": "get-group-by-organization-and-group-name", "parameters": [ { - "description": "OAuth2 provider settings request", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.OAuth2ProviderSettings" - } + "type": "string", + "format": "uuid", + "description": "Organization ID", + "name": "organization", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Group name", + "name": "groupName", + "in": "path", + "required": true } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.OAuth2ProviderSettings" + "$ref": "#/definitions/codersdk.Group" } } }, @@ -5929,61 +6559,63 @@ const docTemplate = `{ ] } }, - "/api/v2/organizations": { + "/api/v2/organizations/{organization}/groups/{groupName}/members": { "get": { "produces": [ "application/json" ], "tags": [ - "Organizations" - ], - "summary": "Get organizations", - "operationId": "get-organizations", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/codersdk.Organization" - } - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ] - }, - "post": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Organizations" + "Enterprise" ], - "summary": "Create organization", - "operationId": "create-organization", + "summary": "Get group members by organization and group name", + "operationId": "get-group-members-by-organization-and-group-name", "parameters": [ { - "description": "Create organization request", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.CreateOrganizationRequest" - } + "type": "string", + "format": "uuid", + "description": "Organization ID", + "name": "organization", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Group name", + "name": "groupName", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Member search query", + "name": "q", + "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": { - "201": { - "description": "Created", + "200": { + "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.Organization" + "$ref": "#/definitions/codersdk.GroupMembersResponse" } } }, @@ -5994,16 +6626,17 @@ const docTemplate = `{ ] } }, - "/api/v2/organizations/{organization}": { + "/api/v2/organizations/{organization}/groups/{groupName}/members/ai/spend": { "get": { + "description": "Returns aggregate AI spend attributed to the group per requested user.\nA maximum of 100 user IDs may be requested per call, and requests with more are rejected, so callers are expected to batch across multiple requests.\nUser IDs that are not members of the group, or that the caller has no read access to, are silently omitted.", "produces": [ "application/json" ], "tags": [ - "Organizations" + "Enterprise" ], - "summary": "Get organization by ID", - "operationId": "get-organization-by-id", + "summary": "Get group members AI spend by organization", + "operationId": "get-group-members-ai-spend-by-organization", "parameters": [ { "type": "string", @@ -6012,13 +6645,27 @@ const docTemplate = `{ "name": "organization", "in": "path", "required": true + }, + { + "type": "string", + "description": "Group name", + "name": "groupName", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Comma-separated list of user IDs (maximum 100)", + "name": "user_ids", + "in": "query", + "required": true } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.Organization" + "$ref": "#/definitions/codersdk.GroupMembersAISpend" } } }, @@ -6027,20 +6674,23 @@ const docTemplate = `{ "CoderSessionToken": [] } ] - }, - "delete": { + } + }, + "/api/v2/organizations/{organization}/mcp-servers": { + "get": { "produces": [ "application/json" ], "tags": [ - "Organizations" + "MCP" ], - "summary": "Delete organization", - "operationId": "delete-organization", + "summary": "List MCP server configs", + "operationId": "list-mcp-server-configs", "parameters": [ { "type": "string", - "description": "Organization ID or name", + "format": "uuid", + "description": "Organization ID", "name": "organization", "in": "path", "required": true @@ -6050,7 +6700,10 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.Response" + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.MCPServerConfig" + } } } }, @@ -6060,7 +6713,7 @@ const docTemplate = `{ } ] }, - "patch": { + "post": { "consumes": [ "application/json" ], @@ -6068,33 +6721,34 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Organizations" + "MCP" ], - "summary": "Update organization", - "operationId": "update-organization", + "summary": "Create MCP server config", + "operationId": "create-mcp-server-config", "parameters": [ { "type": "string", - "description": "Organization ID or name", + "format": "uuid", + "description": "Organization ID", "name": "organization", "in": "path", "required": true }, { - "description": "Patch organization request", + "description": "Create MCP server config request", "name": "request", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/codersdk.UpdateOrganizationRequest" + "$ref": "#/definitions/codersdk.CreateMCPServerConfigRequest" } } ], "responses": { - "200": { - "description": "OK", + "201": { + "description": "Created", "schema": { - "$ref": "#/definitions/codersdk.Organization" + "$ref": "#/definitions/codersdk.MCPServerConfig" } } }, @@ -6105,17 +6759,16 @@ const docTemplate = `{ ] } }, - "/api/v2/organizations/{organization}/ai/spend/export": { + "/api/v2/organizations/{organization}/mcp-servers/{mcpserverconfig}": { "get": { - "description": "Returns per-user, per-group, per-model, per-provider aggregated AI spend for the organization as CSV, built from raw AI Gateway token usage.\nThe optional period_start and period_end query parameters bound the period and are interpreted as UTC. They must be provided together and span at most 31 days. When both are omitted, the current UTC monthly period is used.\nAn explicit period_start must fall within the configured AI Gateway data retention window, since older token usage is purged. The default period is narrowed to that window instead, and every row echoes the applied bounds.\nRequires organization-level administrator permissions.", "produces": [ - "text/csv" + "application/json" ], "tags": [ - "Enterprise" + "MCP" ], - "summary": "Export organization AI spend as CSV", - "operationId": "export-organization-ai-spend-as-csv", + "summary": "Get MCP server config", + "operationId": "get-mcp-server-config", "parameters": [ { "type": "string", @@ -6125,49 +6778,11 @@ const docTemplate = `{ "in": "path", "required": true }, - { - "type": "string", - "format": "date-time", - "description": "Inclusive lower bound (RFC3339)", - "name": "period_start", - "in": "query" - }, - { - "type": "string", - "format": "date-time", - "description": "Exclusive upper bound (RFC3339)", - "name": "period_end", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK" - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ] - } - }, - "/api/v2/organizations/{organization}/groups": { - "get": { - "produces": [ - "application/json" - ], - "tags": [ - "Enterprise" - ], - "summary": "Get groups by organization", - "operationId": "get-groups-by-organization", - "parameters": [ { "type": "string", "format": "uuid", - "description": "Organization ID", - "name": "organization", + "description": "MCP server config ID", + "name": "mcpserverconfig", "in": "path", "required": true } @@ -6176,10 +6791,7 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/codersdk.Group" - } + "$ref": "#/definitions/codersdk.MCPServerConfig" } } }, @@ -6189,42 +6801,33 @@ const docTemplate = `{ } ] }, - "post": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], + "delete": { "tags": [ - "Enterprise" + "MCP" ], - "summary": "Create group for organization", - "operationId": "create-group-for-organization", + "summary": "Delete MCP server config", + "operationId": "delete-mcp-server-config", "parameters": [ - { - "description": "Create group request", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.CreateGroupRequest" - } - }, { "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": { - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/codersdk.Group" - } + "204": { + "description": "No Content" } }, "security": [ @@ -6232,19 +6835,19 @@ const docTemplate = `{ "CoderSessionToken": [] } ] - } - }, - "/api/v2/organizations/{organization}/groups/ai/spend": { - "get": { - "description": "Returns AI spend limits and aggregate spend for the requested groups.\nA maximum of 100 group IDs may be requested per call, and requests with more are rejected, so callers are expected to batch across multiple requests.\nUnknown or unreadable group IDs are silently omitted.", + }, + "patch": { + "consumes": [ + "application/json" + ], "produces": [ "application/json" ], "tags": [ - "Enterprise" + "MCP" ], - "summary": "Get organization groups AI spend", - "operationId": "get-organization-groups-ai-spend", + "summary": "Update MCP server config", + "operationId": "update-mcp-server-config", "parameters": [ { "type": "string", @@ -6256,17 +6859,27 @@ const docTemplate = `{ }, { "type": "string", - "description": "Comma-separated list of group IDs (maximum 100)", - "name": "group_ids", - "in": "query", + "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.OrganizationGroupsAISpend" + "$ref": "#/definitions/codersdk.MCPServerConfig" } } }, @@ -6277,16 +6890,16 @@ const docTemplate = `{ ] } }, - "/api/v2/organizations/{organization}/groups/{groupName}": { + "/api/v2/organizations/{organization}/mcp-servers/{mcpserverconfig}/acl": { "get": { "produces": [ "application/json" ], "tags": [ - "Enterprise" + "MCP" ], - "summary": "Get group by organization and group name", - "operationId": "get-group-by-organization-and-group-name", + "summary": "Get MCP server config ACL", + "operationId": "get-mcp-server-config-acl", "parameters": [ { "type": "string", @@ -6298,8 +6911,9 @@ const docTemplate = `{ }, { "type": "string", - "description": "Group name", - "name": "groupName", + "format": "uuid", + "description": "MCP server config ID", + "name": "mcpserverconfig", "in": "path", "required": true } @@ -6308,7 +6922,7 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.Group" + "$ref": "#/definitions/codersdk.MCPServerConfigACL" } } }, @@ -6317,18 +6931,16 @@ const docTemplate = `{ "CoderSessionToken": [] } ] - } - }, - "/api/v2/organizations/{organization}/groups/{groupName}/members": { - "get": { - "produces": [ + }, + "patch": { + "consumes": [ "application/json" ], "tags": [ - "Enterprise" + "MCP" ], - "summary": "Get group members by organization and group name", - "operationId": "get-group-members-by-organization-and-group-name", + "summary": "Update MCP server config ACL", + "operationId": "update-mcp-server-config-acl", "parameters": [ { "type": "string", @@ -6338,45 +6950,27 @@ const docTemplate = `{ "in": "path", "required": true }, - { - "type": "string", - "description": "Group name", - "name": "groupName", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Member search query", - "name": "q", - "in": "query" - }, { "type": "string", "format": "uuid", - "description": "After ID", - "name": "after_id", - "in": "query" - }, - { - "type": "integer", - "description": "Page limit", - "name": "limit", - "in": "query" + "description": "MCP server config ID", + "name": "mcpserverconfig", + "in": "path", + "required": true }, { - "type": "integer", - "description": "Page offset", - "name": "offset", - "in": "query" + "description": "Update MCP server config ACL request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateMCPServerConfigACLRequest" + } } ], "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.GroupMembersResponse" - } + "204": { + "description": "No Content" } }, "security": [ @@ -6386,17 +6980,13 @@ const docTemplate = `{ ] } }, - "/api/v2/organizations/{organization}/groups/{groupName}/members/ai/spend": { + "/api/v2/organizations/{organization}/mcp-servers/{mcpserverconfig}/oauth2/connect": { "get": { - "description": "Returns aggregate AI spend attributed to the group per requested user.\nA maximum of 100 user IDs may be requested per call, and requests with more are rejected, so callers are expected to batch across multiple requests.\nUser IDs that are not members of the group, or that the caller has no read access to, are silently omitted.", - "produces": [ - "application/json" - ], "tags": [ - "Enterprise" + "MCP" ], - "summary": "Get group members AI spend by organization", - "operationId": "get-group-members-ai-spend-by-organization", + "summary": "Initiate MCP server OAuth2 connect", + "operationId": "initiate-mcp-server-oauth2-connect", "parameters": [ { "type": "string", @@ -6408,25 +6998,16 @@ const docTemplate = `{ }, { "type": "string", - "description": "Group name", - "name": "groupName", + "format": "uuid", + "description": "MCP server config ID", + "name": "mcpserverconfig", "in": "path", "required": true - }, - { - "type": "string", - "description": "Comma-separated list of user IDs (maximum 100)", - "name": "user_ids", - "in": "query", - "required": true } ], "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.GroupMembersAISpend" - } + "307": { + "description": "Temporary Redirect" } }, "security": [ @@ -6766,6 +7347,106 @@ 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": [] + } + ] + } + }, + "/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": [] + } + ] + } + }, "/api/v2/organizations/{organization}/members/{user}/roles": { "put": { "consumes": [ @@ -10803,10 +11484,110 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Users" + "Users" + ], + "summary": "Get user by name", + "operationId": "get-user-by-name", + "parameters": [ + { + "type": "string", + "description": "User ID, username, or me", + "name": "user", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.User" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + }, + "delete": { + "tags": [ + "Users" + ], + "summary": "Delete user", + "operationId": "delete-user", + "parameters": [ + { + "type": "string", + "description": "User ID, name, or me", + "name": "user", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/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": "Get user by name", - "operationId": "get-user-by-name", + "summary": "Update user AI provider key", + "operationId": "update-user-ai-provider-key", "parameters": [ { "type": "string", @@ -10814,13 +11595,29 @@ const docTemplate = `{ "name": "user", "in": "path", "required": true + }, + { + "type": "string", + "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.User" + "$ref": "#/definitions/codersdk.UserAIProviderKeyConfig" } } }, @@ -10832,22 +11629,29 @@ const docTemplate = `{ }, "delete": { "tags": [ - "Users" + "Chats" ], - "summary": "Delete user", - "operationId": "delete-user", + "summary": "Delete user AI provider key", + "operationId": "delete-user-ai-provider-key", "parameters": [ { "type": "string", - "description": "User ID, name, or me", + "description": "User ID, username, or me", "name": "user", "in": "path", "required": true + }, + { + "type": "string", + "description": "AI provider ID", + "name": "aiProvider", + "in": "path", + "required": true } ], "responses": { - "200": { - "description": "OK" + "204": { + "description": "No Content" } }, "security": [ @@ -16138,6 +16942,12 @@ const docTemplate = `{ "ReinitializeReasonPrebuildClaimed" ] }, + "coderd.chatsByWorkspaceResponse": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, "coderd.cspViolation": { "type": "object", "properties": { @@ -16929,6 +17739,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 +19190,14 @@ const docTemplate = `{ } } }, + "codersdk.ChatAutoArchiveDaysResponse": { + "type": "object", + "properties": { + "auto_archive_days": { + "type": "integer" + } + } + }, "codersdk.ChatBusyBehavior": { "type": "string", "enum": [ @@ -18531,6 +19376,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 +20619,14 @@ const docTemplate = `{ "ChatPersonalModelOverrideModeModel" ] }, + "codersdk.ChatPersonalModelOverridesAdminSettings": { + "type": "object", + "properties": { + "allow_users": { + "type": "boolean" + } + } + }, "codersdk.ChatPlanMode": { "type": "string", "enum": [ @@ -19764,6 +20636,14 @@ const docTemplate = `{ "ChatPlanModePlan" ] }, + "codersdk.ChatPlanModeInstructionsResponse": { + "type": "object", + "properties": { + "plan_mode_instructions": { + "type": "string" + } + } + }, "codersdk.ChatPrompt": { "type": "object", "properties": { @@ -20000,6 +20880,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 +20981,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 +21994,14 @@ const docTemplate = `{ } } }, + "codersdk.CreateUserAIProviderKeyRequest": { + "type": "object", + "properties": { + "api_key": { + "type": "string" + } + } + }, "codersdk.CreateUserRequestWithOrgs": { "type": "object", "required": [ @@ -26441,6 +27352,17 @@ const docTemplate = `{ } } }, + "codersdk.SubmitToolResultsRequest": { + "type": "object", + "properties": { + "results": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.ToolResult" + } + } + } + }, "codersdk.SupportConfig": { "type": "object", "properties": { @@ -27685,6 +28607,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 +28734,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 +28816,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 +28873,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 +29240,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 +29705,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 +29842,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 4ce1e67d1ec..5720007029e 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -127,25 +127,48 @@ } } }, - "/api/experimental/chats": { + "/api/experimental/chats/{chat}/stream/desktop": { "get": { - "description": "Experimental: this endpoint is subject to change.", - "produces": ["application/json"], + "description": "Raw binary WebSocket stream of the chat workspace desktop.\nExperimental: this endpoint is subject to change.", + "produces": ["application/octet-stream"], "tags": ["Chats"], - "summary": "List chats", - "operationId": "list-chats", + "summary": "Connect to chat workspace desktop via WebSockets", + "operationId": "connect-to-chat-workspace-desktop-via-websockets", "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" - }, + "format": "uuid", + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true + } + ], + "responses": { + "101": { + "description": "Switching Protocols" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/experimental/users/{user}/skills": { + "get": { + "produces": ["application/json"], + "tags": ["Users"], + "summary": "List user skills", + "operationId": "list-user-skills", + "parameters": [ { "type": "string", - "description": "Filter by label as key:value. Repeat for multiple (AND logic).", - "name": "label", - "in": "query" + "description": "User ID, username, or me", + "name": "user", + "in": "path", + "required": true } ], "responses": { @@ -154,7 +177,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/definitions/codersdk.Chat" + "$ref": "#/definitions/codersdk.UserSkillMetadata" } } } @@ -163,23 +186,32 @@ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } }, "post": { - "description": "Experimental: this endpoint is subject to change.", "consumes": ["application/json"], "produces": ["application/json"], - "tags": ["Chats"], - "summary": "Create chat", - "operationId": "create-chat", + "tags": ["Users"], + "summary": "Create a user skill", + "operationId": "create-a-user-skill", "parameters": [ { - "description": "Create chat request", + "type": "string", + "description": "User ID, username, or me", + "name": "user", + "in": "path", + "required": true + }, + { + "description": "Create user skill request", "name": "request", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/codersdk.CreateChatRequest" + "$ref": "#/definitions/codersdk.CreateUserSkillRequest" } } ], @@ -187,13 +219,7 @@ "201": { "description": "Created", "schema": { - "$ref": "#/definitions/codersdk.Chat" - } - }, - "413": { - "description": "Request body exceeds 256 KiB", - "schema": { - "$ref": "#/definitions/codersdk.Response" + "$ref": "#/definitions/codersdk.UserSkill" } } }, @@ -201,20 +227,39 @@ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } } }, - "/api/experimental/chats/config/retention-days": { + "/api/experimental/users/{user}/skills/{skillName}": { "get": { "produces": ["application/json"], - "tags": ["Chats"], - "summary": "Get chat retention days", - "operationId": "get-chat-retention-days", + "tags": ["Users"], + "summary": "Get a user skill by name", + "operationId": "get-a-user-skill-by-name", + "parameters": [ + { + "type": "string", + "description": "User ID, username, or me", + "name": "user", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Skill name", + "name": "skillName", + "in": "path", + "required": true + } + ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.ChatRetentionDaysResponse" + "$ref": "#/definitions/codersdk.UserSkill" } } }, @@ -227,20 +272,24 @@ "skip": true } }, - "put": { - "consumes": ["application/json"], - "tags": ["Chats"], - "summary": "Update chat retention days", - "operationId": "update-chat-retention-days", + "delete": { + "tags": ["Users"], + "summary": "Delete a user skill", + "operationId": "delete-a-user-skill", "parameters": [ { - "description": "Request body", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.UpdateChatRetentionDaysRequest" - } + "type": "string", + "description": "User ID, username, or me", + "name": "user", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Skill name", + "name": "skillName", + "in": "path", + "required": true } ], "responses": { @@ -256,47 +305,43 @@ "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" - ], + }, + "patch": { + "consumes": ["application/json"], "produces": ["application/json"], - "tags": ["Chats"], - "summary": "Upload chat file", - "operationId": "upload-chat-file", + "tags": ["Users"], + "summary": "Update a user skill", + "operationId": "update-a-user-skill", "parameters": [ { "type": "string", - "format": "uuid", - "description": "Organization ID", - "name": "organization", - "in": "query", + "description": "User ID, username, or me", + "name": "user", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Skill name", + "name": "skillName", + "in": "path", "required": true + }, + { + "description": "Update user skill request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateUserSkillRequest" + } } ], "responses": { - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/codersdk.UploadChatFileResponse" - } - }, - "413": { - "description": "Request body exceeds 10 MiB", + "200": { + "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.Response" + "$ref": "#/definitions/codersdk.UserSkill" } } }, @@ -304,40 +349,72 @@ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } } }, - "/api/experimental/chats/files/{file}": { + "/api/experimental/watch-all-workspacebuilds": { "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" + "produces": ["application/json"], + "tags": ["Workspaces"], + "summary": "Watch all workspace builds", + "operationId": "watch-all-workspace-builds", + "responses": { + "101": { + "description": "Switching Protocols" + } + }, + "security": [ + { + "CoderSessionToken": [] + } ], - "tags": ["Chats"], - "summary": "Get chat file", - "operationId": "get-chat-file", + "x-apidocgen": { + "skip": true + } + } + }, + "/api/v2/": { + "get": { + "produces": ["application/json"], + "tags": ["General"], + "summary": "API root handler", + "operationId": "api-root-handler", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.Response" + } + } + } + } + }, + "/api/v2/agent-firewall/sessions/{id}": { + "get": { + "produces": ["application/json"], + "tags": ["Enterprise"], + "summary": "Get agent firewall session by ID", + "operationId": "get-agent-firewall-session-by-id", "parameters": [ { "type": "string", "format": "uuid", - "description": "File ID", - "name": "file", + "description": "Agent firewall session ID", + "name": "id", "in": "path", "required": true } ], "responses": { "200": { - "description": "OK" - } + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.AgentFirewallSession" + } + } }, "security": [ { @@ -346,72 +423,45 @@ ] } }, - "/api/experimental/chats/files/{file}/download": { + "/api/v2/agent-firewall/sessions/{id}/logs": { "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", + "produces": ["application/json"], + "tags": ["Enterprise"], + "summary": "Get agent firewall session logs", + "operationId": "get-agent-firewall-session-logs", "parameters": [ { "type": "string", "format": "uuid", - "description": "File ID", - "name": "file", + "description": "Agent firewall session ID", + "name": "id", "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": "integer", + "description": "Inclusive lower bound on sequence number", + "name": "seq_after", + "in": "query" + }, { - "type": "string", - "format": "uuid", - "description": "File ID", - "name": "file", - "in": "path", - "required": true + "type": "integer", + "description": "Exclusive upper bound on sequence number", + "name": "seq_before", + "in": "query" + }, + { + "type": "integer", + "description": "Maximum number of logs to return (default 100)", + "name": "limit", + "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.ChatFileDownloadURLResponse" + "$ref": "#/definitions/codersdk.AgentFirewallSessionLogsResponse" } } }, @@ -419,24 +469,24 @@ { "CoderSessionToken": [] } - ], - "x-apidocgen": { - "skip": true - } + ] } }, - "/api/experimental/chats/watch": { + "/api/v2/ai-gateway/clients": { "get": { - "description": "Experimental: this endpoint is subject to change.", + "description": "Alias: also available at /api/v2/aibridge/clients for backward compatibility.", "produces": ["application/json"], - "tags": ["Chats"], - "summary": "Watch chat events for a user via WebSockets", - "operationId": "watch-chat-events-for-a-user-via-websockets", + "tags": ["AI Gateway"], + "summary": "List AI Gateway clients", + "operationId": "list-ai-gateway-clients", "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.ChatWatchEvent" + "type": "array", + "items": { + "type": "string" + } } } }, @@ -447,28 +497,20 @@ ] } }, - "/api/experimental/chats/{chat}": { + "/api/v2/ai-gateway/keys": { "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 - } - ], + "tags": ["Enterprise"], + "summary": "List AI Gateway keys", + "operationId": "list-ai-gateway-keys", "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.Chat" + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.AIGatewayKey" + } } } }, @@ -478,34 +520,29 @@ } ] }, - "patch": { - "description": "Experimental: this endpoint is subject to change.", + "post": { "consumes": ["application/json"], - "tags": ["Chats"], - "summary": "Update chat", - "operationId": "update-chat", + "produces": ["application/json"], + "tags": ["Enterprise"], + "summary": "Create AI Gateway key", + "operationId": "create-ai-gateway-key", "parameters": [ { - "type": "string", - "format": "uuid", - "description": "Chat ID", - "name": "chat", - "in": "path", - "required": true - }, - { - "description": "Update chat request", + "description": "Create AI Gateway key request", "name": "request", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/codersdk.UpdateChatRequest" + "$ref": "#/definitions/codersdk.CreateAIGatewayKeyRequest" } } ], "responses": { - "204": { - "description": "No Content" + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/codersdk.CreateAIGatewayKeyResponse" + } } }, "security": [ @@ -515,63 +552,19 @@ ] } }, - "/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", + "/api/v2/ai-gateway/keys/{key}": { + "delete": { + "tags": ["Enterprise"], + "summary": "Delete AI Gateway key", + "operationId": "delete-ai-gateway-key", "parameters": [ { "type": "string", "format": "uuid", - "description": "Chat ID", - "name": "chat", + "description": "Key ID", + "name": "key", "in": "path", "required": true - }, - { - "description": "Update chat ACL request", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.UpdateChatACL" - } } ], "responses": { @@ -583,34 +576,24 @@ { "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.", + "/api/v2/ai-gateway/models": { + "get": { + "description": "Alias: also available at /api/v2/aibridge/models for backward compatibility.", "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 - } - ], + "tags": ["AI Gateway"], + "summary": "List AI Gateway models", + "operationId": "list-ai-gateway-models", "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.Chat" + "type": "array", + "items": { + "type": "string" + } } } }, @@ -618,34 +601,64 @@ { "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": [ + "/api/v2/ai-gateway/serve": { + "get": { + "tags": ["Enterprise"], + "summary": "AI Gateway serve", + "operationId": "ai-gateway-serve", + "responses": { + "101": { + "description": "Switching Protocols" + } + }, + "security": [ { - "type": "string", - "format": "uuid", - "description": "Chat ID", - "name": "chat", - "in": "path", - "required": true + "AIGatewayKey": [] } - ], - "responses": { + ] + } + }, + "/api/v2/ai-gateway/sessions": { + "get": { + "description": "Alias: also available at /api/v2/aibridge/sessions for backward compatibility.", + "produces": ["application/json"], + "tags": ["AI Gateway"], + "summary": "List AI Gateway sessions", + "operationId": "list-ai-gateway-sessions", + "parameters": [ + { + "type": "string", + "description": "Search query in the format `key:value`. Available keys are: initiator, provider, provider_name, model, client, session_id, started_after, started_before.", + "name": "q", + "in": "query" + }, + { + "type": "integer", + "description": "Page limit", + "name": "limit", + "in": "query" + }, + { + "type": "string", + "description": "Cursor pagination after session ID (cannot be used with offset)", + "name": "after_session_id", + "in": "query" + }, + { + "type": "integer", + "description": "Offset pagination (cannot be used with after_session_id)", + "name": "offset", + "in": "query" + } + ], + "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.Chat" + "$ref": "#/definitions/codersdk.AIBridgeListSessionsResponse" } } }, @@ -656,28 +669,45 @@ ] } }, - "/api/experimental/chats/{chat}/cost": { + "/api/v2/ai-gateway/sessions/{session_id}": { "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.", + "description": "Alias: also available at /api/v2/aibridge/sessions/{session_id} for backward compatibility.", "produces": ["application/json"], - "tags": ["Chats"], - "summary": "Get chat cost", - "operationId": "get-chat-cost", + "tags": ["AI Gateway"], + "summary": "Get AI Gateway session threads", + "operationId": "get-ai-gateway-session-threads", "parameters": [ { "type": "string", - "format": "uuid", - "description": "Chat ID", - "name": "chat", + "description": "Session ID (client_session_id or interception UUID)", + "name": "session_id", "in": "path", "required": true + }, + { + "type": "string", + "description": "Thread pagination cursor (forward/older)", + "name": "after_id", + "in": "query" + }, + { + "type": "string", + "description": "Thread pagination cursor (backward/newer)", + "name": "before_id", + "in": "query" + }, + { + "type": "integer", + "description": "Number of threads per page (default 50)", + "name": "limit", + "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.ChatCost" + "$ref": "#/definitions/codersdk.AIBridgeSessionThreadsResponse" } } }, @@ -688,28 +718,20 @@ ] } }, - "/api/experimental/chats/{chat}/diff": { + "/api/v2/ai/providers": { "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 - } - ], + "tags": ["AI Providers"], + "summary": "List AI providers", + "operationId": "list-ai-providers", "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.ChatDiffContents" + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.AIProvider" + } } } }, @@ -718,30 +740,29 @@ "CoderSessionToken": [] } ] - } - }, - "/api/experimental/chats/{chat}/interrupt": { + }, "post": { - "description": "Experimental: this endpoint is subject to change.", + "consumes": ["application/json"], "produces": ["application/json"], - "tags": ["Chats"], - "summary": "Interrupt chat", - "operationId": "interrupt-chat", + "tags": ["AI Providers"], + "summary": "Create an AI provider", + "operationId": "create-an-ai-provider", "parameters": [ { - "type": "string", - "format": "uuid", - "description": "Chat ID", - "name": "chat", - "in": "path", - "required": true + "description": "Create AI provider request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.CreateAIProviderRequest" + } } ], "responses": { - "200": { - "description": "OK", + "201": { + "description": "Created", "schema": { - "$ref": "#/definitions/codersdk.Chat" + "$ref": "#/definitions/codersdk.AIProvider" } } }, @@ -752,46 +773,26 @@ ] } }, - "/api/experimental/chats/{chat}/messages": { + "/api/v2/ai/providers/{idOrName}": { "get": { - "description": "Experimental: this endpoint is subject to change.", "produces": ["application/json"], - "tags": ["Chats"], - "summary": "List chat messages", - "operationId": "list-chat-messages", + "tags": ["AI Providers"], + "summary": "Get an AI provider", + "operationId": "get-an-ai-provider", "parameters": [ { "type": "string", - "format": "uuid", - "description": "Chat ID", - "name": "chat", + "description": "Provider ID or name", + "name": "idOrName", "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" + "$ref": "#/definitions/codersdk.AIProvider" } } }, @@ -801,38 +802,22 @@ } ] }, - "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", + "delete": { + "tags": ["AI Providers"], + "summary": "Delete an AI provider", + "operationId": "delete-an-ai-provider", "parameters": [ { "type": "string", - "format": "uuid", - "description": "Chat ID", - "name": "chat", + "description": "Provider ID or name", + "name": "idOrName", "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" - } + "204": { + "description": "No Content" } }, "security": [ @@ -840,39 +825,28 @@ "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", + "tags": ["AI Providers"], + "summary": "Update an AI provider", + "operationId": "update-an-ai-provider", "parameters": [ { "type": "string", - "format": "uuid", - "description": "Chat ID", - "name": "chat", - "in": "path", - "required": true - }, - { - "type": "integer", - "description": "Message ID", - "name": "message", + "description": "Provider ID or name", + "name": "idOrName", "in": "path", "required": true }, { - "description": "Edit chat message request", + "description": "Update AI provider request", "name": "request", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/codersdk.EditChatMessageRequest" + "$ref": "#/definitions/codersdk.UpdateAIProviderRequest" } } ], @@ -880,7 +854,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.EditChatMessageResponse" + "$ref": "#/definitions/codersdk.AIProvider" } } }, @@ -891,34 +865,17 @@ ] } }, - "/api/experimental/chats/{chat}/prompts": { + "/api/v2/appearance": { "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" - } - ], + "tags": ["Enterprise"], + "summary": "Get appearance", + "operationId": "get-appearance", "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.ChatPromptsResponse" + "$ref": "#/definitions/codersdk.AppearanceConfig" } } }, @@ -927,62 +884,29 @@ "CoderSessionToken": [] } ] - } - }, - "/api/experimental/chats/{chat}/reconcile-invalid": { - "post": { - "description": "Experimental: this endpoint is subject to change.", + }, + "put": { + "consumes": ["application/json"], "produces": ["application/json"], - "tags": ["Chats"], - "summary": "Reconcile invalid chat state", - "operationId": "reconcile-invalid-chat-state", + "tags": ["Enterprise"], + "summary": "Update appearance", + "operationId": "update-appearance", "parameters": [ { - "type": "string", - "format": "uuid", - "description": "Chat ID", - "name": "chat", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", + "description": "Update appearance request", + "name": "request", + "in": "body", + "required": true, "schema": { - "$ref": "#/definitions/codersdk.Chat" + "$ref": "#/definitions/codersdk.UpdateAppearanceConfig" } } - }, - "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" + "$ref": "#/definitions/codersdk.UpdateAppearanceConfig" } } }, @@ -993,26 +917,22 @@ ] } }, - "/api/experimental/chats/{chat}/stream/desktop": { + "/api/v2/applications/auth-redirect": { "get": { - "description": "Raw binary WebSocket stream of the chat workspace desktop.\nExperimental: this endpoint is subject to change.", - "produces": ["application/octet-stream"], - "tags": ["Chats"], - "summary": "Connect to chat workspace desktop via WebSockets", - "operationId": "connect-to-chat-workspace-desktop-via-websockets", + "tags": ["Applications"], + "summary": "Redirect to URI with encrypted API key", + "operationId": "redirect-to-uri-with-encrypted-api-key", "parameters": [ { "type": "string", - "format": "uuid", - "description": "Chat ID", - "name": "chat", - "in": "path", - "required": true + "description": "Redirect destination", + "name": "redirect_uri", + "in": "query" } ], "responses": { - "101": { - "description": "Switching Protocols" + "307": { + "description": "Temporary Redirect" } }, "security": [ @@ -1022,28 +942,18 @@ ] } }, - "/api/experimental/chats/{chat}/stream/git": { + "/api/v2/applications/host": { "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 - } - ], + "tags": ["Applications"], + "summary": "Get applications host", + "operationId": "get-applications-host", + "deprecated": true, "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.WorkspaceAgentGitServerMessage" + "$ref": "#/definitions/codersdk.AppHostResponse" } } }, @@ -1054,28 +964,29 @@ ] } }, - "/api/experimental/chats/{chat}/stream/parts": { - "get": { - "description": "Experimental: this endpoint is subject to change.", + "/api/v2/applications/reconnecting-pty-signed-token": { + "post": { + "consumes": ["application/json"], "produces": ["application/json"], - "tags": ["Chats"], - "summary": "Stream chat parts via WebSockets", - "operationId": "stream-chat-parts-via-websockets", + "tags": ["Enterprise"], + "summary": "Issue signed app token for reconnecting PTY", + "operationId": "issue-signed-app-token-for-reconnecting-pty", "parameters": [ { - "type": "string", - "format": "uuid", - "description": "Chat ID", - "name": "chat", - "in": "path", - "required": true + "description": "Issue reconnecting PTY signed token request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.IssueReconnectingPTYSignedTokenRequest" + } } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.ChatStreamEvent" + "$ref": "#/definitions/codersdk.IssueReconnectingPTYSignedTokenResponse" } } }, @@ -1089,28 +1000,38 @@ } } }, - "/api/experimental/chats/{chat}/title/propose": { - "post": { - "description": "Experimental: this endpoint is subject to change.", + "/api/v2/audit": { + "get": { "produces": ["application/json"], - "tags": ["Chats"], - "summary": "Propose chat title", - "operationId": "propose-chat-title", + "tags": ["Audit"], + "summary": "Get audit logs", + "operationId": "get-audit-logs", "parameters": [ { "type": "string", - "format": "uuid", - "description": "Chat ID", - "name": "chat", - "in": "path", + "description": "Search query", + "name": "q", + "in": "query" + }, + { + "type": "integer", + "description": "Page limit", + "name": "limit", + "in": "query", "required": true + }, + { + "type": "integer", + "description": "Page offset", + "name": "offset", + "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.ProposeChatTitleResponse" + "$ref": "#/definitions/codersdk.AuditLogResponse" } } }, @@ -1121,49 +1042,26 @@ ] } }, - "/api/experimental/mcp/servers/{mcpServer}/oauth2/callback": { - "get": { - "produces": ["text/html"], - "tags": ["MCP"], - "summary": "Handle MCP server OAuth2 callback", - "operationId": "handle-mcp-server-oauth2-callback", + "/api/v2/audit/testgenerate": { + "post": { + "consumes": ["application/json"], + "tags": ["Audit"], + "summary": "Generate fake audit log", + "operationId": "generate-fake-audit-log", "parameters": [ { - "type": "string", - "format": "uuid", - "description": "MCP server config ID", - "name": "mcpServer", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Authorization code issued by the provider. Required together with state on success.", - "name": "code", - "in": "query" - }, - { - "type": "string", - "description": "Opaque state issued by the connect endpoint. Required together with code on success.", - "name": "state", - "in": "query" - }, - { - "type": "string", - "description": "Provider error code. Present instead of code when authorization fails.", - "name": "error", - "in": "query" - }, - { - "type": "string", - "description": "Provider error description accompanying error.", - "name": "error_description", - "in": "query" + "description": "Audit log request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.CreateTestAuditLogRequest" + } } ], "responses": { - "200": { - "description": "OK" + "204": { + "description": "No Content" } }, "security": [ @@ -1176,60 +1074,45 @@ } } }, - "/api/experimental/mcp/servers/{mcpServer}/oauth2/disconnect": { - "delete": { + "/api/v2/auth/scopes": { + "get": { "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 - } - ], + "tags": ["Authorization"], + "summary": "List API key scopes", + "operationId": "list-api-key-scopes", "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.MCPServerOAuth2DisconnectResponse" + "$ref": "#/definitions/codersdk.ExternalAPIKeyScopes" } } - }, - "security": [ - { - "CoderSessionToken": [] - } - ], - "x-apidocgen": { - "skip": true } } }, - "/api/experimental/organizations/{organization}/chats/model-overrides": { - "get": { + "/api/v2/authcheck": { + "post": { + "consumes": ["application/json"], "produces": ["application/json"], - "tags": ["Chats"], - "summary": "List organization chat model overrides", - "operationId": "list-organization-chat-model-overrides", + "tags": ["Authorization"], + "summary": "Check authorization", + "operationId": "check-authorization", "parameters": [ { - "type": "string", - "description": "Organization name or ID", - "name": "organization", - "in": "path", - "required": true + "description": "Authorization request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.AuthorizationRequest" + } } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.ChatModelOverridesResponse" + "$ref": "#/definitions/codersdk.AuthorizationResponse" } } }, @@ -1237,89 +1120,53 @@ { "CoderSessionToken": [] } - ], - "x-apidocgen": { - "skip": true - } + ] } }, - "/api/experimental/organizations/{organization}/chats/model-overrides/{context}": { - "put": { - "consumes": ["application/json"], + "/api/v2/buildinfo": { + "get": { "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" - } - } - ], + "tags": ["General"], + "summary": "Build info", + "operationId": "build-info", "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.ChatModelOverrideResponse" + "$ref": "#/definitions/codersdk.BuildInfoResponse" } } - }, - "security": [ - { - "CoderSessionToken": [] - } - ], - "x-apidocgen": { - "skip": true } } }, - "/api/experimental/organizations/{organization}/chats/models": { + "/api/v2/chats": { "get": { "produces": ["application/json"], "tags": ["Chats"], - "summary": "List AI models and provider descriptors in an organization", - "operationId": "list-ai-models-by-organization", + "summary": "List chats", + "operationId": "list-chats", "parameters": [ { "type": "string", - "description": "Organization name or ID", - "name": "organization", - "in": "path", - "required": true + "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; quote multi-word values; cannot be combined with title, pr_title, or pr; a value that tokenizes to no searchable words 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": { - "$ref": "#/definitions/codersdk.OrganizationChatModelsResponse" + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.Chat" + } } } }, @@ -1327,32 +1174,22 @@ { "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", + "summary": "Create chat", + "operationId": "create-chat", "parameters": [ { - "type": "string", - "description": "Organization name or ID", - "name": "organization", - "in": "path", - "required": true - }, - { - "description": "Model", + "description": "Create chat request", "name": "request", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/codersdk.CreateChatModelRequest" + "$ref": "#/definitions/codersdk.CreateChatRequest" } } ], @@ -1360,7 +1197,13 @@ "201": { "description": "Created", "schema": { - "$ref": "#/definitions/codersdk.ChatModel" + "$ref": "#/definitions/codersdk.Chat" + } + }, + "413": { + "description": "Request body exceeds 256 KiB", + "schema": { + "$ref": "#/definitions/codersdk.Response" } } }, @@ -1368,39 +1211,28 @@ { "CoderSessionToken": [] } - ], - "x-apidocgen": { - "skip": true - } + ] } }, - "/api/experimental/organizations/{organization}/chats/models/{model}": { + "/api/v2/chats/by-workspace": { "get": { "produces": ["application/json"], "tags": ["Chats"], - "summary": "Get an AI model", - "operationId": "get-ai-model", + "summary": "List chats by workspace", + "operationId": "list-chats-by-workspace", "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": "Comma-separated workspace IDs", + "name": "workspace_ids", + "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.ChatModel" + "$ref": "#/definitions/coderd.chatsByWorkspaceResponse" } } }, @@ -1408,122 +1240,68 @@ { "CoderSessionToken": [] } - ], - "x-apidocgen": { - "skip": true - } - }, - "delete": { + ] + } + }, + "/api/v2/chats/config/auto-archive-days": { + "get": { + "produces": ["application/json"], "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 - } - ], + "summary": "Get chat auto archive days", + "operationId": "get-chat-auto-archive-days", "responses": { - "204": { - "description": "No Content" + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.ChatAutoArchiveDaysResponse" + } } }, "security": [ { "CoderSessionToken": [] } - ], - "x-apidocgen": { - "skip": true - } + ] }, - "patch": { + "put": { "consumes": ["application/json"], - "produces": ["application/json"], "tags": ["Chats"], - "summary": "Update an AI model", - "operationId": "update-ai-model", + "summary": "Update chat auto archive days", + "operationId": "update-chat-auto-archive-days", "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", + "description": "Request body", "name": "request", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/codersdk.UpdateChatModelRequest" + "$ref": "#/definitions/codersdk.UpdateChatAutoArchiveDaysRequest" } } ], "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.ChatModel" - } + "204": { + "description": "No Content" } }, "security": [ { "CoderSessionToken": [] } - ], - "x-apidocgen": { - "skip": true - } + ] } }, - "/api/experimental/organizations/{organization}/chats/models/{model}/acl": { + "/api/v2/chats/config/debug-logging": { "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 - } - ], + "summary": "Get chat debug logging setting", + "operationId": "get-chat-debug-logging-setting", "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.ChatModelACL" + "$ref": "#/definitions/codersdk.ChatDebugLoggingAdminSettings" } } }, @@ -1531,39 +1309,21 @@ { "CoderSessionToken": [] } - ], - "x-apidocgen": { - "skip": true - } + ] }, - "patch": { + "put": { "consumes": ["application/json"], "tags": ["Chats"], - "summary": "Update an AI model ACL", - "operationId": "update-ai-model-acl", + "summary": "Update chat debug logging setting", + "operationId": "update-chat-debug-logging-setting", "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", + "description": "Request body", "name": "request", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/codersdk.UpdateChatModelACLRequest" + "$ref": "#/definitions/codersdk.UpdateChatDebugLoggingAllowUsersRequest" } } ], @@ -1576,36 +1336,20 @@ { "CoderSessionToken": [] } - ], - "x-apidocgen": { - "skip": true - } + ] } }, - "/api/experimental/organizations/{organization}/mcp-servers": { + "/api/v2/chats/config/debug-retention-days": { "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 - } - ], + "tags": ["Chats"], + "summary": "Get chat debug retention days", + "operationId": "get-chat-debug-retention-days", "responses": { "200": { "description": "OK", "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/codersdk.MCPServerConfig" - } + "$ref": "#/definitions/codersdk.ChatDebugRetentionDaysResponse" } } }, @@ -1613,83 +1357,47 @@ { "CoderSessionToken": [] } - ], - "x-apidocgen": { - "skip": true - } + ] }, - "post": { + "put": { "consumes": ["application/json"], - "produces": ["application/json"], - "tags": ["MCP"], - "summary": "Create MCP server config", - "operationId": "create-mcp-server-config", + "tags": ["Chats"], + "summary": "Update chat debug retention days", + "operationId": "update-chat-debug-retention-days", "parameters": [ { - "type": "string", - "format": "uuid", - "description": "Organization ID", - "name": "organization", - "in": "path", - "required": true - }, - { - "description": "Create MCP server config request", + "description": "Request body", "name": "request", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/codersdk.CreateMCPServerConfigRequest" + "$ref": "#/definitions/codersdk.UpdateChatDebugRetentionDaysRequest" } } ], "responses": { - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/codersdk.MCPServerConfig" - } + "204": { + "description": "No Content" } }, "security": [ { "CoderSessionToken": [] } - ], - "x-apidocgen": { - "skip": true - } + ] } }, - "/api/experimental/organizations/{organization}/mcp-servers/{mcpserverconfig}": { + "/api/v2/chats/config/personal-model-overrides": { "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 - } - ], + "tags": ["Chats"], + "summary": "Get chat personal model override settings", + "operationId": "get-chat-personal-model-override-settings", "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.MCPServerConfig" + "$ref": "#/definitions/codersdk.ChatPersonalModelOverridesAdminSettings" } } }, @@ -1697,31 +1405,22 @@ { "CoderSessionToken": [] } - ], - "x-apidocgen": { - "skip": true - } + ] }, - "delete": { - "tags": ["MCP"], - "summary": "Delete MCP server config", - "operationId": "delete-mcp-server-config", + "put": { + "consumes": ["application/json"], + "tags": ["Chats"], + "summary": "Update chat personal model override settings", + "operationId": "update-chat-personal-model-override-settings", "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": "Request body", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateChatPersonalModelOverridesAdminSettingsRequest" + } } ], "responses": { @@ -1733,91 +1432,68 @@ { "CoderSessionToken": [] } - ], - "x-apidocgen": { - "skip": true - } + ] + } + }, + "/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": [] + } + ] }, - "patch": { + "put": { "consumes": ["application/json"], - "produces": ["application/json"], - "tags": ["MCP"], - "summary": "Update MCP server config", - "operationId": "update-mcp-server-config", + "tags": ["Chats"], + "summary": "Update chat plan mode instructions", + "operationId": "update-chat-plan-mode-instructions", "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", + "description": "Request body", "name": "request", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/codersdk.UpdateMCPServerConfigRequest" + "$ref": "#/definitions/codersdk.UpdateChatPlanModeInstructionsRequest" } } ], "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.MCPServerConfig" - } + "204": { + "description": "No Content" } }, "security": [ { "CoderSessionToken": [] } - ], - "x-apidocgen": { - "skip": true - } + ] } }, - "/api/experimental/organizations/{organization}/mcp-servers/{mcpserverconfig}/acl": { + "/api/v2/chats/config/retention-days": { "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 - } - ], + "tags": ["Chats"], + "summary": "Get chat retention days", + "operationId": "get-chat-retention-days", "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.MCPServerConfigACL" + "$ref": "#/definitions/codersdk.ChatRetentionDaysResponse" } } }, @@ -1825,40 +1501,21 @@ { "CoderSessionToken": [] } - ], - "x-apidocgen": { - "skip": true - } + ] }, - "patch": { + "put": { "consumes": ["application/json"], - "tags": ["MCP"], - "summary": "Update MCP server config ACL", - "operationId": "update-mcp-server-config-acl", + "tags": ["Chats"], + "summary": "Update chat retention days", + "operationId": "update-chat-retention-days", "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", + "description": "Request body", "name": "request", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/codersdk.UpdateMCPServerConfigACLRequest" + "$ref": "#/definitions/codersdk.UpdateChatRetentionDaysRequest" } } ], @@ -1871,77 +1528,68 @@ { "CoderSessionToken": [] } - ], - "x-apidocgen": { - "skip": true - } + ] } }, - "/api/experimental/organizations/{organization}/mcp-servers/{mcpserverconfig}/oauth2/connect": { + "/api/v2/chats/config/system-prompt": { "get": { - "tags": ["MCP"], - "summary": "Initiate MCP server OAuth2 connect", - "operationId": "initiate-mcp-server-oauth2-connect", - "parameters": [ + "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": [ { - "type": "string", - "format": "uuid", - "description": "Organization ID", - "name": "organization", - "in": "path", - "required": true - }, + "CoderSessionToken": [] + } + ] + }, + "put": { + "consumes": ["application/json"], + "tags": ["Chats"], + "summary": "Update chat system prompt", + "operationId": "update-chat-system-prompt", + "parameters": [ { - "type": "string", - "format": "uuid", - "description": "MCP server config ID", - "name": "mcpserverconfig", - "in": "path", - "required": true + "description": "Request body", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateChatSystemPromptRequest" + } } ], "responses": { - "307": { - "description": "Temporary Redirect" + "204": { + "description": "No Content" } }, "security": [ { "CoderSessionToken": [] } - ], - "x-apidocgen": { - "skip": true - } + ] } }, - "/api/experimental/organizations/{organization}/members/{user}/chats/model-overrides": { + "/api/v2/chats/config/user-compaction-thresholds": { "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 - } - ], + "summary": "Get user chat compaction thresholds", + "operationId": "get-user-chat-compaction-thresholds", "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.UserChatPersonalModelOverridesResponse" + "$ref": "#/definitions/codersdk.UserChatCompactionThresholds" } } }, @@ -1949,89 +1597,84 @@ { "CoderSessionToken": [] } - ], - "x-apidocgen": { - "skip": true - } + ] } }, - "/api/experimental/organizations/{organization}/members/{user}/chats/model-overrides/{context}": { + "/api/v2/chats/config/user-compaction-thresholds/{modelConfig}": { "put": { "consumes": ["application/json"], + "produces": ["application/json"], "tags": ["Chats"], - "summary": "Update organization member chat model override", - "operationId": "update-organization-member-chat-model-override", + "summary": "Update user chat compaction threshold", + "operationId": "update-user-chat-compaction-threshold", "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", + "description": "Model config ID", + "name": "modelConfig", "in": "path", "required": true }, { - "description": "Personal model override", + "description": "Request body", "name": "request", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/codersdk.UpdateUserChatPersonalModelOverrideRequest" + "$ref": "#/definitions/codersdk.UpdateUserChatCompactionThresholdRequest" } } ], "responses": { - "204": { - "description": "No Content" + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.UserChatCompactionThreshold" + } } }, "security": [ { "CoderSessionToken": [] } - ], - "x-apidocgen": { - "skip": true - } - } - }, - "/api/experimental/users/{user}/skills": { - "get": { - "produces": ["application/json"], - "tags": ["Users"], - "summary": "List user skills", - "operationId": "list-user-skills", + ] + }, + "delete": { + "tags": ["Chats"], + "summary": "Delete user chat compaction threshold", + "operationId": "delete-user-chat-compaction-threshold", "parameters": [ { "type": "string", - "description": "User ID, username, or me", - "name": "user", + "description": "Model config ID", + "name": "modelConfig", "in": "path", "required": true } ], + "responses": { + "204": { + "description": "No Content" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/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": { - "type": "array", - "items": { - "$ref": "#/definitions/codersdk.UserSkillMetadata" - } + "$ref": "#/definitions/codersdk.UserChatDebugLoggingSettings" } } }, @@ -2039,80 +1682,47 @@ { "CoderSessionToken": [] } - ], - "x-apidocgen": { - "skip": true - } + ] }, - "post": { + "put": { "consumes": ["application/json"], - "produces": ["application/json"], - "tags": ["Users"], - "summary": "Create a user skill", - "operationId": "create-a-user-skill", + "tags": ["Chats"], + "summary": "Update user chat debug logging setting", + "operationId": "update-user-chat-debug-logging-setting", "parameters": [ { - "type": "string", - "description": "User ID, username, or me", - "name": "user", - "in": "path", - "required": true - }, - { - "description": "Create user skill request", + "description": "Request body", "name": "request", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/codersdk.CreateUserSkillRequest" + "$ref": "#/definitions/codersdk.UpdateUserChatDebugLoggingRequest" } } ], "responses": { - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/codersdk.UserSkill" - } + "204": { + "description": "No Content" } }, "security": [ { "CoderSessionToken": [] } - ], - "x-apidocgen": { - "skip": true - } + ] } }, - "/api/experimental/users/{user}/skills/{skillName}": { + "/api/v2/chats/config/user-prompt": { "get": { "produces": ["application/json"], - "tags": ["Users"], - "summary": "Get a user skill by name", - "operationId": "get-a-user-skill-by-name", - "parameters": [ - { - "type": "string", - "description": "User ID, username, or me", - "name": "user", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Skill name", - "name": "skillName", - "in": "path", - "required": true - } - ], + "tags": ["Chats"], + "summary": "Get user chat custom prompt", + "operationId": "get-user-chat-custom-prompt", "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.UserSkill" + "$ref": "#/definitions/codersdk.UserChatCustomPrompt" } } }, @@ -2120,73 +1730,22 @@ { "CoderSessionToken": [] } - ], - "x-apidocgen": { - "skip": true - } - }, - "delete": { - "tags": ["Users"], - "summary": "Delete a user skill", - "operationId": "delete-a-user-skill", - "parameters": [ - { - "type": "string", - "description": "User ID, username, or me", - "name": "user", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Skill name", - "name": "skillName", - "in": "path", - "required": true - } - ], - "responses": { - "204": { - "description": "No Content" - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ], - "x-apidocgen": { - "skip": true - } + ] }, - "patch": { + "put": { "consumes": ["application/json"], "produces": ["application/json"], - "tags": ["Users"], - "summary": "Update a user skill", - "operationId": "update-a-user-skill", + "tags": ["Chats"], + "summary": "Update user chat custom prompt", + "operationId": "update-user-chat-custom-prompt", "parameters": [ { - "type": "string", - "description": "User ID, username, or me", - "name": "user", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Skill name", - "name": "skillName", - "in": "path", - "required": true - }, - { - "description": "Update user skill request", + "description": "Request body", "name": "request", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/codersdk.UpdateUserSkillRequest" + "$ref": "#/definitions/codersdk.UserChatCustomPrompt" } } ], @@ -2194,7 +1753,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.UserSkill" + "$ref": "#/definitions/codersdk.UserChatCustomPrompt" } } }, @@ -2202,70 +1761,95 @@ { "CoderSessionToken": [] } - ], - "x-apidocgen": { - "skip": true - } + ] } }, - "/api/experimental/watch-all-workspacebuilds": { + "/api/v2/chats/config/workspace-ttl": { "get": { "produces": ["application/json"], - "tags": ["Workspaces"], - "summary": "Watch all workspace builds", - "operationId": "watch-all-workspace-builds", + "tags": ["Chats"], + "summary": "Get chat workspace time to live", + "operationId": "get-chat-workspace-time-to-live", "responses": { - "101": { - "description": "Switching Protocols" + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.ChatWorkspaceTTLResponse" + } } }, "security": [ { "CoderSessionToken": [] } - ], - "x-apidocgen": { - "skip": true - } - } - }, - "/api/v2/": { - "get": { - "produces": ["application/json"], - "tags": ["General"], - "summary": "API root handler", - "operationId": "api-root-handler", - "responses": { - "200": { - "description": "OK", + ] + }, + "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.Response" + "$ref": "#/definitions/codersdk.UpdateChatWorkspaceTTLRequest" } } - } - } - }, - "/api/v2/agent-firewall/sessions/{id}": { - "get": { - "produces": ["application/json"], - "tags": ["Enterprise"], - "summary": "Get agent firewall session by ID", - "operationId": "get-agent-firewall-session-by-id", + ], + "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": "Agent firewall session ID", - "name": "id", - "in": "path", + "description": "Organization ID", + "name": "organization", + "in": "query", "required": true } ], "responses": { - "200": { - "description": "OK", + "201": { + "description": "Created", "schema": { - "$ref": "#/definitions/codersdk.AgentFirewallSession" + "$ref": "#/definitions/codersdk.UploadChatFileResponse" + } + }, + "413": { + "description": "Request body exceeds 10 MiB", + "schema": { + "$ref": "#/definitions/codersdk.Response" } } }, @@ -2276,45 +1860,83 @@ ] } }, - "/api/v2/agent-firewall/sessions/{id}/logs": { + "/api/v2/chats/files/{file}": { "get": { - "produces": ["application/json"], - "tags": ["Enterprise"], - "summary": "Get agent firewall session logs", - "operationId": "get-agent-firewall-session-logs", + "tags": ["Chats"], + "summary": "Get chat file", + "operationId": "get-chat-file", "parameters": [ { "type": "string", "format": "uuid", - "description": "Agent firewall session ID", - "name": "id", + "description": "File ID", + "name": "file", "in": "path", "required": true - }, + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ { - "type": "integer", - "description": "Inclusive lower bound on sequence number", - "name": "seq_after", - "in": "query" - }, + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/chats/files/{file}/download": { + "get": { + "tags": ["Chats"], + "summary": "Download chat file with signed token", + "operationId": "download-chat-file-with-signed-token", + "parameters": [ { - "type": "integer", - "description": "Exclusive upper bound on sequence number", - "name": "seq_before", - "in": "query" + "type": "string", + "format": "uuid", + "description": "File ID", + "name": "file", + "in": "path", + "required": true }, { - "type": "integer", - "description": "Maximum number of logs to return (default 100)", - "name": "limit", - "in": "query" + "type": "string", + "description": "Signed download token", + "name": "token", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/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.AgentFirewallSessionLogsResponse" + "$ref": "#/definitions/codersdk.ChatFileDownloadURLResponse" } } }, @@ -2325,21 +1947,17 @@ ] } }, - "/api/v2/ai-gateway/clients": { + "/api/v2/chats/models": { "get": { - "description": "Alias: also available at /api/v2/aibridge/clients for backward compatibility.", "produces": ["application/json"], - "tags": ["AI Gateway"], - "summary": "List AI Gateway clients", - "operationId": "list-ai-gateway-clients", + "tags": ["Chats"], + "summary": "List AI models and provider descriptors in the default organization", + "operationId": "list-ai-models-and-provider-descriptors-in-the-default-organization", "responses": { "200": { "description": "OK", "schema": { - "type": "array", - "items": { - "type": "string" - } + "$ref": "#/definitions/codersdk.OrganizationChatModelsResponse" } } }, @@ -2350,20 +1968,17 @@ ] } }, - "/api/v2/ai-gateway/keys": { + "/api/v2/chats/watch": { "get": { "produces": ["application/json"], - "tags": ["Enterprise"], - "summary": "List AI Gateway keys", - "operationId": "list-ai-gateway-keys", + "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": { - "type": "array", - "items": { - "$ref": "#/definitions/codersdk.AIGatewayKey" - } + "$ref": "#/definitions/codersdk.ChatWatchEvent" } } }, @@ -2372,29 +1987,29 @@ "CoderSessionToken": [] } ] - }, - "post": { - "consumes": ["application/json"], + } + }, + "/api/v2/chats/{chat}": { + "get": { "produces": ["application/json"], - "tags": ["Enterprise"], - "summary": "Create AI Gateway key", - "operationId": "create-ai-gateway-key", + "tags": ["Chats"], + "summary": "Get chat by ID", + "operationId": "get-chat-by-id", "parameters": [ { - "description": "Create AI Gateway key request", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.CreateAIGatewayKeyRequest" - } + "type": "string", + "format": "uuid", + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true } ], "responses": { - "201": { - "description": "Created", + "200": { + "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.CreateAIGatewayKeyResponse" + "$ref": "#/definitions/codersdk.Chat" } } }, @@ -2403,21 +2018,29 @@ "CoderSessionToken": [] } ] - } - }, - "/api/v2/ai-gateway/keys/{key}": { - "delete": { - "tags": ["Enterprise"], - "summary": "Delete AI Gateway key", - "operationId": "delete-ai-gateway-key", + }, + "patch": { + "consumes": ["application/json"], + "tags": ["Chats"], + "summary": "Update chat", + "operationId": "update-chat", "parameters": [ { "type": "string", "format": "uuid", - "description": "Key ID", - "name": "key", + "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": { @@ -2432,21 +2055,27 @@ ] } }, - "/api/v2/ai-gateway/models": { + "/api/v2/chats/{chat}/acl": { "get": { - "description": "Alias: also available at /api/v2/aibridge/models for backward compatibility.", "produces": ["application/json"], - "tags": ["AI Gateway"], - "summary": "List AI Gateway models", - "operationId": "list-ai-gateway-models", + "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": { - "type": "array", - "items": { - "type": "string" - } + "$ref": "#/definitions/codersdk.ChatACL" } } }, @@ -2455,64 +2084,34 @@ "CoderSessionToken": [] } ] - } - }, - "/api/v2/ai-gateway/serve": { - "get": { - "tags": ["Enterprise"], - "summary": "AI Gateway serve", - "operationId": "ai-gateway-serve", - "responses": { - "101": { - "description": "Switching Protocols" - } - }, - "security": [ - { - "AIGatewayKey": [] - } - ] - } - }, - "/api/v2/ai-gateway/sessions": { - "get": { - "description": "Alias: also available at /api/v2/aibridge/sessions for backward compatibility.", - "produces": ["application/json"], - "tags": ["AI Gateway"], - "summary": "List AI Gateway sessions", - "operationId": "list-ai-gateway-sessions", - "parameters": [ - { - "type": "string", - "description": "Search query in the format `key:value`. Available keys are: initiator, provider, provider_name, model, client, session_id, started_after, started_before.", - "name": "q", - "in": "query" - }, - { - "type": "integer", - "description": "Page limit", - "name": "limit", - "in": "query" - }, + }, + "patch": { + "consumes": ["application/json"], + "tags": ["Chats"], + "summary": "Update chat ACL", + "operationId": "update-chat-acl", + "parameters": [ { "type": "string", - "description": "Cursor pagination after session ID (cannot be used with offset)", - "name": "after_session_id", - "in": "query" + "format": "uuid", + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true }, { - "type": "integer", - "description": "Offset pagination (cannot be used with after_session_id)", - "name": "offset", - "in": "query" + "description": "Update chat ACL request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateChatACL" + } } ], "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.AIBridgeListSessionsResponse" - } + "204": { + "description": "No Content" } }, "security": [ @@ -2522,45 +2121,28 @@ ] } }, - "/api/v2/ai-gateway/sessions/{session_id}": { - "get": { - "description": "Alias: also available at /api/v2/aibridge/sessions/{session_id} for backward compatibility.", + "/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": ["AI Gateway"], - "summary": "Get AI Gateway session threads", - "operationId": "get-ai-gateway-session-threads", + "tags": ["Chats"], + "summary": "Compact chat", + "operationId": "compact-chat", "parameters": [ { "type": "string", - "description": "Session ID (client_session_id or interception UUID)", - "name": "session_id", + "format": "uuid", + "description": "Chat ID", + "name": "chat", "in": "path", "required": true - }, - { - "type": "string", - "description": "Thread pagination cursor (forward/older)", - "name": "after_id", - "in": "query" - }, - { - "type": "string", - "description": "Thread pagination cursor (backward/newer)", - "name": "before_id", - "in": "query" - }, - { - "type": "integer", - "description": "Number of threads per page (default 50)", - "name": "limit", - "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.AIBridgeSessionThreadsResponse" + "$ref": "#/definitions/codersdk.Chat" } } }, @@ -2571,51 +2153,27 @@ ] } }, - "/api/v2/ai/providers": { - "get": { - "produces": ["application/json"], - "tags": ["AI Providers"], - "summary": "List AI providers", - "operationId": "list-ai-providers", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/codersdk.AIProvider" - } - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ] - }, - "post": { - "consumes": ["application/json"], + "/api/v2/chats/{chat}/context": { + "put": { "produces": ["application/json"], - "tags": ["AI Providers"], - "summary": "Create an AI provider", - "operationId": "create-an-ai-provider", + "tags": ["Chats"], + "summary": "Refresh chat context", + "operationId": "refresh-chat-context", "parameters": [ { - "description": "Create AI provider request", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.CreateAIProviderRequest" - } + "type": "string", + "format": "uuid", + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true } ], "responses": { - "201": { - "description": "Created", + "200": { + "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.AIProvider" + "$ref": "#/definitions/codersdk.Chat" } } }, @@ -2626,17 +2184,19 @@ ] } }, - "/api/v2/ai/providers/{idOrName}": { + "/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": ["AI Providers"], - "summary": "Get an AI provider", - "operationId": "get-an-ai-provider", + "tags": ["Chats"], + "summary": "Get chat cost", + "operationId": "get-chat-cost", "parameters": [ { "type": "string", - "description": "Provider ID or name", - "name": "idOrName", + "format": "uuid", + "description": "Chat ID", + "name": "chat", "in": "path", "required": true } @@ -2645,7 +2205,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.AIProvider" + "$ref": "#/definitions/codersdk.ChatCost" } } }, @@ -2654,23 +2214,30 @@ "CoderSessionToken": [] } ] - }, - "delete": { - "tags": ["AI Providers"], - "summary": "Delete an AI provider", - "operationId": "delete-an-ai-provider", + } + }, + "/api/v2/chats/{chat}/diff": { + "get": { + "produces": ["application/json"], + "tags": ["Chats"], + "summary": "Get chat diff contents", + "operationId": "get-chat-diff-contents", "parameters": [ { "type": "string", - "description": "Provider ID or name", - "name": "idOrName", + "format": "uuid", + "description": "Chat ID", + "name": "chat", "in": "path", "required": true } ], "responses": { - "204": { - "description": "No Content" + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.ChatDiffContents" + } } }, "security": [ @@ -2678,36 +2245,29 @@ "CoderSessionToken": [] } ] - }, - "patch": { - "consumes": ["application/json"], + } + }, + "/api/v2/chats/{chat}/interrupt": { + "post": { "produces": ["application/json"], - "tags": ["AI Providers"], - "summary": "Update an AI provider", - "operationId": "update-an-ai-provider", + "tags": ["Chats"], + "summary": "Interrupt chat", + "operationId": "interrupt-chat", "parameters": [ { "type": "string", - "description": "Provider ID or name", - "name": "idOrName", + "format": "uuid", + "description": "Chat ID", + "name": "chat", "in": "path", "required": true - }, - { - "description": "Update AI provider request", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.UpdateAIProviderRequest" - } } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.AIProvider" + "$ref": "#/definitions/codersdk.Chat" } } }, @@ -2718,17 +2278,45 @@ ] } }, - "/api/v2/appearance": { + "/api/v2/chats/{chat}/messages": { "get": { "produces": ["application/json"], - "tags": ["Enterprise"], - "summary": "Get appearance", - "operationId": "get-appearance", + "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.AppearanceConfig" + "$ref": "#/definitions/codersdk.ChatMessagesResponse" } } }, @@ -2738,20 +2326,28 @@ } ] }, - "put": { + "post": { "consumes": ["application/json"], "produces": ["application/json"], - "tags": ["Enterprise"], - "summary": "Update appearance", - "operationId": "update-appearance", + "tags": ["Chats"], + "summary": "Send chat message", + "operationId": "send-chat-message", "parameters": [ { - "description": "Update appearance request", + "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.UpdateAppearanceConfig" + "$ref": "#/definitions/codersdk.CreateChatMessageRequest" } } ], @@ -2759,7 +2355,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.UpdateAppearanceConfig" + "$ref": "#/definitions/codersdk.CreateChatMessageResponse" } } }, @@ -2770,68 +2366,36 @@ ] } }, - "/api/v2/applications/auth-redirect": { - "get": { - "tags": ["Applications"], - "summary": "Redirect to URI with encrypted API key", - "operationId": "redirect-to-uri-with-encrypted-api-key", + "/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", - "description": "Redirect destination", - "name": "redirect_uri", - "in": "query" - } - ], - "responses": { - "307": { - "description": "Temporary Redirect" - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ] - } - }, - "/api/v2/applications/host": { - "get": { - "produces": ["application/json"], - "tags": ["Applications"], - "summary": "Get applications host", - "operationId": "get-applications-host", - "deprecated": true, - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.AppHostResponse" - } - } - }, - "security": [ + "format": "uuid", + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true + }, { - "CoderSessionToken": [] - } - ] - } - }, - "/api/v2/applications/reconnecting-pty-signed-token": { - "post": { - "consumes": ["application/json"], - "produces": ["application/json"], - "tags": ["Enterprise"], - "summary": "Issue signed app token for reconnecting PTY", - "operationId": "issue-signed-app-token-for-reconnecting-pty", - "parameters": [ + "type": "integer", + "description": "Message ID", + "name": "message", + "in": "path", + "required": true + }, { - "description": "Issue reconnecting PTY signed token request", + "description": "Edit chat message request", "name": "request", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/codersdk.IssueReconnectingPTYSignedTokenRequest" + "$ref": "#/definitions/codersdk.EditChatMessageRequest" } } ], @@ -2839,7 +2403,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.IssueReconnectingPTYSignedTokenResponse" + "$ref": "#/definitions/codersdk.EditChatMessageResponse" } } }, @@ -2847,36 +2411,29 @@ { "CoderSessionToken": [] } - ], - "x-apidocgen": { - "skip": true - } + ] } }, - "/api/v2/audit": { + "/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": ["Audit"], - "summary": "Get audit logs", - "operationId": "get-audit-logs", + "tags": ["Chats"], + "summary": "List chat user prompts", + "operationId": "list-chat-user-prompts", "parameters": [ { "type": "string", - "description": "Search query", - "name": "q", - "in": "query" - }, - { - "type": "integer", - "description": "Page limit", - "name": "limit", - "in": "query", + "format": "uuid", + "description": "Chat ID", + "name": "chat", + "in": "path", "required": true }, { "type": "integer", - "description": "Page offset", - "name": "offset", + "description": "Page size, 0 to 2000. 0 (the default) means the server-side default of 500.", + "name": "limit", "in": "query" } ], @@ -2884,7 +2441,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.AuditLogResponse" + "$ref": "#/definitions/codersdk.ChatPromptsResponse" } } }, @@ -2895,21 +2452,25 @@ ] } }, - "/api/v2/audit/testgenerate": { - "post": { - "consumes": ["application/json"], - "tags": ["Audit"], - "summary": "Generate fake audit log", - "operationId": "generate-fake-audit-log", + "/api/v2/chats/{chat}/queue/{queuedMessage}": { + "delete": { + "tags": ["Chats"], + "summary": "Delete chat queued message", + "operationId": "delete-chat-queued-message", "parameters": [ { - "description": "Audit log request", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.CreateTestAuditLogRequest" - } + "type": "string", + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Queued message ID", + "name": "queuedMessage", + "in": "path", + "required": true } ], "responses": { @@ -2921,51 +2482,67 @@ { "CoderSessionToken": [] } - ], - "x-apidocgen": { - "skip": true - } + ] } }, - "/api/v2/auth/scopes": { - "get": { + "/api/v2/chats/{chat}/queue/{queuedMessage}/promote": { + "post": { "produces": ["application/json"], - "tags": ["Authorization"], - "summary": "List API key scopes", - "operationId": "list-api-key-scopes", + "tags": ["Chats"], + "summary": "Promote chat queued message", + "operationId": "promote-chat-queued-message", + "parameters": [ + { + "type": "string", + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Queued message ID", + "name": "queuedMessage", + "in": "path", + "required": true + } + ], "responses": { - "200": { - "description": "OK", + "202": { + "description": "Accepted", "schema": { - "$ref": "#/definitions/codersdk.ExternalAPIKeyScopes" + "$ref": "#/definitions/codersdk.Response" } } - } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] } }, - "/api/v2/authcheck": { + "/api/v2/chats/{chat}/reconcile-invalid": { "post": { - "consumes": ["application/json"], "produces": ["application/json"], - "tags": ["Authorization"], - "summary": "Check authorization", - "operationId": "check-authorization", + "tags": ["Chats"], + "summary": "Reconcile invalid chat state", + "operationId": "reconcile-invalid-chat-state", "parameters": [ { - "description": "Authorization request", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.AuthorizationRequest" - } + "type": "string", + "format": "uuid", + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.AuthorizationResponse" + "$ref": "#/definitions/codersdk.Chat" } } }, @@ -2976,54 +2553,58 @@ ] } }, - "/api/v2/buildinfo": { + "/api/v2/chats/{chat}/stream": { "get": { "produces": ["application/json"], - "tags": ["General"], - "summary": "Build info", - "operationId": "build-info", + "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.BuildInfoResponse" + "$ref": "#/definitions/codersdk.ChatStreamEvent" } } - } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] } }, - "/api/v2/connectionlog": { + "/api/v2/chats/{chat}/stream/git": { "get": { "produces": ["application/json"], - "tags": ["Enterprise"], - "summary": "Get connection logs", - "operationId": "get-connection-logs", + "tags": ["Chats"], + "summary": "Watch chat workspace git state via WebSockets", + "operationId": "watch-chat-workspace-git-state-via-websockets", "parameters": [ { "type": "string", - "description": "Search query", - "name": "q", - "in": "query" - }, - { - "type": "integer", - "description": "Page limit", - "name": "limit", - "in": "query", + "format": "uuid", + "description": "Chat ID", + "name": "chat", + "in": "path", "required": true - }, - { - "type": "integer", - "description": "Page offset", - "name": "offset", - "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.ConnectionLogResponse" + "$ref": "#/definitions/codersdk.WorkspaceAgentGitServerMessage" } } }, @@ -3034,15 +2615,185 @@ ] } }, - "/api/v2/csp/reports": { - "post": { - "consumes": ["application/json"], - "tags": ["General"], - "summary": "Report CSP violations", - "operationId": "report-csp-violations", - "parameters": [ - { - "description": "Violation report", + "/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": { + "$ref": "#/definitions/codersdk.ChatStreamEvent" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/chats/{chat}/title/propose": { + "post": { + "produces": ["application/json"], + "tags": ["Chats"], + "summary": "Propose chat title", + "operationId": "propose-chat-title", + "parameters": [ + { + "type": "string", + "description": "Chat ID", + "name": "chat", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.ProposeChatTitleResponse" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/chats/{chat}/title/regenerate": { + "post": { + "produces": ["application/json"], + "tags": ["Chats"], + "summary": "Regenerate chat title", + "operationId": "regenerate-chat-title", + "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}/tool-results": { + "post": { + "consumes": ["application/json"], + "tags": ["Chats"], + "summary": "Submit chat tool results", + "operationId": "submit-chat-tool-results", + "parameters": [ + { + "type": "string", + "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"], + "tags": ["Enterprise"], + "summary": "Get connection logs", + "operationId": "get-connection-logs", + "parameters": [ + { + "type": "string", + "description": "Search query", + "name": "q", + "in": "query" + }, + { + "type": "integer", + "description": "Page limit", + "name": "limit", + "in": "query", + "required": true + }, + { + "type": "integer", + "description": "Page offset", + "name": "offset", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.ConnectionLogResponse" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/csp/reports": { + "post": { + "consumes": ["application/json"], + "tags": ["General"], + "summary": "Report CSP violations", + "operationId": "report-csp-violations", + "parameters": [ + { + "description": "Violation report", "name": "request", "in": "body", "required": true, @@ -4456,7 +4207,611 @@ "schema": { "type": "array", "items": { - "$ref": "#/definitions/codersdk.License" + "$ref": "#/definitions/codersdk.License" + } + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + }, + "post": { + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["Enterprise"], + "summary": "Add new license", + "operationId": "add-new-license", + "parameters": [ + { + "description": "Add license request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.AddLicenseRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/codersdk.License" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/licenses/refresh-entitlements": { + "post": { + "produces": ["application/json"], + "tags": ["Enterprise"], + "summary": "Update license entitlements", + "operationId": "update-license-entitlements", + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/codersdk.Response" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/licenses/trial": { + "post": { + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["Enterprise"], + "summary": "Request a trial license", + "operationId": "request-a-trial-license", + "parameters": [ + { + "description": "Trial license request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.CreateTrialLicenseRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/codersdk.License" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/licenses/{id}": { + "delete": { + "produces": ["application/json"], + "tags": ["Enterprise"], + "summary": "Delete license", + "operationId": "delete-license", + "parameters": [ + { + "type": "string", + "format": "number", + "description": "License ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/mcp/servers/{mcpServer}/oauth2/callback": { + "get": { + "tags": ["MCP"], + "summary": "Handle MCP server OAuth2 callback", + "operationId": "handle-mcp-server-oauth2-callback", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "MCP server config ID", + "name": "mcpServer", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Authorization code issued by the provider. Required together with state on success.", + "name": "code", + "in": "query" + }, + { + "type": "string", + "description": "Opaque state issued by the connect endpoint. Required together with code on success.", + "name": "state", + "in": "query" + }, + { + "type": "string", + "description": "Provider error code. Present instead of code when authorization fails.", + "name": "error", + "in": "query" + }, + { + "type": "string", + "description": "Provider error description accompanying error.", + "name": "error_description", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/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": [] + } + ] + } + }, + "/api/v2/notifications/custom": { + "post": { + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["Notifications"], + "summary": "Send a custom notification", + "operationId": "send-a-custom-notification", + "parameters": [ + { + "description": "Provide a non-empty title or message", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.CustomNotificationRequest" + } + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Invalid request body", + "schema": { + "$ref": "#/definitions/codersdk.Response" + } + }, + "403": { + "description": "System users cannot send custom notifications", + "schema": { + "$ref": "#/definitions/codersdk.Response" + } + }, + "500": { + "description": "Failed to send custom notification", + "schema": { + "$ref": "#/definitions/codersdk.Response" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/notifications/dispatch-methods": { + "get": { + "produces": ["application/json"], + "tags": ["Notifications"], + "summary": "Get notification dispatch methods", + "operationId": "get-notification-dispatch-methods", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.NotificationMethodsResponse" + } + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/notifications/inbox": { + "get": { + "produces": ["application/json"], + "tags": ["Notifications"], + "summary": "List inbox notifications", + "operationId": "list-inbox-notifications", + "parameters": [ + { + "type": "string", + "description": "Comma-separated list of target IDs to filter notifications", + "name": "targets", + "in": "query" + }, + { + "type": "string", + "description": "Comma-separated list of template IDs to filter notifications", + "name": "templates", + "in": "query" + }, + { + "type": "string", + "description": "Filter notifications by read status. Possible values: read, unread, all", + "name": "read_status", + "in": "query" + }, + { + "type": "string", + "format": "uuid", + "description": "ID of the last notification from the current page. Notifications returned will be older than the associated one", + "name": "starting_before", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.ListInboxNotificationsResponse" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/notifications/inbox/mark-all-as-read": { + "put": { + "tags": ["Notifications"], + "summary": "Mark all unread notifications as read", + "operationId": "mark-all-unread-notifications-as-read", + "responses": { + "204": { + "description": "No Content" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/notifications/inbox/watch": { + "get": { + "produces": ["application/json"], + "tags": ["Notifications"], + "summary": "Watch for new inbox notifications", + "operationId": "watch-for-new-inbox-notifications", + "parameters": [ + { + "type": "string", + "description": "Comma-separated list of target IDs to filter notifications", + "name": "targets", + "in": "query" + }, + { + "type": "string", + "description": "Comma-separated list of template IDs to filter notifications", + "name": "templates", + "in": "query" + }, + { + "type": "string", + "description": "Filter notifications by read status. Possible values: read, unread, all", + "name": "read_status", + "in": "query" + }, + { + "enum": ["plaintext", "markdown"], + "type": "string", + "description": "Define the output format for notifications title and body.", + "name": "format", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.GetInboxNotificationResponse" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/notifications/inbox/{id}/read-status": { + "put": { + "produces": ["application/json"], + "tags": ["Notifications"], + "summary": "Update read status of a notification", + "operationId": "update-read-status-of-a-notification", + "parameters": [ + { + "type": "string", + "description": "id of the notification", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.Response" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/notifications/settings": { + "get": { + "produces": ["application/json"], + "tags": ["Notifications"], + "summary": "Get notifications settings", + "operationId": "get-notifications-settings", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.NotificationsSettings" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + }, + "put": { + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["Notifications"], + "summary": "Update notifications settings", + "operationId": "update-notifications-settings", + "parameters": [ + { + "description": "Notifications settings request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.NotificationsSettings" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.NotificationsSettings" + } + }, + "304": { + "description": "Not Modified" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/notifications/templates/custom": { + "get": { + "produces": ["application/json"], + "tags": ["Notifications"], + "summary": "Get custom notification templates", + "operationId": "get-custom-notification-templates", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.NotificationTemplate" + } + } + }, + "500": { + "description": "Failed to retrieve 'custom' notifications template", + "schema": { + "$ref": "#/definitions/codersdk.Response" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/notifications/templates/system": { + "get": { + "produces": ["application/json"], + "tags": ["Notifications"], + "summary": "Get system notification templates", + "operationId": "get-system-notification-templates", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.NotificationTemplate" + } + } + }, + "500": { + "description": "Failed to retrieve 'system' notifications template", + "schema": { + "$ref": "#/definitions/codersdk.Response" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/notifications/templates/{notification_template}/method": { + "put": { + "produces": ["application/json"], + "tags": ["Enterprise"], + "summary": "Update notification template dispatch method", + "operationId": "update-notification-template-dispatch-method", + "parameters": [ + { + "type": "string", + "description": "Notification template UUID", + "name": "notification_template", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "Success" + }, + "304": { + "description": "Not modified" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/notifications/test": { + "post": { + "tags": ["Notifications"], + "summary": "Send a test notification", + "operationId": "send-a-test-notification", + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/api/v2/oauth2-provider/apps": { + "get": { + "produces": ["application/json"], + "tags": ["Enterprise"], + "summary": "Get OAuth2 applications.", + "operationId": "get-oauth2-applications", + "parameters": [ + { + "type": "string", + "description": "Filter by applications authorized for a user", + "name": "user_id", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.OAuth2ProviderApp" } } } @@ -4471,24 +4826,24 @@ "consumes": ["application/json"], "produces": ["application/json"], "tags": ["Enterprise"], - "summary": "Add new license", - "operationId": "add-new-license", + "summary": "Create OAuth2 application.", + "operationId": "create-oauth2-application", "parameters": [ { - "description": "Add license request", + "description": "The OAuth2 application to create.", "name": "request", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/codersdk.AddLicenseRequest" + "$ref": "#/definitions/codersdk.PostOAuth2ProviderAppRequest" } } ], "responses": { - "201": { - "description": "Created", + "200": { + "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.License" + "$ref": "#/definitions/codersdk.OAuth2ProviderApp" } } }, @@ -4499,17 +4854,26 @@ ] } }, - "/api/v2/licenses/refresh-entitlements": { - "post": { + "/api/v2/oauth2-provider/apps/{app}": { + "get": { "produces": ["application/json"], "tags": ["Enterprise"], - "summary": "Update license entitlements", - "operationId": "update-license-entitlements", + "summary": "Get OAuth2 application.", + "operationId": "get-oauth2-application", + "parameters": [ + { + "type": "string", + "description": "App ID", + "name": "app", + "in": "path", + "required": true + } + ], "responses": { - "201": { - "description": "Created", + "200": { + "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.Response" + "$ref": "#/definitions/codersdk.OAuth2ProviderApp" } } }, @@ -4518,31 +4882,36 @@ "CoderSessionToken": [] } ] - } - }, - "/api/v2/licenses/trial": { - "post": { + }, + "put": { "consumes": ["application/json"], "produces": ["application/json"], "tags": ["Enterprise"], - "summary": "Request a trial license", - "operationId": "request-a-trial-license", + "summary": "Update OAuth2 application.", + "operationId": "update-oauth2-application", "parameters": [ { - "description": "Trial license request", + "type": "string", + "description": "App ID", + "name": "app", + "in": "path", + "required": true + }, + { + "description": "Update an OAuth2 application.", "name": "request", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/codersdk.CreateTrialLicenseRequest" + "$ref": "#/definitions/codersdk.PutOAuth2ProviderAppRequest" } } ], "responses": { - "201": { - "description": "Created", + "200": { + "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.License" + "$ref": "#/definitions/codersdk.OAuth2ProviderApp" } } }, @@ -4551,27 +4920,23 @@ "CoderSessionToken": [] } ] - } - }, - "/api/v2/licenses/{id}": { + }, "delete": { - "produces": ["application/json"], "tags": ["Enterprise"], - "summary": "Delete license", - "operationId": "delete-license", + "summary": "Delete OAuth2 application.", + "operationId": "delete-oauth2-application", "parameters": [ { "type": "string", - "format": "number", - "description": "License ID", - "name": "id", + "description": "App ID", + "name": "app", "in": "path", "required": true } ], "responses": { - "200": { - "description": "OK" + "204": { + "description": "No Content" } }, "security": [ @@ -4581,44 +4946,29 @@ ] } }, - "/api/v2/notifications/custom": { - "post": { - "consumes": ["application/json"], + "/api/v2/oauth2-provider/apps/{app}/secrets": { + "get": { "produces": ["application/json"], - "tags": ["Notifications"], - "summary": "Send a custom notification", - "operationId": "send-a-custom-notification", + "tags": ["Enterprise"], + "summary": "Get OAuth2 application secrets.", + "operationId": "get-oauth2-application-secrets", "parameters": [ { - "description": "Provide a non-empty title or message", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.CustomNotificationRequest" - } + "type": "string", + "description": "App ID", + "name": "app", + "in": "path", + "required": true } ], "responses": { - "204": { - "description": "No Content" - }, - "400": { - "description": "Invalid request body", - "schema": { - "$ref": "#/definitions/codersdk.Response" - } - }, - "403": { - "description": "System users cannot send custom notifications", - "schema": { - "$ref": "#/definitions/codersdk.Response" - } - }, - "500": { - "description": "Failed to send custom notification", + "200": { + "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.Response" + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.OAuth2ProviderAppSecret" + } } } }, @@ -4627,21 +4977,28 @@ "CoderSessionToken": [] } ] - } - }, - "/api/v2/notifications/dispatch-methods": { - "get": { + }, + "post": { "produces": ["application/json"], - "tags": ["Notifications"], - "summary": "Get notification dispatch methods", - "operationId": "get-notification-dispatch-methods", + "tags": ["Enterprise"], + "summary": "Create OAuth2 application secret.", + "operationId": "create-oauth2-application-secret", + "parameters": [ + { + "type": "string", + "description": "App ID", + "name": "app", + "in": "path", + "required": true + } + ], "responses": { "200": { "description": "OK", "schema": { "type": "array", "items": { - "$ref": "#/definitions/codersdk.NotificationMethodsResponse" + "$ref": "#/definitions/codersdk.OAuth2ProviderAppSecretFull" } } } @@ -4653,109 +5010,50 @@ ] } }, - "/api/v2/notifications/inbox": { - "get": { - "produces": ["application/json"], - "tags": ["Notifications"], - "summary": "List inbox notifications", - "operationId": "list-inbox-notifications", + "/api/v2/oauth2-provider/apps/{app}/secrets/{secretID}": { + "delete": { + "tags": ["Enterprise"], + "summary": "Delete OAuth2 application secret.", + "operationId": "delete-oauth2-application-secret", "parameters": [ { "type": "string", - "description": "Comma-separated list of target IDs to filter notifications", - "name": "targets", - "in": "query" - }, - { - "type": "string", - "description": "Comma-separated list of template IDs to filter notifications", - "name": "templates", - "in": "query" - }, - { - "type": "string", - "description": "Filter notifications by read status. Possible values: read, unread, all", - "name": "read_status", - "in": "query" + "description": "App ID", + "name": "app", + "in": "path", + "required": true }, { "type": "string", - "format": "uuid", - "description": "ID of the last notification from the current page. Notifications returned will be older than the associated one", - "name": "starting_before", - "in": "query" + "description": "Secret ID", + "name": "secretID", + "in": "path", + "required": true } ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.ListInboxNotificationsResponse" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ] - } - }, - "/api/v2/notifications/inbox/mark-all-as-read": { - "put": { - "tags": ["Notifications"], - "summary": "Mark all unread notifications as read", - "operationId": "mark-all-unread-notifications-as-read", "responses": { "204": { "description": "No Content" } }, "security": [ - { - "CoderSessionToken": [] - } - ] - } - }, - "/api/v2/notifications/inbox/watch": { - "get": { - "produces": ["application/json"], - "tags": ["Notifications"], - "summary": "Watch for new inbox notifications", - "operationId": "watch-for-new-inbox-notifications", - "parameters": [ - { - "type": "string", - "description": "Comma-separated list of target IDs to filter notifications", - "name": "targets", - "in": "query" - }, - { - "type": "string", - "description": "Comma-separated list of template IDs to filter notifications", - "name": "templates", - "in": "query" - }, - { - "type": "string", - "description": "Filter notifications by read status. Possible values: read, unread, all", - "name": "read_status", - "in": "query" - }, - { - "enum": ["plaintext", "markdown"], - "type": "string", - "description": "Define the output format for notifications title and body.", - "name": "format", - "in": "query" + { + "CoderSessionToken": [] } - ], + ] + } + }, + "/api/v2/oauth2-provider/settings": { + "get": { + "produces": ["application/json"], + "tags": ["Enterprise"], + "summary": "Get OAuth2 provider settings.", + "operationId": "get-oauth2-provider-settings", "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.GetInboxNotificationResponse" + "$ref": "#/definitions/codersdk.OAuth2ProviderSettings" } } }, @@ -4764,28 +5062,29 @@ "CoderSessionToken": [] } ] - } - }, - "/api/v2/notifications/inbox/{id}/read-status": { + }, "put": { + "consumes": ["application/json"], "produces": ["application/json"], - "tags": ["Notifications"], - "summary": "Update read status of a notification", - "operationId": "update-read-status-of-a-notification", + "tags": ["Enterprise"], + "summary": "Update OAuth2 provider settings.", + "operationId": "update-oauth2-provider-settings", "parameters": [ { - "type": "string", - "description": "id of the notification", - "name": "id", - "in": "path", - "required": true + "description": "OAuth2 provider settings request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.OAuth2ProviderSettings" + } } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.Response" + "$ref": "#/definitions/codersdk.OAuth2ProviderSettings" } } }, @@ -4796,17 +5095,20 @@ ] } }, - "/api/v2/notifications/settings": { + "/api/v2/organizations": { "get": { "produces": ["application/json"], - "tags": ["Notifications"], - "summary": "Get notifications settings", - "operationId": "get-notifications-settings", + "tags": ["Organizations"], + "summary": "Get organizations", + "operationId": "get-organizations", "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.NotificationsSettings" + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.Organization" + } } } }, @@ -4816,32 +5118,29 @@ } ] }, - "put": { + "post": { "consumes": ["application/json"], "produces": ["application/json"], - "tags": ["Notifications"], - "summary": "Update notifications settings", - "operationId": "update-notifications-settings", + "tags": ["Organizations"], + "summary": "Create organization", + "operationId": "create-organization", "parameters": [ { - "description": "Notifications settings request", + "description": "Create organization request", "name": "request", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/codersdk.NotificationsSettings" + "$ref": "#/definitions/codersdk.CreateOrganizationRequest" } } ], "responses": { - "200": { - "description": "OK", + "201": { + "description": "Created", "schema": { - "$ref": "#/definitions/codersdk.NotificationsSettings" + "$ref": "#/definitions/codersdk.Organization" } - }, - "304": { - "description": "Not Modified" } }, "security": [ @@ -4851,26 +5150,27 @@ ] } }, - "/api/v2/notifications/templates/custom": { + "/api/v2/organizations/{organization}": { "get": { "produces": ["application/json"], - "tags": ["Notifications"], - "summary": "Get custom notification templates", - "operationId": "get-custom-notification-templates", + "tags": ["Organizations"], + "summary": "Get organization by ID", + "operationId": "get-organization-by-id", + "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.NotificationTemplate" - } - } - }, - "500": { - "description": "Failed to retrieve 'custom' notifications template", - "schema": { - "$ref": "#/definitions/codersdk.Response" + "$ref": "#/definitions/codersdk.Organization" } } }, @@ -4879,26 +5179,24 @@ "CoderSessionToken": [] } ] - } - }, - "/api/v2/notifications/templates/system": { - "get": { + }, + "delete": { "produces": ["application/json"], - "tags": ["Notifications"], - "summary": "Get system notification templates", - "operationId": "get-system-notification-templates", + "tags": ["Organizations"], + "summary": "Delete organization", + "operationId": "delete-organization", + "parameters": [ + { + "type": "string", + "description": "Organization ID or name", + "name": "organization", + "in": "path", + "required": true + } + ], "responses": { "200": { "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/codersdk.NotificationTemplate" - } - } - }, - "500": { - "description": "Failed to retrieve 'system' notifications template", "schema": { "$ref": "#/definitions/codersdk.Response" } @@ -4909,29 +5207,37 @@ "CoderSessionToken": [] } ] - } - }, - "/api/v2/notifications/templates/{notification_template}/method": { - "put": { + }, + "patch": { + "consumes": ["application/json"], "produces": ["application/json"], - "tags": ["Enterprise"], - "summary": "Update notification template dispatch method", - "operationId": "update-notification-template-dispatch-method", + "tags": ["Organizations"], + "summary": "Update organization", + "operationId": "update-organization", "parameters": [ { "type": "string", - "description": "Notification template UUID", - "name": "notification_template", + "description": "Organization ID or name", + "name": "organization", "in": "path", "required": true + }, + { + "description": "Patch organization request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateOrganizationRequest" + } } ], "responses": { "200": { - "description": "Success" - }, - "304": { - "description": "Not modified" + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.Organization" + } } }, "security": [ @@ -4941,11 +5247,37 @@ ] } }, - "/api/v2/notifications/test": { - "post": { - "tags": ["Notifications"], - "summary": "Send a test notification", - "operationId": "send-a-test-notification", + "/api/v2/organizations/{organization}/ai/spend/export": { + "get": { + "description": "Returns per-user, per-group, per-model, per-provider aggregated AI spend for the organization as CSV, built from raw AI Gateway token usage.\nThe optional period_start and period_end query parameters bound the period and are interpreted as UTC. They must be provided together and span at most 31 days. When both are omitted, the current UTC monthly period is used.\nAn explicit period_start must fall within the configured AI Gateway data retention window, since older token usage is purged. The default period is narrowed to that window instead, and every row echoes the applied bounds.\nRequires organization-level administrator permissions.", + "produces": ["text/csv"], + "tags": ["Enterprise"], + "summary": "Export organization AI spend as CSV", + "operationId": "export-organization-ai-spend-as-csv", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Organization ID", + "name": "organization", + "in": "path", + "required": true + }, + { + "type": "string", + "format": "date-time", + "description": "Inclusive lower bound (RFC3339)", + "name": "period_start", + "in": "query" + }, + { + "type": "string", + "format": "date-time", + "description": "Exclusive upper bound (RFC3339)", + "name": "period_end", + "in": "query" + } + ], "responses": { "200": { "description": "OK" @@ -4958,28 +5290,26 @@ ] } }, - "/api/v2/oauth2-provider/apps": { + "/api/v2/organizations/{organization}/chats/model-overrides": { "get": { "produces": ["application/json"], - "tags": ["Enterprise"], - "summary": "Get OAuth2 applications.", - "operationId": "get-oauth2-applications", + "tags": ["Chats"], + "summary": "List organization chat model overrides", + "operationId": "list-organization-chat-model-overrides", "parameters": [ { "type": "string", - "description": "Filter by applications authorized for a user", - "name": "user_id", - "in": "query" + "description": "Organization name or ID", + "name": "organization", + "in": "path", + "required": true } ], "responses": { "200": { "description": "OK", "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/codersdk.OAuth2ProviderApp" - } + "$ref": "#/definitions/codersdk.ChatModelOverridesResponse" } } }, @@ -4988,21 +5318,44 @@ "CoderSessionToken": [] } ] - }, - "post": { + } + }, + "/api/v2/organizations/{organization}/chats/model-overrides/{context}": { + "put": { "consumes": ["application/json"], "produces": ["application/json"], - "tags": ["Enterprise"], - "summary": "Create OAuth2 application.", - "operationId": "create-oauth2-application", + "tags": ["Chats"], + "summary": "Update organization chat model override", + "operationId": "update-organization-chat-model-override", "parameters": [ { - "description": "The OAuth2 application to create.", + "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.PostOAuth2ProviderAppRequest" + "$ref": "#/definitions/codersdk.UpdateChatModelOverrideRequest" } } ], @@ -5010,7 +5363,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.OAuth2ProviderApp" + "$ref": "#/definitions/codersdk.ChatModelOverrideResponse" } } }, @@ -5021,17 +5374,17 @@ ] } }, - "/api/v2/oauth2-provider/apps/{app}": { + "/api/v2/organizations/{organization}/chats/models": { "get": { "produces": ["application/json"], - "tags": ["Enterprise"], - "summary": "Get OAuth2 application.", - "operationId": "get-oauth2-application", + "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": "App ID", - "name": "app", + "description": "Organization name or ID", + "name": "organization", "in": "path", "required": true } @@ -5040,7 +5393,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.OAuth2ProviderApp" + "$ref": "#/definitions/codersdk.OrganizationChatModelsResponse" } } }, @@ -5050,35 +5403,35 @@ } ] }, - "put": { + "post": { "consumes": ["application/json"], "produces": ["application/json"], - "tags": ["Enterprise"], - "summary": "Update OAuth2 application.", - "operationId": "update-oauth2-application", + "tags": ["Chats"], + "summary": "Create an AI model in an organization", + "operationId": "create-an-ai-model-in-an-organization", "parameters": [ { "type": "string", - "description": "App ID", - "name": "app", + "description": "Organization name or ID", + "name": "organization", "in": "path", "required": true }, { - "description": "Update an OAuth2 application.", + "description": "Model", "name": "request", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/codersdk.PutOAuth2ProviderAppRequest" + "$ref": "#/definitions/codersdk.CreateChatModelRequest" } } ], "responses": { - "200": { - "description": "OK", + "201": { + "description": "Created", "schema": { - "$ref": "#/definitions/codersdk.OAuth2ProviderApp" + "$ref": "#/definitions/codersdk.ChatModel" } } }, @@ -5087,23 +5440,36 @@ "CoderSessionToken": [] } ] - }, - "delete": { - "tags": ["Enterprise"], - "summary": "Delete OAuth2 application.", - "operationId": "delete-oauth2-application", + } + }, + "/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": "App ID", - "name": "app", + "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" + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.ChatModel" + } } }, "security": [ @@ -5111,32 +5477,30 @@ "CoderSessionToken": [] } ] - } - }, - "/api/v2/oauth2-provider/apps/{app}/secrets": { - "get": { - "produces": ["application/json"], - "tags": ["Enterprise"], - "summary": "Get OAuth2 application secrets.", - "operationId": "get-oauth2-application-secrets", + }, + "delete": { + "tags": ["Chats"], + "summary": "Delete an AI model", + "operationId": "delete-an-ai-model", "parameters": [ { "type": "string", - "description": "App ID", - "name": "app", + "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": { - "type": "array", - "items": { - "$ref": "#/definitions/codersdk.OAuth2ProviderAppSecret" - } - } + "204": { + "description": "No Content" } }, "security": [ @@ -5145,28 +5509,42 @@ } ] }, - "post": { + "patch": { + "consumes": ["application/json"], "produces": ["application/json"], - "tags": ["Enterprise"], - "summary": "Create OAuth2 application secret.", - "operationId": "create-oauth2-application-secret", + "tags": ["Chats"], + "summary": "Update an AI model", + "operationId": "update-an-ai-model", "parameters": [ { "type": "string", - "description": "App ID", - "name": "app", + "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": { - "type": "array", - "items": { - "$ref": "#/definitions/codersdk.OAuth2ProviderAppSecretFull" - } + "$ref": "#/definitions/codersdk.ChatModel" } } }, @@ -5177,50 +5555,34 @@ ] } }, - "/api/v2/oauth2-provider/apps/{app}/secrets/{secretID}": { - "delete": { - "tags": ["Enterprise"], - "summary": "Delete OAuth2 application secret.", - "operationId": "delete-oauth2-application-secret", + "/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": "App ID", - "name": "app", + "description": "Organization name or ID", + "name": "organization", "in": "path", "required": true }, { "type": "string", - "description": "Secret ID", - "name": "secretID", + "format": "uuid", + "description": "Model ID", + "name": "model", "in": "path", "required": true } ], - "responses": { - "204": { - "description": "No Content" - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ] - } - }, - "/api/v2/oauth2-provider/settings": { - "get": { - "produces": ["application/json"], - "tags": ["Enterprise"], - "summary": "Get OAuth2 provider settings.", - "operationId": "get-oauth2-provider-settings", "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.OAuth2ProviderSettings" + "$ref": "#/definitions/codersdk.ChatModelACL" } } }, @@ -5230,29 +5592,40 @@ } ] }, - "put": { + "patch": { "consumes": ["application/json"], - "produces": ["application/json"], - "tags": ["Enterprise"], - "summary": "Update OAuth2 provider settings.", - "operationId": "update-oauth2-provider-settings", + "tags": ["Chats"], + "summary": "Update an AI model ACL", + "operationId": "update-an-ai-model-acl", "parameters": [ { - "description": "OAuth2 provider settings request", + "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.OAuth2ProviderSettings" + "$ref": "#/definitions/codersdk.UpdateChatModelACLRequest" } } ], "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.OAuth2ProviderSettings" - } + "204": { + "description": "No Content" } }, "security": [ @@ -5262,19 +5635,29 @@ ] } }, - "/api/v2/organizations": { + "/api/v2/organizations/{organization}/groups": { "get": { "produces": ["application/json"], - "tags": ["Organizations"], - "summary": "Get organizations", - "operationId": "get-organizations", + "tags": ["Enterprise"], + "summary": "Get groups by organization", + "operationId": "get-groups-by-organization", + "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.Organization" + "$ref": "#/definitions/codersdk.Group" } } } @@ -5288,25 +5671,32 @@ "post": { "consumes": ["application/json"], "produces": ["application/json"], - "tags": ["Organizations"], - "summary": "Create organization", - "operationId": "create-organization", + "tags": ["Enterprise"], + "summary": "Create group for organization", + "operationId": "create-group-for-organization", "parameters": [ { - "description": "Create organization request", + "description": "Create group request", "name": "request", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/codersdk.CreateOrganizationRequest" + "$ref": "#/definitions/codersdk.CreateGroupRequest" } + }, + { + "type": "string", + "description": "Organization ID", + "name": "organization", + "in": "path", + "required": true } ], "responses": { "201": { "description": "Created", "schema": { - "$ref": "#/definitions/codersdk.Organization" + "$ref": "#/definitions/codersdk.Group" } } }, @@ -5317,12 +5707,13 @@ ] } }, - "/api/v2/organizations/{organization}": { + "/api/v2/organizations/{organization}/groups/ai/spend": { "get": { + "description": "Returns AI spend limits and aggregate spend for the requested groups.\nA maximum of 100 group IDs may be requested per call, and requests with more are rejected, so callers are expected to batch across multiple requests.\nUnknown or unreadable group IDs are silently omitted.", "produces": ["application/json"], - "tags": ["Organizations"], - "summary": "Get organization by ID", - "operationId": "get-organization-by-id", + "tags": ["Enterprise"], + "summary": "Get organization groups AI spend", + "operationId": "get-organization-groups-ai-spend", "parameters": [ { "type": "string", @@ -5331,13 +5722,20 @@ "name": "organization", "in": "path", "required": true + }, + { + "type": "string", + "description": "Comma-separated list of group IDs (maximum 100)", + "name": "group_ids", + "in": "query", + "required": true } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.Organization" + "$ref": "#/definitions/codersdk.OrganizationGroupsAISpend" } } }, @@ -5346,26 +5744,36 @@ "CoderSessionToken": [] } ] - }, - "delete": { + } + }, + "/api/v2/organizations/{organization}/groups/{groupName}": { + "get": { "produces": ["application/json"], - "tags": ["Organizations"], - "summary": "Delete organization", - "operationId": "delete-organization", + "tags": ["Enterprise"], + "summary": "Get group by organization and group name", + "operationId": "get-group-by-organization-and-group-name", "parameters": [ { "type": "string", - "description": "Organization ID or name", + "format": "uuid", + "description": "Organization ID", "name": "organization", "in": "path", "required": true + }, + { + "type": "string", + "description": "Group name", + "name": "groupName", + "in": "path", + "required": true } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.Response" + "$ref": "#/definitions/codersdk.Group" } } }, @@ -5374,36 +5782,61 @@ "CoderSessionToken": [] } ] - }, - "patch": { - "consumes": ["application/json"], + } + }, + "/api/v2/organizations/{organization}/groups/{groupName}/members": { + "get": { "produces": ["application/json"], - "tags": ["Organizations"], - "summary": "Update organization", - "operationId": "update-organization", + "tags": ["Enterprise"], + "summary": "Get group members by organization and group name", + "operationId": "get-group-members-by-organization-and-group-name", "parameters": [ { "type": "string", - "description": "Organization ID or name", + "format": "uuid", + "description": "Organization ID", "name": "organization", "in": "path", "required": true }, { - "description": "Patch organization request", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.UpdateOrganizationRequest" - } + "type": "string", + "description": "Group name", + "name": "groupName", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Member search query", + "name": "q", + "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": { - "$ref": "#/definitions/codersdk.Organization" + "$ref": "#/definitions/codersdk.GroupMembersResponse" } } }, @@ -5414,13 +5847,13 @@ ] } }, - "/api/v2/organizations/{organization}/ai/spend/export": { + "/api/v2/organizations/{organization}/groups/{groupName}/members/ai/spend": { "get": { - "description": "Returns per-user, per-group, per-model, per-provider aggregated AI spend for the organization as CSV, built from raw AI Gateway token usage.\nThe optional period_start and period_end query parameters bound the period and are interpreted as UTC. They must be provided together and span at most 31 days. When both are omitted, the current UTC monthly period is used.\nAn explicit period_start must fall within the configured AI Gateway data retention window, since older token usage is purged. The default period is narrowed to that window instead, and every row echoes the applied bounds.\nRequires organization-level administrator permissions.", - "produces": ["text/csv"], + "description": "Returns aggregate AI spend attributed to the group per requested user.\nA maximum of 100 user IDs may be requested per call, and requests with more are rejected, so callers are expected to batch across multiple requests.\nUser IDs that are not members of the group, or that the caller has no read access to, are silently omitted.", + "produces": ["application/json"], "tags": ["Enterprise"], - "summary": "Export organization AI spend as CSV", - "operationId": "export-organization-ai-spend-as-csv", + "summary": "Get group members AI spend by organization", + "operationId": "get-group-members-ai-spend-by-organization", "parameters": [ { "type": "string", @@ -5432,22 +5865,25 @@ }, { "type": "string", - "format": "date-time", - "description": "Inclusive lower bound (RFC3339)", - "name": "period_start", - "in": "query" + "description": "Group name", + "name": "groupName", + "in": "path", + "required": true }, { "type": "string", - "format": "date-time", - "description": "Exclusive upper bound (RFC3339)", - "name": "period_end", - "in": "query" + "description": "Comma-separated list of user IDs (maximum 100)", + "name": "user_ids", + "in": "query", + "required": true } ], "responses": { "200": { - "description": "OK" + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.GroupMembersAISpend" + } } }, "security": [ @@ -5457,12 +5893,12 @@ ] } }, - "/api/v2/organizations/{organization}/groups": { + "/api/v2/organizations/{organization}/mcp-servers": { "get": { "produces": ["application/json"], - "tags": ["Enterprise"], - "summary": "Get groups by organization", - "operationId": "get-groups-by-organization", + "tags": ["MCP"], + "summary": "List MCP server configs", + "operationId": "list-mcp-server-configs", "parameters": [ { "type": "string", @@ -5479,7 +5915,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/definitions/codersdk.Group" + "$ref": "#/definitions/codersdk.MCPServerConfig" } } } @@ -5493,32 +5929,72 @@ "post": { "consumes": ["application/json"], "produces": ["application/json"], - "tags": ["Enterprise"], - "summary": "Create group for organization", - "operationId": "create-group-for-organization", + "tags": ["MCP"], + "summary": "Create MCP server config", + "operationId": "create-mcp-server-config", "parameters": [ { - "description": "Create group request", + "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.CreateGroupRequest" + "$ref": "#/definitions/codersdk.CreateMCPServerConfigRequest" } - }, + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/codersdk.MCPServerConfig" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/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", + "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": { - "201": { - "description": "Created", + "200": { + "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.Group" + "$ref": "#/definitions/codersdk.MCPServerConfig" } } }, @@ -5527,15 +6003,46 @@ "CoderSessionToken": [] } ] - } - }, - "/api/v2/organizations/{organization}/groups/ai/spend": { - "get": { - "description": "Returns AI spend limits and aggregate spend for the requested groups.\nA maximum of 100 group IDs may be requested per call, and requests with more are rejected, so callers are expected to batch across multiple requests.\nUnknown or unreadable group IDs are silently omitted.", + }, + "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": [] + } + ] + }, + "patch": { + "consumes": ["application/json"], "produces": ["application/json"], - "tags": ["Enterprise"], - "summary": "Get organization groups AI spend", - "operationId": "get-organization-groups-ai-spend", + "tags": ["MCP"], + "summary": "Update MCP server config", + "operationId": "update-mcp-server-config", "parameters": [ { "type": "string", @@ -5547,17 +6054,27 @@ }, { "type": "string", - "description": "Comma-separated list of group IDs (maximum 100)", - "name": "group_ids", - "in": "query", + "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.OrganizationGroupsAISpend" + "$ref": "#/definitions/codersdk.MCPServerConfig" } } }, @@ -5568,12 +6085,12 @@ ] } }, - "/api/v2/organizations/{organization}/groups/{groupName}": { + "/api/v2/organizations/{organization}/mcp-servers/{mcpserverconfig}/acl": { "get": { "produces": ["application/json"], - "tags": ["Enterprise"], - "summary": "Get group by organization and group name", - "operationId": "get-group-by-organization-and-group-name", + "tags": ["MCP"], + "summary": "Get MCP server config ACL", + "operationId": "get-mcp-server-config-acl", "parameters": [ { "type": "string", @@ -5585,8 +6102,9 @@ }, { "type": "string", - "description": "Group name", - "name": "groupName", + "format": "uuid", + "description": "MCP server config ID", + "name": "mcpserverconfig", "in": "path", "required": true } @@ -5595,7 +6113,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.Group" + "$ref": "#/definitions/codersdk.MCPServerConfigACL" } } }, @@ -5604,14 +6122,12 @@ "CoderSessionToken": [] } ] - } - }, - "/api/v2/organizations/{organization}/groups/{groupName}/members": { - "get": { - "produces": ["application/json"], - "tags": ["Enterprise"], - "summary": "Get group members by organization and group name", - "operationId": "get-group-members-by-organization-and-group-name", + }, + "patch": { + "consumes": ["application/json"], + "tags": ["MCP"], + "summary": "Update MCP server config ACL", + "operationId": "update-mcp-server-config-acl", "parameters": [ { "type": "string", @@ -5623,43 +6139,25 @@ }, { "type": "string", - "description": "Group name", - "name": "groupName", + "format": "uuid", + "description": "MCP server config ID", + "name": "mcpserverconfig", "in": "path", "required": true }, { - "type": "string", - "description": "Member search query", - "name": "q", - "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" + "description": "Update MCP server config ACL request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateMCPServerConfigACLRequest" + } } ], "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.GroupMembersResponse" - } + "204": { + "description": "No Content" } }, "security": [ @@ -5669,13 +6167,11 @@ ] } }, - "/api/v2/organizations/{organization}/groups/{groupName}/members/ai/spend": { + "/api/v2/organizations/{organization}/mcp-servers/{mcpserverconfig}/oauth2/connect": { "get": { - "description": "Returns aggregate AI spend attributed to the group per requested user.\nA maximum of 100 user IDs may be requested per call, and requests with more are rejected, so callers are expected to batch across multiple requests.\nUser IDs that are not members of the group, or that the caller has no read access to, are silently omitted.", - "produces": ["application/json"], - "tags": ["Enterprise"], - "summary": "Get group members AI spend by organization", - "operationId": "get-group-members-ai-spend-by-organization", + "tags": ["MCP"], + "summary": "Initiate MCP server OAuth2 connect", + "operationId": "initiate-mcp-server-oauth2-connect", "parameters": [ { "type": "string", @@ -5687,25 +6183,16 @@ }, { "type": "string", - "description": "Group name", - "name": "groupName", + "format": "uuid", + "description": "MCP server config ID", + "name": "mcpserverconfig", "in": "path", "required": true - }, - { - "type": "string", - "description": "Comma-separated list of user IDs (maximum 100)", - "name": "user_ids", - "in": "query", - "required": true } ], "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.GroupMembersAISpend" - } + "307": { + "description": "Temporary Redirect" } }, "security": [ @@ -6011,6 +6498,94 @@ ] } }, + "/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": [] + } + ] + } + }, + "/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": [] + } + ] + } + }, "/api/v2/organizations/{organization}/members/{user}/roles": { "put": { "consumes": ["application/json"], @@ -9592,12 +10167,100 @@ ] } }, - "/api/v2/users/{user}": { - "get": { + "/api/v2/users/{user}": { + "get": { + "produces": ["application/json"], + "tags": ["Users"], + "summary": "Get user by name", + "operationId": "get-user-by-name", + "parameters": [ + { + "type": "string", + "description": "User ID, username, or me", + "name": "user", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.User" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + }, + "delete": { + "tags": ["Users"], + "summary": "Delete user", + "operationId": "delete-user", + "parameters": [ + { + "type": "string", + "description": "User ID, name, or me", + "name": "user", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ] + } + }, + "/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": ["Users"], - "summary": "Get user by name", - "operationId": "get-user-by-name", + "tags": ["Chats"], + "summary": "Update user AI provider key", + "operationId": "update-user-ai-provider-key", "parameters": [ { "type": "string", @@ -9605,13 +10268,29 @@ "name": "user", "in": "path", "required": true + }, + { + "type": "string", + "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.User" + "$ref": "#/definitions/codersdk.UserAIProviderKeyConfig" } } }, @@ -9622,21 +10301,28 @@ ] }, "delete": { - "tags": ["Users"], - "summary": "Delete user", - "operationId": "delete-user", + "tags": ["Chats"], + "summary": "Delete user AI provider key", + "operationId": "delete-user-ai-provider-key", "parameters": [ { "type": "string", - "description": "User ID, name, or me", + "description": "User ID, username, or me", "name": "user", "in": "path", "required": true + }, + { + "type": "string", + "description": "AI provider ID", + "name": "aiProvider", + "in": "path", + "required": true } ], "responses": { - "200": { - "description": "OK" + "204": { + "description": "No Content" } }, "security": [ @@ -14354,6 +15040,12 @@ "enum": ["prebuild_claimed"], "x-enum-varnames": ["ReinitializeReasonPrebuildClaimed"] }, + "coderd.chatsByWorkspaceResponse": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, "coderd.cspViolation": { "type": "object", "properties": { @@ -15139,6 +15831,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 +17240,14 @@ } } }, + "codersdk.ChatAutoArchiveDaysResponse": { + "type": "object", + "properties": { + "auto_archive_days": { + "type": "integer" + } + } + }, "codersdk.ChatBusyBehavior": { "type": "string", "enum": ["queue", "interrupt"], @@ -16676,6 +17403,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 +18623,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 +18874,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 +18970,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 +19933,14 @@ } } }, + "codersdk.CreateUserAIProviderKeyRequest": { + "type": "object", + "properties": { + "api_key": { + "type": "string" + } + } + }, "codersdk.CreateUserRequestWithOrgs": { "type": "object", "required": ["username"], @@ -24303,6 +25096,17 @@ } } }, + "codersdk.SubmitToolResultsRequest": { + "type": "object", + "properties": { + "results": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.ToolResult" + } + } + } + }, "codersdk.SupportConfig": { "type": "object", "properties": { @@ -25474,6 +26278,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 +26403,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 +26485,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 +26542,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 +26890,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 +27322,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 +27459,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 00000000000..dcc40a14dc7 --- /dev/null +++ b/coderd/chat_routes.go @@ -0,0 +1,166 @@ +package coderd + +import ( + "net/http" + "time" + + "github.com/go-chi/chi/v5" + + "github.com/coder/coder/v2/coderd/httpmw" + "github.com/coder/coder/v2/coderd/rbac/policy" + "github.com/coder/coder/v2/codersdk" +) + +func (api *API) registerChatFileDownloadRoute(r chi.Router) { + r.Group(func(r chi.Router) { + r.Use(httpmw.RateLimit(api.FilesRateLimit, time.Minute)) + r.Get("/chats/files/{file}/download", api.downloadChatFile) + }) +} + +func (api *API) registerDefaultOrganizationChatModelsRoute(r chi.Router) { + r.With( + 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(api.Database), + ).Get("/models", api.listDefaultOrganizationChatModels) +} + +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) + }) +} + +func (api *API) registerOrganizationMCPServerRoutes(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) + }) + }) +} + +func (api *API) registerOrganizationChatRoutes(r chi.Router) { + 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) + }) +} + +func (api *API) registerChatCollectionRoutes(r chi.Router) { + 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(api.FilesRateLimit, time.Minute)) + r.Post("/", api.postChatFile) + r.Post("/{file}/download-url", api.postChatFileDownloadURL) + r.Get("/{file}", api.chatFileByID) + }) +} + +func (api *API) registerChatConfigRoutes(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) +} + +func (api *API) registerChatRoutes(r chi.Router) { + 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) + }) +} + +func (api *API) registerChatStreamRoutes(r chi.Router) { + r.Get("/", api.streamChat) + r.Get("/parts", api.streamChatParts) + r.Get("/git", api.watchChatGit) +} + +func (api *API) registerMCPServerOAuth2Routes(r chi.Router) { + // 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) +} diff --git a/coderd/chat_routes_test.go b/coderd/chat_routes_test.go new file mode 100644 index 00000000000..9090f916758 --- /dev/null +++ b/coderd/chat_routes_test.go @@ -0,0 +1,185 @@ +package coderd_test + +import ( + "net/http" + "strings" + "testing" + + "github.com/go-chi/chi/v5" + "github.com/stretchr/testify/require" + + "github.com/coder/coder/v2/coderd/coderdtest" + "github.com/coder/coder/v2/testutil" +) + +func TestChatRoutesCompatibility(t *testing.T) { + t.Parallel() + + client, _, api := coderdtest.NewWithAPI(t, nil) + coderdtest.CreateFirstUser(t, client) + + promoted := []string{ + http.MethodGet + " /users/{user}/ai-provider-keys", + http.MethodPut + " /users/{user}/ai-provider-keys/{aiProvider}", + http.MethodDelete + " /users/{user}/ai-provider-keys/{aiProvider}", + http.MethodGet + " /chats/files/{file}/download", + http.MethodGet + " /organizations/{organization}/mcp-servers", + http.MethodPost + " /organizations/{organization}/mcp-servers", + http.MethodGet + " /organizations/{organization}/mcp-servers/{mcpserverconfig}", + http.MethodPatch + " /organizations/{organization}/mcp-servers/{mcpserverconfig}", + http.MethodDelete + " /organizations/{organization}/mcp-servers/{mcpserverconfig}", + http.MethodGet + " /organizations/{organization}/mcp-servers/{mcpserverconfig}/acl", + http.MethodPatch + " /organizations/{organization}/mcp-servers/{mcpserverconfig}/acl", + http.MethodGet + " /organizations/{organization}/mcp-servers/{mcpserverconfig}/oauth2/connect", + http.MethodGet + " /organizations/{organization}/chats/model-overrides", + http.MethodPut + " /organizations/{organization}/chats/model-overrides/{context}", + http.MethodGet + " /organizations/{organization}/members/{user}/chats/model-overrides", + http.MethodPut + " /organizations/{organization}/members/{user}/chats/model-overrides/{context}", + http.MethodGet + " /organizations/{organization}/chats/models", + http.MethodPost + " /organizations/{organization}/chats/models", + http.MethodGet + " /organizations/{organization}/chats/models/{model}", + http.MethodPatch + " /organizations/{organization}/chats/models/{model}", + http.MethodDelete + " /organizations/{organization}/chats/models/{model}", + http.MethodGet + " /organizations/{organization}/chats/models/{model}/acl", + http.MethodPatch + " /organizations/{organization}/chats/models/{model}/acl", + http.MethodGet + " /chats/models", + http.MethodGet + " /chats/by-workspace", + http.MethodGet + " /chats", + http.MethodPost + " /chats", + http.MethodGet + " /chats/watch", + http.MethodPost + " /chats/files", + http.MethodPost + " /chats/files/{file}/download-url", + http.MethodGet + " /chats/files/{file}", + http.MethodGet + " /chats/config/system-prompt", + http.MethodPut + " /chats/config/system-prompt", + http.MethodGet + " /chats/config/plan-mode-instructions", + http.MethodPut + " /chats/config/plan-mode-instructions", + http.MethodGet + " /chats/config/personal-model-overrides", + http.MethodPut + " /chats/config/personal-model-overrides", + http.MethodGet + " /chats/config/debug-logging", + http.MethodPut + " /chats/config/debug-logging", + http.MethodGet + " /chats/config/user-debug-logging", + http.MethodPut + " /chats/config/user-debug-logging", + http.MethodGet + " /chats/config/user-prompt", + http.MethodPut + " /chats/config/user-prompt", + http.MethodGet + " /chats/config/user-compaction-thresholds", + http.MethodPut + " /chats/config/user-compaction-thresholds/{modelConfig}", + http.MethodDelete + " /chats/config/user-compaction-thresholds/{modelConfig}", + http.MethodGet + " /chats/config/workspace-ttl", + http.MethodPut + " /chats/config/workspace-ttl", + http.MethodGet + " /chats/config/retention-days", + http.MethodPut + " /chats/config/retention-days", + http.MethodGet + " /chats/config/debug-retention-days", + http.MethodPut + " /chats/config/debug-retention-days", + http.MethodGet + " /chats/config/auto-archive-days", + http.MethodPut + " /chats/config/auto-archive-days", + http.MethodGet + " /chats/{chat}/acl", + http.MethodPatch + " /chats/{chat}/acl", + http.MethodGet + " /chats/{chat}", + http.MethodPatch + " /chats/{chat}", + http.MethodGet + " /chats/{chat}/cost", + http.MethodGet + " /chats/{chat}/messages", + http.MethodPost + " /chats/{chat}/messages", + http.MethodPatch + " /chats/{chat}/messages/{message}", + http.MethodGet + " /chats/{chat}/prompts", + http.MethodGet + " /chats/{chat}/stream", + http.MethodGet + " /chats/{chat}/stream/parts", + http.MethodGet + " /chats/{chat}/stream/git", + http.MethodPost + " /chats/{chat}/interrupt", + http.MethodPost + " /chats/{chat}/compact", + http.MethodPost + " /chats/{chat}/reconcile-invalid", + http.MethodPost + " /chats/{chat}/tool-results", + http.MethodPost + " /chats/{chat}/title/regenerate", + http.MethodPost + " /chats/{chat}/title/propose", + http.MethodGet + " /chats/{chat}/diff", + http.MethodPut + " /chats/{chat}/context", + http.MethodDelete + " /chats/{chat}/queue/{queuedMessage}", + http.MethodPost + " /chats/{chat}/queue/{queuedMessage}/promote", + http.MethodGet + " /mcp/servers/{mcpServer}/oauth2/callback", + http.MethodDelete + " /mcp/servers/{mcpServer}/oauth2/disconnect", + } + + for name, router := range map[string]chi.Router{ + "experimental": api.ExperimentalHandler, + "v2": api.APIHandler, + } { + routes := walkRoutes(t, router) + for _, route := range promoted { + require.Contains(t, routes, route, "%s route tree", name) + } + } + + v2Routes := walkRoutes(t, api.APIHandler) + for _, excluded := range []string{ + http.MethodGet + " /chats/providers", + http.MethodPost + " /chats/providers", + http.MethodPatch + " /chats/providers/{providerConfig}", + http.MethodDelete + " /chats/providers/{providerConfig}", + http.MethodGet + " /chats/user-provider-configs", + http.MethodPut + " /chats/user-provider-configs/{providerConfig}", + http.MethodDelete + " /chats/user-provider-configs/{providerConfig}", + http.MethodGet + " /chats/{chat}/debug/runs", + http.MethodGet + " /chats/{chat}/debug/runs/{debugRun}", + http.MethodGet + " /chats/config/computer-use-provider", + http.MethodPut + " /chats/config/computer-use-provider", + http.MethodGet + " /chats/config/advisor", + http.MethodPut + " /chats/config/advisor", + http.MethodGet + " /chats/{chat}/stream/desktop", + http.MethodGet + " /chats/model-configs", + http.MethodPost + " /chats/model-configs", + } { + require.NotContains(t, v2Routes, excluded) + } + for route := range v2Routes { + require.NotContains(t, route, " /mcp/http") + } + + ctx := testutil.Context(t, testutil.WaitLong) + for _, excluded := range []struct { + method string + path string + }{ + {http.MethodGet, "/api/v2/chats/config/computer-use-provider"}, + {http.MethodPut, "/api/v2/chats/config/computer-use-provider"}, + {http.MethodGet, "/api/v2/chats/config/advisor"}, + {http.MethodPut, "/api/v2/chats/config/advisor"}, + {http.MethodPost, "/api/v2/mcp/http/server"}, + {http.MethodGet, "/api/v2/chats/00000000-0000-0000-0000-000000000000/debug/runs"}, + {http.MethodGet, "/api/v2/chats/00000000-0000-0000-0000-000000000000/stream/desktop"}, + // Experimental-only segments that would otherwise fall into the + // {chat} wildcard and 400 on UUID parsing. + {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/providers/00000000-0000-0000-0000-000000000000"}, + {http.MethodGet, "/api/v2/chats/user-provider-configs"}, + } { + res, err := client.Request(ctx, excluded.method, excluded.path, nil) + require.NoError(t, err) + _ = res.Body.Close() + require.Equal(t, http.StatusNotFound, res.StatusCode, "%s %s", excluded.method, excluded.path) + } + + for _, path := range []string{ + "/api/v2/chats", + "/api/v2/chats/config/system-prompt", + } { + res, err := client.Request(ctx, http.MethodGet, path, nil) + require.NoError(t, err) + _ = res.Body.Close() + require.Equal(t, http.StatusOK, res.StatusCode, path) + } +} + +func walkRoutes(t *testing.T, router chi.Router) map[string]struct{} { + t.Helper() + + routes := make(map[string]struct{}) + err := chi.Walk(router, func(method, route string, _ http.Handler, _ ...func(http.Handler) http.Handler) error { + route = strings.TrimSuffix(route, "/") + routes[method+" "+route] = struct{}{} + return nil + }) + require.NoError(t, err) + return routes +} diff --git a/coderd/coderd.go b/coderd/coderd.go index 7199a8977d6..c497b6fd840 100644 --- a/coderd/coderd.go +++ b/coderd/coderd.go @@ -1369,42 +1369,26 @@ 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) }) + api.registerChatFileDownloadRoute(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) - }) + api.registerOrganizationMCPServerRoutes(r) + api.registerOrganizationChatRoutes(r) + r.Route("/members/{user}", func(r chi.Router) { + r.Use(httpmw.ExtractOrganizationMemberParam(options.Database)) + api.registerOrganizationMemberChatRoutes(r) }) }) }) @@ -1425,100 +1409,22 @@ func New(options *Options) *API { 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.Use(apiKeyMiddleware) + api.registerDefaultOrganizationChatModelsRoute(r) + api.registerChatCollectionRoutes(r) 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) + api.registerChatConfigRoutes(r) 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) { @@ -1538,33 +1444,10 @@ func New(options *Options) *API { }) 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) + api.registerChatRoutes(r) r.Route("/stream", func(r chi.Router) { - r.Get("/", api.streamChat) - r.Get("/parts", api.streamChatParts) + api.registerChatStreamRoutes(r) 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) @@ -1574,15 +1457,9 @@ func New(options *Options) *API { }) 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) + // 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 +1510,36 @@ func New(options *Options) *API { r.Get("/available", handleExperimentsAvailable) r.Get("/", api.handleExperimentsGet) }) + api.registerChatFileDownloadRoute(r) + r.Route("/chats", func(r chi.Router) { + r.Use(apiKeyMiddleware) + api.registerDefaultOrganizationChatModelsRoute(r) + api.registerChatCollectionRoutes(r) + r.Route("/config", api.registerChatConfigRoutes) + // 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{"/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.Route("/{chat}", func(r chi.Router) { + r.Use(httpmw.ExtractChatParam(options.Database)) + api.registerChatRoutes(r) + r.Route("/stream", api.registerChatStreamRoutes) + }) + }) + r.Route("/mcp", func(r chi.Router) { + r.Use(apiKeyMiddleware) + api.registerMCPServerOAuth2Routes(r) + }) + r.Get("/updatecheck", api.updateCheck) r.Route("/audit", func(r chi.Router) { r.Use( @@ -1695,6 +1602,8 @@ func New(options *Options) *API { r.Use( httpmw.ExtractOrganizationParam(options.Database), ) + api.registerOrganizationMCPServerRoutes(r) + api.registerOrganizationChatRoutes(r) r.Get("/", api.organization) r.Post("/templateversions", api.postTemplateVersionsByOrganization) r.Route("/templates", func(r chi.Router) { @@ -1732,6 +1641,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 +1794,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) diff --git a/coderd/coderdtest/swaggerparser.go b/coderd/coderdtest/swaggerparser.go index 2fe62ecd1ad..6b4818bc613 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" { @@ -381,6 +382,10 @@ func assertSecurityDefined(t *testing.T, comment SwaggerComment) { } func assertAccept(t *testing.T, comment SwaggerComment) { + if comment.method == "post" && comment.router == "/api/v2/chats/files" { + return + } + var hasRequestBody bool for _, c := range comment.parameters { if c.name == "request" && c.kind == "body" || @@ -407,6 +412,11 @@ func assertAccept(t *testing.T, comment SwaggerComment) { var allowedProduceTypes = []string{"json", "text/event-stream", "text/html", "text/plain"} func assertProduce(t *testing.T, comment SwaggerComment) { + if comment.method == "get" && (comment.router == "/api/v2/chats/files/{file}" || + comment.router == "/api/v2/chats/files/{file}/download") { + return + } + var hasResponseModel bool for _, r := range comment.successes { if r.model != "" { diff --git a/coderd/exp_chats.go b/coderd/exp_chats.go index a7a093e3987..6d104f537fd 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,8 +387,6 @@ 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 @@ -403,8 +395,7 @@ func (api *API) chatsByWorkspace(rw http.ResponseWriter, r *http.Request) { // @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)." // @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 +1177,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 +1186,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 +1448,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 +1455,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 +1552,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 +1562,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 +1654,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 +1661,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 +1722,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 +1836,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 +1843,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 +2086,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 +2153,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 +2209,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 +2537,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 +2546,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 +2739,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 +2749,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 +2895,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" +// @Param queuedMessage path string 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 +2955,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" +// @Param queuedMessage path string 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,8 +3081,6 @@ 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 @@ -3108,8 +3088,7 @@ func (api *API) markChatAsRead(ctx context.Context, chatID uuid.UUID) { // @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. +// @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 +3219,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 +3226,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 +3267,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 +3274,7 @@ 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] -// @x-apidocgen {"skip": true} -// @Description Experimental: this endpoint is subject to change. +// @Router /api/v2/chats/{chat}/compact [post] // @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 +3336,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 +3343,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 +3385,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 +3392,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 +3444,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 +3451,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 +4059,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 +4292,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 +4327,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 +4453,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 +4483,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,10 +4605,7 @@ 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] -// @x-apidocgen {"skip": true} -// -// EXPERIMENTAL: this endpoint is experimental and is subject to change. +// @Router /api/v2/organizations/{organization}/chats/model-overrides [get] // //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) { @@ -4653,10 +4642,7 @@ 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] -// @x-apidocgen {"skip": true} -// -// EXPERIMENTAL: this endpoint is experimental and is subject to change. +// @Router /api/v2/organizations/{organization}/chats/model-overrides/{context} [put] func (api *API) putOrganizationChatModelOverride(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() organization := httpmw.OrganizationParam(r) @@ -4776,7 +4762,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 +4908,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,10 +4953,7 @@ 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] -// @x-apidocgen {"skip": true} -// -// EXPERIMENTAL: this endpoint is experimental and is subject to change. +// @Router /api/v2/organizations/{organization}/members/{user}/chats/model-overrides [get] // //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) { @@ -5035,10 +5031,7 @@ 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] -// @x-apidocgen {"skip": true} -// -// EXPERIMENTAL: this endpoint is experimental and is subject to change. +// @Router /api/v2/organizations/{organization}/members/{user}/chats/model-overrides/{context} [put] func (api *API) putUserChatPersonalModelOverride(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() apiKey := httpmw.APIKey(r) @@ -5235,7 +5228,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 +5258,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 +5295,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 +5342,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 +5478,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 +5516,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,8 +5589,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] -// @x-apidocgen {"skip": true} +// @Router /api/v2/chats/config/retention-days [get] // //nolint:revive // get-return: revive assumes get* must be a getter, but this is an HTTP handler. func (api *API) getChatRetentionDays(rw http.ResponseWriter, r *http.Request) { @@ -5587,8 +5618,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] -// @x-apidocgen {"skip": true} +// @Router /api/v2/chats/config/retention-days [put] func (api *API) putChatRetentionDays(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() aReq, commitAudit := api.initChatOperationalSettingsAudit(rw, r) @@ -5627,6 +5657,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 +5687,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 +5735,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 +5765,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 +5809,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 +5842,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 +5893,12 @@ func (api *API) putUserChatCustomPrompt(rw http.ResponseWriter, r *http.Request) } // @Summary Get user chat compaction thresholds -// @x-apidocgen {"skip": true} -// EXPERIMENTAL: this endpoint is experimental and is subject to change. +// @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] // //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 +5957,16 @@ 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 -// @x-apidocgen {"skip": true} -// EXPERIMENTAL: this endpoint is experimental and is subject to change. +// @Summary Update user chat compaction threshold +// @ID update-user-chat-compaction-threshold +// @Security CoderSessionToken +// @Tags Chats +// @Param modelConfig path string true "Model config ID" +// @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] func (api *API) putUserChatCompactionThreshold(rw http.ResponseWriter, r *http.Request) { var ( ctx = r.Context() @@ -5948,9 +6037,13 @@ func (api *API) putUserChatCompactionThreshold(rw http.ResponseWriter, r *http.R }) } -// @Summary Delete user chat compaction threshold for a model config -// @x-apidocgen {"skip": true} -// EXPERIMENTAL: this endpoint is experimental and is subject to change. +// @Summary Delete user chat compaction threshold +// @ID delete-user-chat-compaction-threshold +// @Security CoderSessionToken +// @Tags Chats +// @Param modelConfig path string true "Model config ID" +// @Success 204 +// @Router /api/v2/chats/config/user-compaction-thresholds/{modelConfig} [delete] func (api *API) deleteUserChatCompactionThreshold(rw http.ResponseWriter, r *http.Request) { var ( ctx = r.Context() @@ -5976,8 +6069,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 @@ -5987,8 +6078,7 @@ func (api *API) deleteUserChatCompactionThreshold(rw http.ResponseWriter, r *htt // @Param organization query string true "Organization ID" format(uuid) // @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 +6228,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 +6235,7 @@ 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] -// @x-apidocgen {"skip": true} -// @Description Experimental: this endpoint is subject to change. +// @Router /api/v2/chats/files/{file}/download-url [post] func (api *API) postChatFileDownloadURL(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() fileID, err := uuid.Parse(chi.URLParam(r, "file")) @@ -6194,6 +6280,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 +6294,13 @@ 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] -// @x-apidocgen {"skip": true} -// @Description Experimental: this endpoint is subject to change. +// @Router /api/v2/chats/files/{file}/download [get] func (api *API) downloadChatFile(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() fileID, err := uuid.Parse(chi.URLParam(r, "file")) @@ -6259,17 +6341,13 @@ 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 // @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) // @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 +6604,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 +6674,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" +// @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 +6761,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" +// @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) @@ -6799,15 +6904,25 @@ func (api *API) listDefaultOrganizationChatModelConfigs(rw http.ResponseWriter, httpapi.Write(ctx, rw, http.StatusOK, resp) } +// @Summary List AI models and provider descriptors in the default organization +// @ID list-ai-models-and-provider-descriptors-in-the-default-organization +// @Security CoderSessionToken +// @Tags Chats +// @Produce json +// @Success 200 {object} codersdk.OrganizationChatModelsResponse +// @Router /api/v2/chats/models [get] +func (api *API) listDefaultOrganizationChatModels(rw http.ResponseWriter, r *http.Request) { + api.listChatModelConfigsByOrganization(rw, r) +} + // @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] -// @x-apidocgen {"skip": true} +// @Router /api/v2/organizations/{organization}/chats/models [get] func (api *API) listChatModelConfigsByOrganization(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() organization := httpmw.OrganizationParam(r) @@ -6893,7 +7008,7 @@ func chatModelConfigReadScope(scopes database.APIKeyScopes) bool { // them directly, so the fetch runs under a narrow AsChatd context scoped to // exactly these two reads and the result is projected to the fixed redacted // fields (no key material, base URLs, or headers). Disclosure matches what -// /api/experimental/chats/models already shows any authenticated caller. +// /api/v2/chats/models already shows any authenticated caller. func (api *API) chatModelProviderDescriptors( ctx context.Context, userID uuid.UUID, @@ -6957,16 +7072,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" // @Success 200 {object} codersdk.ChatModel -// @Router /api/experimental/organizations/{organization}/chats/models/{model} [get] -// @x-apidocgen {"skip": true} +// @Router /api/v2/organizations/{organization}/chats/models/{model} [get] // //nolint:revive // get-return: revive assumes get* must be a getter, but this is an HTTP handler. func (api *API) getChatModelConfig(rw http.ResponseWriter, r *http.Request) { @@ -7061,7 +7176,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,8 +7184,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] -// @x-apidocgen {"skip": true} +// @Router /api/v2/organizations/{organization}/chats/models [post] func (api *API) createChatModelConfig(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() apiKey := httpmw.APIKey(r) @@ -7278,7 +7392,7 @@ 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 @@ -7287,8 +7401,7 @@ func (api *API) createChatModelConfig(rw http.ResponseWriter, r *http.Request) { // @Param model path string true "Model ID" // @Param request body codersdk.UpdateChatModelRequest true "Model updates" // @Success 200 {object} codersdk.ChatModel -// @Router /api/experimental/organizations/{organization}/chats/models/{model} [patch] -// @x-apidocgen {"skip": true} +// @Router /api/v2/organizations/{organization}/chats/models/{model} [patch] func (api *API) updateChatModelConfig(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() apiKey := httpmw.APIKey(r) @@ -7534,14 +7647,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" // @Success 204 -// @Router /api/experimental/organizations/{organization}/chats/models/{model} [delete] -// @x-apidocgen {"skip": true} +// @Router /api/v2/organizations/{organization}/chats/models/{model} [delete] func (api *API) deleteChatModelConfig(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() existing := httpmw.ChatModelConfigParam(r) @@ -7922,7 +8034,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" +// @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,8 +8248,6 @@ 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 @@ -8137,9 +8255,7 @@ func (api *API) getChatDebugRun(rw http.ResponseWriter, r *http.Request) { // @Produce json // @Param chat path string true "Chat ID" format(uuid) // @Success 200 {object} codersdk.ChatStreamEvent -// @Router /api/experimental/chats/{chat}/stream/parts [get] -// @x-apidocgen {"skip": true} -// @Description Experimental: this endpoint is subject to change. +// @Router /api/v2/chats/{chat}/stream/parts [get] 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 889cd933745..ce761883bf6 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,7 @@ import ( // @Produce json // @Param chat path string true "Chat ID" format(uuid) // @Success 200 {object} codersdk.ChatACL -// @Router /api/experimental/chats/{chat}/acl [get] -// @x-apidocgen {"skip": true} -// @Description Experimental: this endpoint is subject to change. +// @Router /api/v2/chats/{chat}/acl [get] // //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 +77,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 +85,7 @@ 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] -// @x-apidocgen {"skip": true} -// @Description Experimental: this endpoint is subject to change. +// @Router /api/v2/chats/{chat}/acl [patch] 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 de1922d4179..91274b6c905 100644 --- a/coderd/exp_chats_model_acl.go +++ b/coderd/exp_chats_model_acl.go @@ -24,18 +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] -// @x-apidocgen {"skip": true} +// @Router /api/v2/organizations/{organization}/chats/models/{model}/acl [get] func (api *API) chatModelConfigACLHandler(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() config := httpmw.ChatModelConfigParam(r) @@ -55,10 +52,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,8 +61,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] -// @x-apidocgen {"skip": true} +// @Router /api/v2/organizations/{organization}/chats/models/{model}/acl [patch] func (api *API) updateChatModelConfigACL(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() apiKey := httpmw.APIKey(r) diff --git a/coderd/mcp.go b/coderd/mcp.go index 7ba73157c54..718560274ce 100644 --- a/coderd/mcp.go +++ b/coderd/mcp.go @@ -147,9 +147,7 @@ func shouldRefreshOIDCToken(link database.UserLink) (bool, time.Time) { // @Produce json // @Param organization path string true "Organization ID" format(uuid) // @Success 200 {array} codersdk.MCPServerConfig -// @Router /api/experimental/organizations/{organization}/mcp-servers [get] -// @x-apidocgen {"skip": true} -// EXPERIMENTAL: this endpoint is experimental and is subject to change. +// @Router /api/v2/organizations/{organization}/mcp-servers [get] // //nolint:revive // HTTP handler writes to ResponseWriter. func (api *API) listMCPServerConfigs(rw http.ResponseWriter, r *http.Request) { @@ -289,9 +287,7 @@ func (api *API) mcpServerConfigReadInKeyScope(r *http.Request, organizationID uu // @Param organization path string true "Organization ID" format(uuid) // @Param request body codersdk.CreateMCPServerConfigRequest true "Create MCP server config request" // @Success 201 {object} codersdk.MCPServerConfig -// @Router /api/experimental/organizations/{organization}/mcp-servers [post] -// @x-apidocgen {"skip": true} -// EXPERIMENTAL: this endpoint is experimental and is subject to change. +// @Router /api/v2/organizations/{organization}/mcp-servers [post] // //nolint:revive // HTTP handler writes to ResponseWriter. func (api *API) createMCPServerConfig(rw http.ResponseWriter, r *http.Request) { @@ -517,9 +513,7 @@ func (api *API) createMCPServerConfig(rw http.ResponseWriter, r *http.Request) { // @Param organization path string true "Organization ID" format(uuid) // @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] -// @x-apidocgen {"skip": true} -// EXPERIMENTAL: this endpoint is experimental and is subject to change. +// @Router /api/v2/organizations/{organization}/mcp-servers/{mcpserverconfig} [get] // //nolint:revive // HTTP handler writes to ResponseWriter. func (api *API) getMCPServerConfig(rw http.ResponseWriter, r *http.Request) { @@ -606,9 +600,7 @@ func (api *API) getMCPServerConfigForMutation(rw http.ResponseWriter, r *http.Re // @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] -// @x-apidocgen {"skip": true} -// EXPERIMENTAL: this endpoint is experimental and is subject to change. +// @Router /api/v2/organizations/{organization}/mcp-servers/{mcpserverconfig} [patch] // //nolint:revive // HTTP handler writes to ResponseWriter. func (api *API) updateMCPServerConfig(rw http.ResponseWriter, r *http.Request) { @@ -971,9 +963,7 @@ func (api *API) updateMCPServerConfig(rw http.ResponseWriter, r *http.Request) { // @Param organization path string true "Organization ID" format(uuid) // @Param mcpserverconfig path string true "MCP server config ID" format(uuid) // @Success 204 -// @Router /api/experimental/organizations/{organization}/mcp-servers/{mcpserverconfig} [delete] -// @x-apidocgen {"skip": true} -// EXPERIMENTAL: this endpoint is experimental and is subject to change. +// @Router /api/v2/organizations/{organization}/mcp-servers/{mcpserverconfig} [delete] func (api *API) deleteMCPServerConfig(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() auditor := api.Auditor.Load() @@ -1022,6 +1012,8 @@ 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 @@ -1029,10 +1021,7 @@ func (api *API) deleteMCPServerConfig(rw http.ResponseWriter, r *http.Request) { // @Param organization path string true "Organization ID" format(uuid) // @Param mcpserverconfig path string true "MCP server config ID" format(uuid) // @Success 307 -// @Router /api/experimental/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. +// @Router /api/v2/organizations/{organization}/mcp-servers/{mcpserverconfig}/oauth2/connect [get] // //nolint:revive // HTTP handler writes to ResponseWriter. func (api *API) mcpServerOAuth2Connect(rw http.ResponseWriter, r *http.Request) { @@ -1111,21 +1100,19 @@ 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." // @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. +// @Router /api/v2/mcp/servers/{mcpServer}/oauth2/callback [get] // //nolint:revive // HTTP handler writes to ResponseWriter. func (api *API) mcpServerOAuth2Callback(rw http.ResponseWriter, r *http.Request) { @@ -1326,6 +1313,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 +1323,7 @@ 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] -// @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. +// @Router /api/v2/mcp/servers/{mcpServer}/oauth2/disconnect [delete] func (api *API) mcpServerOAuth2Disconnect(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() apiKey := httpmw.APIKey(r) @@ -1569,6 +1555,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 0227c15cb13..1d7ef27649f 100644 --- a/coderd/mcp_acl.go +++ b/coderd/mcp_acl.go @@ -21,8 +21,6 @@ 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 @@ -31,8 +29,7 @@ import ( // @Param organization path string true "Organization ID" format(uuid) // @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] -// @x-apidocgen {"skip": true} +// @Router /api/v2/organizations/{organization}/mcp-servers/{mcpserverconfig}/acl [get] func (api *API) mcpServerConfigACL(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() config := httpmw.MCPServerConfigParam(r) @@ -58,8 +55,6 @@ 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 @@ -69,8 +64,7 @@ func (api *API) mcpServerConfigACL(rw http.ResponseWriter, r *http.Request) { // @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] -// @x-apidocgen {"skip": true} +// @Router /api/v2/organizations/{organization}/mcp-servers/{mcpserverconfig}/acl [patch] func (api *API) patchMCPServerConfigACL(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() config := httpmw.MCPServerConfigParam(r) diff --git a/codersdk/chats.go b/codersdk/chats.go index b1c05172b72..bb14b1462ac 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/reference/api/chats.md b/docs/reference/api/chats.md index 4c780792494..af6786dd480 100644 --- a/docs/reference/api/chats.md +++ b/docs/reference/api/chats.md @@ -10,27 +10,54 @@ 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. -## List chats +## Connect to chat workspace desktop via WebSockets ### Code samples ```sh # Example request using curl -curl -X GET http://coder-server:8080/api/experimental/chats \ - -H 'Accept: application/json' \ +curl -X GET http://coder-server:8080/api/experimental/chats/{chat}/stream/desktop \ -H 'Coder-Session-Token: API_KEY' ``` -`GET /api/experimental/chats` +`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 | -|---------|-------|--------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `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 | +|--------|------|--------------|----------|-------------| +| `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/v2/chats \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`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; quote multi-word values; cannot be combined with title, pr_title, or pr; a value that tokenizes to no searchable words 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). | ### Example responses @@ -254,15 +281,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,88 +553,122 @@ Experimental: this endpoint is subject to change. To perform this operation, you must be authenticated. [Learn more](authentication.md). -## Upload chat file +## List chats by workspace ### 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 GET http://coder-server:8080/api/v2/chats/by-workspace \ -H 'Accept: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`POST /api/experimental/chats/files` - -Experimental: this endpoint is subject to change. +`GET /api/v2/chats/by-workspace` ### Parameters -| Name | In | Type | Required | Description | -|----------------|-------|--------------|----------|-----------------| -| `organization` | query | string(uuid) | true | Organization ID | +| Name | In | Type | Required | Description | +|-----------------|-------|--------|----------|-------------------------------| +| `workspace_ids` | query | string | false | Comma-separated workspace IDs | ### Example responses -> 201 Response +> 200 Response ```json { - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08" + "property1": "string", + "property2": "string" } ``` ### Responses -| Status | Meaning | Description | Schema | -|--------|-------------------------------------------------------------------------|-----------------------------|------------------------------------------------------------------------------| -| 201 | [Created](https://tools.ietf.org/html/rfc7231#section-6.3.2) | Created | [codersdk.UploadChatFileResponse](schemas.md#codersdkuploadchatfileresponse) | -| 413 | [Payload Too Large](https://tools.ietf.org/html/rfc7231#section-6.5.11) | Request body exceeds 10 MiB | [codersdk.Response](schemas.md#codersdkresponse) | +| 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 file +## Get chat auto archive days ### Code samples ```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/config/auto-archive-days \ + -H 'Accept: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`GET /api/experimental/chats/files/{file}` +`GET /api/v2/chats/config/auto-archive-days` -Experimental: this endpoint is subject to change. +### 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 | -|--------|------|--------------|----------|-------------| -| `file` | path | string(uuid) | true | File ID | +| Name | In | Type | Required | Description | +|--------|------|--------------------------------------------------------------------------------------------------|----------|--------------| +| `body` | body | [codersdk.UpdateChatAutoArchiveDaysRequest](schemas.md#codersdkupdatechatautoarchivedaysrequest) | true | Request body | ### Responses -| Status | Meaning | Description | Schema | -|--------|---------------------------------------------------------|-------------|--------| -| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | | +| 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). -## Watch chat events for a user via WebSockets +## Get chat debug logging setting ### Code samples ```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/config/debug-logging \ -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/config/debug-logging` ### Example responses @@ -617,147 +676,66 @@ Experimental: this endpoint is subject to change. ```json { - "chat": { - "agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e98978", - "archived": true, - "build_id": "bfb1f3fa-bf7b-43a5-9e0b-26cc050e44cb", - "children": [ - {} - ], - "client_type": "ui", - "context": { - "dirty": true, - "dirty_since": "2019-08-24T14:15:22Z", - "error": "string", - "resources": [ - { - "error": "string", - "kind": "instruction_file", - "size_bytes": 0, - "skill_description": "string", - "skill_name": "string", - "source": "string", - "status": "ok", - "tools": [ - { - "description": "string", - "name": "string" - } - ] - } - ] - }, - "created_at": "2019-08-24T14:15:22Z", - "diff_status": { - "additions": 0, - "approved": true, - "author_avatar_url": "string", - "author_login": "string", - "base_branch": "string", - "changed_files": 0, - "changes_requested": true, - "chat_id": "efc9fe20-a1e5-4a8c-9c48-f1b30c1e4f86", - "commits": 0, - "deletions": 0, - "head_branch": "string", - "pr_number": 0, - "pull_request_draft": true, - "pull_request_state": "string", - "pull_request_title": "string", - "refreshed_at": "2019-08-24T14:15:22Z", - "reviewer_count": 0, - "stale_at": "2019-08-24T14:15:22Z", - "url": "string" - }, - "files": [ - { - "created_at": "2019-08-24T14:15:22Z", - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "mime_type": "string", - "name": "string", - "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", - "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", - "size_bytes": 0 - } - ], - "has_unread": true, - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "labels": { - "property1": "string", - "property2": "string" - }, - "last_error": { - "detail": "string", - "kind": "generic", - "message": "string", - "provider": "string", - "retryable": true, - "status_code": 0 - }, - "last_model_config_id": "30ebb95f-c255-4759-9429-89aa4ec1554c", - "last_reasoning_effort": "string", - "last_turn_summary": "string", - "mcp_server_ids": [ - "497f6eca-6276-4993-bfeb-53cbbbba6f08" - ], - "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", - "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", - "owner_name": "string", - "owner_username": "string", - "parent_chat_id": "c3609ee6-3b11-4a93-b9ae-e4fabcc99359", - "pin_order": 0, - "plan_mode": "plan", - "queued_for_capacity": true, - "root_chat_id": "2898031c-fdce-4e3e-8c53-4481dd42fcd7", - "shared": true, - "status": "waiting", - "summary": "string", - "title": "string", - "updated_at": "2019-08-24T14:15:22Z", - "warnings": [ - "string" - ], - "workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9" - }, - "kind": "status_change", - "tool_calls": [ - { - "args": "string", - "tool_call_id": "string", - "tool_name": "string" - } - ] + "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.ChatWatchEvent](schemas.md#codersdkchatwatchevent) | +| 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). -## Get chat by ID +## Update chat debug logging setting ### Code samples ```sh # Example request using curl -curl -X GET http://coder-server:8080/api/experimental/chats/{chat} \ - -H 'Accept: application/json' \ +curl -X PUT http://coder-server:8080/api/v2/chats/config/debug-logging \ + -H 'Content-Type: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`GET /api/experimental/chats/{chat}` +`PUT /api/v2/chats/config/debug-logging` -Experimental: this endpoint is subject to change. +> Body parameter + +```json +{ + "allow_users": true +} +``` ### Parameters -| Name | In | Type | Required | Description | -|--------|------|--------------|----------|-------------| -| `chat` | path | string(uuid) | true | Chat ID | +| 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 @@ -765,253 +743,44 @@ Experimental: this endpoint is subject to change. ```json { - "agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e98978", - "archived": true, - "build_id": "bfb1f3fa-bf7b-43a5-9e0b-26cc050e44cb", - "children": [ - { - "agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e98978", - "archived": true, - "build_id": "bfb1f3fa-bf7b-43a5-9e0b-26cc050e44cb", - "children": [], - "client_type": "ui", - "context": { - "dirty": true, - "dirty_since": "2019-08-24T14:15:22Z", - "error": "string", - "resources": [ - { - "error": "string", - "kind": "instruction_file", - "size_bytes": 0, - "skill_description": "string", - "skill_name": "string", - "source": "string", - "status": "ok", - "tools": [ - { - "description": "string", - "name": "string" - } - ] - } - ] - }, - "created_at": "2019-08-24T14:15:22Z", - "diff_status": { - "additions": 0, - "approved": true, - "author_avatar_url": "string", - "author_login": "string", - "base_branch": "string", - "changed_files": 0, - "changes_requested": true, - "chat_id": "efc9fe20-a1e5-4a8c-9c48-f1b30c1e4f86", - "commits": 0, - "deletions": 0, - "head_branch": "string", - "pr_number": 0, - "pull_request_draft": true, - "pull_request_state": "string", - "pull_request_title": "string", - "refreshed_at": "2019-08-24T14:15:22Z", - "reviewer_count": 0, - "stale_at": "2019-08-24T14:15:22Z", - "url": "string" - }, - "files": [ - { - "created_at": "2019-08-24T14:15:22Z", - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "mime_type": "string", - "name": "string", - "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", - "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", - "size_bytes": 0 - } - ], - "has_unread": true, - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "labels": { - "property1": "string", - "property2": "string" - }, - "last_error": { - "detail": "string", - "kind": "generic", - "message": "string", - "provider": "string", - "retryable": true, - "status_code": 0 - }, - "last_model_config_id": "30ebb95f-c255-4759-9429-89aa4ec1554c", - "last_reasoning_effort": "string", - "last_turn_summary": "string", - "mcp_server_ids": [ - "497f6eca-6276-4993-bfeb-53cbbbba6f08" - ], - "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", - "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", - "owner_name": "string", - "owner_username": "string", - "parent_chat_id": "c3609ee6-3b11-4a93-b9ae-e4fabcc99359", - "pin_order": 0, - "plan_mode": "plan", - "queued_for_capacity": true, - "root_chat_id": "2898031c-fdce-4e3e-8c53-4481dd42fcd7", - "shared": true, - "status": "waiting", - "summary": "string", - "title": "string", - "updated_at": "2019-08-24T14:15:22Z", - "warnings": [ - "string" - ], - "workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9" - } - ], - "client_type": "ui", - "context": { - "dirty": true, - "dirty_since": "2019-08-24T14:15:22Z", - "error": "string", - "resources": [ - { - "error": "string", - "kind": "instruction_file", - "size_bytes": 0, - "skill_description": "string", - "skill_name": "string", - "source": "string", - "status": "ok", - "tools": [ - { - "description": "string", - "name": "string" - } - ] - } - ] - }, - "created_at": "2019-08-24T14:15:22Z", - "diff_status": { - "additions": 0, - "approved": true, - "author_avatar_url": "string", - "author_login": "string", - "base_branch": "string", - "changed_files": 0, - "changes_requested": true, - "chat_id": "efc9fe20-a1e5-4a8c-9c48-f1b30c1e4f86", - "commits": 0, - "deletions": 0, - "head_branch": "string", - "pr_number": 0, - "pull_request_draft": true, - "pull_request_state": "string", - "pull_request_title": "string", - "refreshed_at": "2019-08-24T14:15:22Z", - "reviewer_count": 0, - "stale_at": "2019-08-24T14:15:22Z", - "url": "string" - }, - "files": [ - { - "created_at": "2019-08-24T14:15:22Z", - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "mime_type": "string", - "name": "string", - "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", - "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", - "size_bytes": 0 - } - ], - "has_unread": true, - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "labels": { - "property1": "string", - "property2": "string" - }, - "last_error": { - "detail": "string", - "kind": "generic", - "message": "string", - "provider": "string", - "retryable": true, - "status_code": 0 - }, - "last_model_config_id": "30ebb95f-c255-4759-9429-89aa4ec1554c", - "last_reasoning_effort": "string", - "last_turn_summary": "string", - "mcp_server_ids": [ - "497f6eca-6276-4993-bfeb-53cbbbba6f08" - ], - "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", - "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", - "owner_name": "string", - "owner_username": "string", - "parent_chat_id": "c3609ee6-3b11-4a93-b9ae-e4fabcc99359", - "pin_order": 0, - "plan_mode": "plan", - "queued_for_capacity": true, - "root_chat_id": "2898031c-fdce-4e3e-8c53-4481dd42fcd7", - "shared": true, - "status": "waiting", - "summary": "string", - "title": "string", - "updated_at": "2019-08-24T14:15:22Z", - "warnings": [ - "string" - ], - "workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9" + "debug_retention_days": 0 } ``` ### Responses -| Status | Meaning | Description | Schema | -|--------|---------------------------------------------------------|-------------|------------------------------------------| -| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.Chat](schemas.md#codersdkchat) | +| 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 +## Update chat debug retention days ### Code samples ```sh # Example request using curl -curl -X PATCH http://coder-server:8080/api/experimental/chats/{chat} \ +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' ``` -`PATCH /api/experimental/chats/{chat}` - -Experimental: this endpoint is subject to change. +`PUT /api/v2/chats/config/debug-retention-days` > Body parameter ```json { - "archived": true, - "labels": { - "property1": "string", - "property2": "string" - }, - "pin_order": 0, - "plan_mode": "plan", - "title": "string", - "workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9" + "debug_retention_days": 0 } ``` ### Parameters -| Name | In | Type | Required | Description | -|--------|------|--------------------------------------------------------------------|----------|---------------------| -| `chat` | path | string(uuid) | true | Chat ID | -| `body` | body | [codersdk.UpdateChatRequest](schemas.md#codersdkupdatechatrequest) | true | Update chat request | +| Name | In | Type | Required | Description | +|--------|------|--------------------------------------------------------------------------------------------------------|----------|--------------| +| `body` | body | [codersdk.UpdateChatDebugRetentionDaysRequest](schemas.md#codersdkupdatechatdebugretentiondaysrequest) | true | Request body | ### Responses @@ -1021,26 +790,84 @@ Experimental: this endpoint is subject to change. To perform this operation, you must be authenticated. [Learn more](authentication.md). -## Refresh chat context +## Get chat personal model override settings ### Code samples ```sh # Example request using curl -curl -X PUT http://coder-server:8080/api/experimental/chats/{chat}/context \ +curl -X GET http://coder-server:8080/api/v2/chats/config/personal-model-overrides \ -H 'Accept: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`PUT /api/experimental/chats/{chat}/context` +`GET /api/v2/chats/config/personal-model-overrides` -Experimental: this endpoint is subject to change. +### 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 | -|--------|------|--------------|----------|-------------| -| `chat` | path | string(uuid) | true | Chat ID | +| 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 @@ -1048,288 +875,65 @@ Experimental: this endpoint is subject to change. ```json { - "agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e98978", - "archived": true, - "build_id": "bfb1f3fa-bf7b-43a5-9e0b-26cc050e44cb", - "children": [ - { - "agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e98978", - "archived": true, - "build_id": "bfb1f3fa-bf7b-43a5-9e0b-26cc050e44cb", - "children": [], - "client_type": "ui", - "context": { - "dirty": true, - "dirty_since": "2019-08-24T14:15:22Z", - "error": "string", - "resources": [ - { - "error": "string", - "kind": "instruction_file", - "size_bytes": 0, - "skill_description": "string", - "skill_name": "string", - "source": "string", - "status": "ok", - "tools": [ - { - "description": "string", - "name": "string" - } - ] - } - ] - }, - "created_at": "2019-08-24T14:15:22Z", - "diff_status": { - "additions": 0, - "approved": true, - "author_avatar_url": "string", - "author_login": "string", - "base_branch": "string", - "changed_files": 0, - "changes_requested": true, - "chat_id": "efc9fe20-a1e5-4a8c-9c48-f1b30c1e4f86", - "commits": 0, - "deletions": 0, - "head_branch": "string", - "pr_number": 0, - "pull_request_draft": true, - "pull_request_state": "string", - "pull_request_title": "string", - "refreshed_at": "2019-08-24T14:15:22Z", - "reviewer_count": 0, - "stale_at": "2019-08-24T14:15:22Z", - "url": "string" - }, - "files": [ - { - "created_at": "2019-08-24T14:15:22Z", - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "mime_type": "string", - "name": "string", - "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", - "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", - "size_bytes": 0 - } - ], - "has_unread": true, - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "labels": { - "property1": "string", - "property2": "string" - }, - "last_error": { - "detail": "string", - "kind": "generic", - "message": "string", - "provider": "string", - "retryable": true, - "status_code": 0 - }, - "last_model_config_id": "30ebb95f-c255-4759-9429-89aa4ec1554c", - "last_reasoning_effort": "string", - "last_turn_summary": "string", - "mcp_server_ids": [ - "497f6eca-6276-4993-bfeb-53cbbbba6f08" - ], - "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", - "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", - "owner_name": "string", - "owner_username": "string", - "parent_chat_id": "c3609ee6-3b11-4a93-b9ae-e4fabcc99359", - "pin_order": 0, - "plan_mode": "plan", - "queued_for_capacity": true, - "root_chat_id": "2898031c-fdce-4e3e-8c53-4481dd42fcd7", - "shared": true, - "status": "waiting", - "summary": "string", - "title": "string", - "updated_at": "2019-08-24T14:15:22Z", - "warnings": [ - "string" - ], - "workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9" - } - ], - "client_type": "ui", - "context": { - "dirty": true, - "dirty_since": "2019-08-24T14:15:22Z", - "error": "string", - "resources": [ - { - "error": "string", - "kind": "instruction_file", - "size_bytes": 0, - "skill_description": "string", - "skill_name": "string", - "source": "string", - "status": "ok", - "tools": [ - { - "description": "string", - "name": "string" - } - ] - } - ] - }, - "created_at": "2019-08-24T14:15:22Z", - "diff_status": { - "additions": 0, - "approved": true, - "author_avatar_url": "string", - "author_login": "string", - "base_branch": "string", - "changed_files": 0, - "changes_requested": true, - "chat_id": "efc9fe20-a1e5-4a8c-9c48-f1b30c1e4f86", - "commits": 0, - "deletions": 0, - "head_branch": "string", - "pr_number": 0, - "pull_request_draft": true, - "pull_request_state": "string", - "pull_request_title": "string", - "refreshed_at": "2019-08-24T14:15:22Z", - "reviewer_count": 0, - "stale_at": "2019-08-24T14:15:22Z", - "url": "string" - }, - "files": [ - { - "created_at": "2019-08-24T14:15:22Z", - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "mime_type": "string", - "name": "string", - "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", - "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", - "size_bytes": 0 - } - ], - "has_unread": true, - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "labels": { - "property1": "string", - "property2": "string" - }, - "last_error": { - "detail": "string", - "kind": "generic", - "message": "string", - "provider": "string", - "retryable": true, - "status_code": 0 - }, - "last_model_config_id": "30ebb95f-c255-4759-9429-89aa4ec1554c", - "last_reasoning_effort": "string", - "last_turn_summary": "string", - "mcp_server_ids": [ - "497f6eca-6276-4993-bfeb-53cbbbba6f08" - ], - "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", - "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", - "owner_name": "string", - "owner_username": "string", - "parent_chat_id": "c3609ee6-3b11-4a93-b9ae-e4fabcc99359", - "pin_order": 0, - "plan_mode": "plan", - "queued_for_capacity": true, - "root_chat_id": "2898031c-fdce-4e3e-8c53-4481dd42fcd7", - "shared": true, - "status": "waiting", - "summary": "string", - "title": "string", - "updated_at": "2019-08-24T14:15:22Z", - "warnings": [ - "string" - ], - "workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9" + "plan_mode_instructions": "string" } ``` ### Responses -| Status | Meaning | Description | Schema | -|--------|---------------------------------------------------------|-------------|------------------------------------------| -| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.Chat](schemas.md#codersdkchat) | +| 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). -## Get chat cost +## Update chat plan mode instructions ### Code samples ```sh # Example request using curl -curl -X GET http://coder-server:8080/api/experimental/chats/{chat}/cost \ - -H 'Accept: application/json' \ +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' ``` -`GET /api/experimental/chats/{chat}/cost` - -Experimental: this endpoint is subject to change. - -Cost covers the whole chat tree: the root chat plus every -subagent chat beneath it. Requesting cost for a subagent chat -returns that same total. - -Cost is derived from AI Gateway data, which is subject to its -own retention period, 60 days by default, configured -independently of chat retention. Spend for requests older than -that period is no longer reported, so a chat whose requests -have all been purged reports zero cost. - -### Parameters - -| Name | In | Type | Required | Description | -|--------|------|--------------|----------|-------------| -| `chat` | path | string(uuid) | true | Chat ID | - -### Example responses +`PUT /api/v2/chats/config/plan-mode-instructions` -> 200 Response +> Body parameter ```json { - "chat_id": "efc9fe20-a1e5-4a8c-9c48-f1b30c1e4f86", - "request_count": 0, - "total_cost_micros": 0, - "unpriced_request_count": 0 + "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 | -|--------|---------------------------------------------------------|-------------|--------------------------------------------------| -| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.ChatCost](schemas.md#codersdkchatcost) | +| 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 diff contents +## Get chat retention days ### Code samples ```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/config/retention-days \ -H 'Accept: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`GET /api/experimental/chats/{chat}/diff` - -Experimental: this endpoint is subject to change. - -### Parameters - -| Name | In | Type | Required | Description | -|--------|------|--------------|----------|-------------| -| `chat` | path | string(uuid) | true | Chat ID | +`GET /api/v2/chats/config/retention-days` ### Example responses @@ -1337,43 +941,65 @@ Experimental: this endpoint is subject to change. ```json { - "branch": "string", - "chat_id": "efc9fe20-a1e5-4a8c-9c48-f1b30c1e4f86", - "diff": "string", - "provider": "string", - "pull_request_url": "string", - "remote_origin": "string" + "retention_days": 0 } ``` ### Responses -| Status | Meaning | Description | Schema | -|--------|---------------------------------------------------------|-------------|------------------------------------------------------------------| -| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.ChatDiffContents](schemas.md#codersdkchatdiffcontents) | +| Status | Meaning | Description | Schema | +|--------|---------------------------------------------------------|-------------|------------------------------------------------------------------------------------| +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.ChatRetentionDaysResponse](schemas.md#codersdkchatretentiondaysresponse) | To perform this operation, you must be authenticated. [Learn more](authentication.md). -## Interrupt chat +## Update chat retention days ### Code samples ```sh # Example request using curl -curl -X POST http://coder-server:8080/api/experimental/chats/{chat}/interrupt \ - -H 'Accept: application/json' \ +curl -X PUT http://coder-server:8080/api/v2/chats/config/retention-days \ + -H 'Content-Type: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`POST /api/experimental/chats/{chat}/interrupt` +`PUT /api/v2/chats/config/retention-days` -Experimental: this endpoint is subject to change. +> Body parameter + +```json +{ + "retention_days": 0 +} +``` ### Parameters -| Name | In | Type | Required | Description | -|--------|------|--------------|----------|-------------| -| `chat` | path | string(uuid) | true | Chat ID | +| Name | In | Type | Required | Description | +|--------|------|----------------------------------------------------------------------------------------------|----------|--------------| +| `body` | body | [codersdk.UpdateChatRetentionDaysRequest](schemas.md#codersdkupdatechatretentiondaysrequest) | 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 @@ -1381,400 +1007,79 @@ Experimental: this endpoint is subject to change. ```json { - "agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e98978", - "archived": true, - "build_id": "bfb1f3fa-bf7b-43a5-9e0b-26cc050e44cb", - "children": [ - { - "agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e98978", - "archived": true, - "build_id": "bfb1f3fa-bf7b-43a5-9e0b-26cc050e44cb", - "children": [], - "client_type": "ui", - "context": { - "dirty": true, - "dirty_since": "2019-08-24T14:15:22Z", - "error": "string", - "resources": [ - { - "error": "string", - "kind": "instruction_file", - "size_bytes": 0, - "skill_description": "string", - "skill_name": "string", - "source": "string", - "status": "ok", - "tools": [ - { - "description": "string", - "name": "string" - } - ] - } - ] - }, - "created_at": "2019-08-24T14:15:22Z", - "diff_status": { - "additions": 0, - "approved": true, - "author_avatar_url": "string", - "author_login": "string", - "base_branch": "string", - "changed_files": 0, - "changes_requested": true, - "chat_id": "efc9fe20-a1e5-4a8c-9c48-f1b30c1e4f86", - "commits": 0, - "deletions": 0, - "head_branch": "string", - "pr_number": 0, - "pull_request_draft": true, - "pull_request_state": "string", - "pull_request_title": "string", - "refreshed_at": "2019-08-24T14:15:22Z", - "reviewer_count": 0, - "stale_at": "2019-08-24T14:15:22Z", - "url": "string" - }, - "files": [ - { - "created_at": "2019-08-24T14:15:22Z", - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "mime_type": "string", - "name": "string", - "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", - "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", - "size_bytes": 0 - } - ], - "has_unread": true, - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "labels": { - "property1": "string", - "property2": "string" - }, - "last_error": { - "detail": "string", - "kind": "generic", - "message": "string", - "provider": "string", - "retryable": true, - "status_code": 0 - }, - "last_model_config_id": "30ebb95f-c255-4759-9429-89aa4ec1554c", - "last_reasoning_effort": "string", - "last_turn_summary": "string", - "mcp_server_ids": [ - "497f6eca-6276-4993-bfeb-53cbbbba6f08" - ], - "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", - "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", - "owner_name": "string", - "owner_username": "string", - "parent_chat_id": "c3609ee6-3b11-4a93-b9ae-e4fabcc99359", - "pin_order": 0, - "plan_mode": "plan", - "queued_for_capacity": true, - "root_chat_id": "2898031c-fdce-4e3e-8c53-4481dd42fcd7", - "shared": true, - "status": "waiting", - "summary": "string", - "title": "string", - "updated_at": "2019-08-24T14:15:22Z", - "warnings": [ - "string" - ], - "workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9" - } - ], - "client_type": "ui", - "context": { - "dirty": true, - "dirty_since": "2019-08-24T14:15:22Z", - "error": "string", - "resources": [ - { - "error": "string", - "kind": "instruction_file", - "size_bytes": 0, - "skill_description": "string", - "skill_name": "string", - "source": "string", - "status": "ok", - "tools": [ - { - "description": "string", - "name": "string" - } - ] - } - ] - }, - "created_at": "2019-08-24T14:15:22Z", - "diff_status": { - "additions": 0, - "approved": true, - "author_avatar_url": "string", - "author_login": "string", - "base_branch": "string", - "changed_files": 0, - "changes_requested": true, - "chat_id": "efc9fe20-a1e5-4a8c-9c48-f1b30c1e4f86", - "commits": 0, - "deletions": 0, - "head_branch": "string", - "pr_number": 0, - "pull_request_draft": true, - "pull_request_state": "string", - "pull_request_title": "string", - "refreshed_at": "2019-08-24T14:15:22Z", - "reviewer_count": 0, - "stale_at": "2019-08-24T14:15:22Z", - "url": "string" - }, - "files": [ - { - "created_at": "2019-08-24T14:15:22Z", - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "mime_type": "string", - "name": "string", - "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", - "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", - "size_bytes": 0 - } - ], - "has_unread": true, - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "labels": { - "property1": "string", - "property2": "string" - }, - "last_error": { - "detail": "string", - "kind": "generic", - "message": "string", - "provider": "string", - "retryable": true, - "status_code": 0 - }, - "last_model_config_id": "30ebb95f-c255-4759-9429-89aa4ec1554c", - "last_reasoning_effort": "string", - "last_turn_summary": "string", - "mcp_server_ids": [ - "497f6eca-6276-4993-bfeb-53cbbbba6f08" - ], - "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", - "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", - "owner_name": "string", - "owner_username": "string", - "parent_chat_id": "c3609ee6-3b11-4a93-b9ae-e4fabcc99359", - "pin_order": 0, - "plan_mode": "plan", - "queued_for_capacity": true, - "root_chat_id": "2898031c-fdce-4e3e-8c53-4481dd42fcd7", - "shared": true, - "status": "waiting", - "summary": "string", - "title": "string", - "updated_at": "2019-08-24T14:15:22Z", - "warnings": [ - "string" - ], - "workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9" + "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.Chat](schemas.md#codersdkchat) | +| 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). -## List chat messages +## Update chat system prompt ### Code samples ```sh # Example request using curl -curl -X GET http://coder-server:8080/api/experimental/chats/{chat}/messages \ - -H 'Accept: application/json' \ +curl -X PUT http://coder-server:8080/api/v2/chats/config/system-prompt \ + -H 'Content-Type: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`GET /api/experimental/chats/{chat}/messages` +`PUT /api/v2/chats/config/system-prompt` -Experimental: this endpoint is subject to change. +> Body parameter -### Parameters +```json +{ + "include_default_system_prompt": true, + "system_prompt": "string" +} +``` -| Name | In | Type | Required | Description | -|-------------|-------|--------------|----------|--------------------------------------| -| `chat` | path | string(uuid) | true | Chat ID | -| `before_id` | query | integer | false | Return messages with id < before_id | -| `after_id` | query | integer | false | Return messages with id > after_id | -| `limit` | query | integer | false | Page size, 1 to 200. Defaults to 50. | +### Parameters -### Example responses +| Name | In | Type | Required | Description | +|--------|------|--------------------------------------------------------------------------------------------|----------|--------------| +| `body` | body | [codersdk.UpdateChatSystemPromptRequest](schemas.md#codersdkupdatechatsystempromptrequest) | true | Request body | -> 200 Response +### Responses -```json +| 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 compaction thresholds + +### Code samples + +```sh +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/chats/config/user-compaction-thresholds \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /api/v2/chats/config/user-compaction-thresholds` + +### Example responses + +> 200 Response + +```json { - "has_more": true, - "messages": [ + "thresholds": [ { - "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 - } - } - ], - "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" + "threshold_percent": 0 } ] } @@ -1782,59 +1087,98 @@ Experimental: this endpoint is subject to change. ### Responses -| Status | Meaning | Description | Schema | -|--------|---------------------------------------------------------|-------------|--------------------------------------------------------------------------| -| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.ChatMessagesResponse](schemas.md#codersdkchatmessagesresponse) | +| Status | Meaning | Description | Schema | +|--------|---------------------------------------------------------|-------------|------------------------------------------------------------------------------------------| +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.UserChatCompactionThresholds](schemas.md#codersdkuserchatcompactionthresholds) | To perform this operation, you must be authenticated. [Learn more](authentication.md). -## Send chat message +## Update user chat compaction threshold ### Code samples ```sh # Example request using curl -curl -X POST http://coder-server:8080/api/experimental/chats/{chat}/messages \ +curl -X PUT http://coder-server:8080/api/v2/chats/config/user-compaction-thresholds/{modelConfig} \ -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. +`PUT /api/v2/chats/config/user-compaction-thresholds/{modelConfig}` > Body parameter ```json { - "busy_behavior": "queue", - "content": [ - { - "content": "string", - "end_line": 0, - "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", - "file_name": "string", - "start_line": 0, - "text": "string", - "type": "text" - } - ], - "mcp_server_ids": [ - "497f6eca-6276-4993-bfeb-53cbbbba6f08" - ], + "threshold_percent": 100 +} +``` + +### Parameters + +| Name | In | Type | Required | Description | +|---------------|------|------------------------------------------------------------------------------------------------------------------|----------|-----------------| +| `modelConfig` | path | string | true | Model config ID | +| `body` | body | [codersdk.UpdateUserChatCompactionThresholdRequest](schemas.md#codersdkupdateuserchatcompactionthresholdrequest) | true | Request body | + +### Example responses + +> 200 Response + +```json +{ "model_config_id": "f5fb4d91-62ca-4377-9ee6-5d43ba00d205", - "plan_mode": "plan", - "reasoning_effort": "string" + "threshold_percent": 0 } ``` +### Responses + +| Status | Meaning | Description | Schema | +|--------|---------------------------------------------------------|-------------|----------------------------------------------------------------------------------------| +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.UserChatCompactionThreshold](schemas.md#codersdkuserchatcompactionthreshold) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Delete user chat compaction threshold + +### Code samples + +```sh +# Example request using curl +curl -X DELETE http://coder-server:8080/api/v2/chats/config/user-compaction-thresholds/{modelConfig} \ + -H 'Coder-Session-Token: API_KEY' +``` + +`DELETE /api/v2/chats/config/user-compaction-thresholds/{modelConfig}` + ### Parameters -| Name | In | Type | Required | Description | -|--------|------|----------------------------------------------------------------------------------|----------|-----------------------------| -| `chat` | path | string(uuid) | true | Chat ID | -| `body` | body | [codersdk.CreateChatMessageRequest](schemas.md#codersdkcreatechatmessagerequest) | true | Create chat message request | +| Name | In | Type | Required | Description | +|---------------|------|--------|----------|-----------------| +| `modelConfig` | path | string | true | Model config 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). + +## 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 @@ -1842,516 +1186,113 @@ Experimental: this endpoint is subject to change. ```json { - "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 - } - }, - "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", - "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 - } - } - ], - "queued": true, - "queued_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", - "id": 0, - "model_config_id": "f5fb4d91-62ca-4377-9ee6-5d43ba00d205" - }, - "warnings": [ - "string" - ] + "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.CreateChatMessageResponse](schemas.md#codersdkcreatechatmessageresponse) | +| 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). -## Edit chat message +## Update user chat debug logging setting ### Code samples ```sh # Example request using curl -curl -X PATCH http://coder-server:8080/api/experimental/chats/{chat}/messages/{message} \ +curl -X PUT http://coder-server:8080/api/v2/chats/config/user-debug-logging \ -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. +`PUT /api/v2/chats/config/user-debug-logging` > Body parameter ```json { - "content": [ - { - "content": "string", - "end_line": 0, - "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", - "file_name": "string", - "start_line": 0, - "text": "string", - "type": "text" - } - ], - "mcp_server_ids": [ - "497f6eca-6276-4993-bfeb-53cbbbba6f08" - ], - "model_config_id": "f5fb4d91-62ca-4377-9ee6-5d43ba00d205", - "reasoning_effort": "string" + "debug_logging_enabled": true } ``` ### Parameters -| Name | In | Type | Required | Description | -|-----------|------|------------------------------------------------------------------------------|----------|---------------------------| -| `chat` | path | string(uuid) | true | Chat ID | -| `message` | path | integer | true | Message ID | -| `body` | body | [codersdk.EditChatMessageRequest](schemas.md#codersdkeditchatmessagerequest) | true | Edit chat message request | +| Name | In | Type | Required | Description | +|--------|------|----------------------------------------------------------------------------------------------------|----------|--------------| +| `body` | body | [codersdk.UpdateUserChatDebugLoggingRequest](schemas.md#codersdkupdateuserchatdebugloggingrequest) | true | Request body | -### Example responses +### 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 { - "deleted_message_ids": [ - 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 - } - }, - "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", - "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 - } - } - ], - "warnings": [ - "string" - ] + "custom_prompt": "string" } ``` ### Responses -| Status | Meaning | Description | Schema | -|--------|---------------------------------------------------------|-------------|--------------------------------------------------------------------------------| -| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.EditChatMessageResponse](schemas.md#codersdkeditchatmessageresponse) | +| 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). -## List chat user prompts +## Update user chat custom prompt ### Code samples ```sh # Example request using curl -curl -X GET http://coder-server:8080/api/experimental/chats/{chat}/prompts \ +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' ``` -`GET /api/experimental/chats/{chat}/prompts` +`PUT /api/v2/chats/config/user-prompt` -Experimental: this endpoint is subject to change. +> Body parameter -Returns the user-authored prompts in a chat, newest first, -with each prompt's text parts concatenated in the order they -were authored. Used by the composer to power the up/down -arrow prompt-history cycle without paging through every -message in the chat. +```json +{ + "custom_prompt": "string" +} +``` ### Parameters -| Name | In | Type | Required | Description | -|---------|-------|--------------|----------|-----------------------------------------------------------------------------| -| `chat` | path | string(uuid) | true | Chat ID | -| `limit` | query | integer | false | Page size, 0 to 2000. 0 (the default) means the server-side default of 500. | +| Name | In | Type | Required | Description | +|--------|------|--------------------------------------------------------------------------|----------|--------------| +| `body` | body | [codersdk.UserChatCustomPrompt](schemas.md#codersdkuserchatcustomprompt) | true | Request body | ### Example responses @@ -2359,599 +1300,5213 @@ message in the chat. ```json { - "prompts": [ - { - "id": 0, - "text": "string" - } - ] + "custom_prompt": "string" } ``` ### Responses -| Status | Meaning | Description | Schema | -|--------|---------------------------------------------------------|-------------|------------------------------------------------------------------------| -| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.ChatPromptsResponse](schemas.md#codersdkchatpromptsresponse) | +| 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). -## Reconcile invalid chat state +## Get chat workspace time to live ### Code samples ```sh # Example request using curl -curl -X POST http://coder-server:8080/api/experimental/chats/{chat}/reconcile-invalid \ +curl -X GET http://coder-server:8080/api/v2/chats/config/workspace-ttl \ -H 'Accept: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`POST /api/experimental/chats/{chat}/reconcile-invalid` +`GET /api/v2/chats/config/workspace-ttl` -Experimental: this endpoint is subject to change. +### Example responses -### Parameters +> 200 Response -| Name | In | Type | Required | Description | -|--------|------|--------------|----------|-------------| -| `chat` | path | string(uuid) | true | Chat ID | +```json +{ + "workspace_ttl_ms": 0 +} +``` -### Example responses +### Responses -> 200 Response +| 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 { - "agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e98978", - "archived": true, - "build_id": "bfb1f3fa-bf7b-43a5-9e0b-26cc050e44cb", - "children": [ - { - "agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e98978", - "archived": true, - "build_id": "bfb1f3fa-bf7b-43a5-9e0b-26cc050e44cb", - "children": [], - "client_type": "ui", - "context": { - "dirty": true, - "dirty_since": "2019-08-24T14:15:22Z", - "error": "string", - "resources": [ - { - "error": "string", - "kind": "instruction_file", - "size_bytes": 0, - "skill_description": "string", - "skill_name": "string", - "source": "string", - "status": "ok", - "tools": [ - { - "description": "string", - "name": "string" - } - ] - } - ] - }, - "created_at": "2019-08-24T14:15:22Z", - "diff_status": { - "additions": 0, - "approved": true, - "author_avatar_url": "string", - "author_login": "string", - "base_branch": "string", - "changed_files": 0, - "changes_requested": true, - "chat_id": "efc9fe20-a1e5-4a8c-9c48-f1b30c1e4f86", - "commits": 0, - "deletions": 0, - "head_branch": "string", - "pr_number": 0, - "pull_request_draft": true, - "pull_request_state": "string", - "pull_request_title": "string", - "refreshed_at": "2019-08-24T14:15:22Z", - "reviewer_count": 0, - "stale_at": "2019-08-24T14:15:22Z", - "url": "string" - }, - "files": [ - { - "created_at": "2019-08-24T14:15:22Z", - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "mime_type": "string", - "name": "string", - "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", - "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", - "size_bytes": 0 - } - ], - "has_unread": true, - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "labels": { - "property1": "string", - "property2": "string" - }, - "last_error": { - "detail": "string", - "kind": "generic", - "message": "string", - "provider": "string", - "retryable": true, - "status_code": 0 - }, - "last_model_config_id": "30ebb95f-c255-4759-9429-89aa4ec1554c", - "last_reasoning_effort": "string", - "last_turn_summary": "string", - "mcp_server_ids": [ - "497f6eca-6276-4993-bfeb-53cbbbba6f08" - ], - "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", - "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", - "owner_name": "string", - "owner_username": "string", - "parent_chat_id": "c3609ee6-3b11-4a93-b9ae-e4fabcc99359", - "pin_order": 0, - "plan_mode": "plan", - "queued_for_capacity": true, - "root_chat_id": "2898031c-fdce-4e3e-8c53-4481dd42fcd7", - "shared": true, - "status": "waiting", - "summary": "string", - "title": "string", - "updated_at": "2019-08-24T14:15:22Z", - "warnings": [ - "string" - ], - "workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9" - } - ], - "client_type": "ui", - "context": { - "dirty": true, - "dirty_since": "2019-08-24T14:15:22Z", - "error": "string", - "resources": [ - { - "error": "string", - "kind": "instruction_file", - "size_bytes": 0, - "skill_description": "string", - "skill_name": "string", - "source": "string", - "status": "ok", - "tools": [ - { - "description": "string", - "name": "string" - } - ] - } - ] - }, - "created_at": "2019-08-24T14:15:22Z", - "diff_status": { - "additions": 0, - "approved": true, - "author_avatar_url": "string", - "author_login": "string", - "base_branch": "string", - "changed_files": 0, - "changes_requested": true, - "chat_id": "efc9fe20-a1e5-4a8c-9c48-f1b30c1e4f86", - "commits": 0, - "deletions": 0, - "head_branch": "string", - "pr_number": 0, - "pull_request_draft": true, - "pull_request_state": "string", - "pull_request_title": "string", - "refreshed_at": "2019-08-24T14:15:22Z", - "reviewer_count": 0, - "stale_at": "2019-08-24T14:15:22Z", - "url": "string" - }, - "files": [ - { - "created_at": "2019-08-24T14:15:22Z", - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "mime_type": "string", - "name": "string", - "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", - "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", - "size_bytes": 0 - } - ], - "has_unread": true, - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "labels": { - "property1": "string", - "property2": "string" - }, - "last_error": { - "detail": "string", - "kind": "generic", - "message": "string", - "provider": "string", - "retryable": true, - "status_code": 0 - }, - "last_model_config_id": "30ebb95f-c255-4759-9429-89aa4ec1554c", - "last_reasoning_effort": "string", - "last_turn_summary": "string", - "mcp_server_ids": [ - "497f6eca-6276-4993-bfeb-53cbbbba6f08" - ], - "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", - "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", - "owner_name": "string", - "owner_username": "string", - "parent_chat_id": "c3609ee6-3b11-4a93-b9ae-e4fabcc99359", - "pin_order": 0, - "plan_mode": "plan", - "queued_for_capacity": true, - "root_chat_id": "2898031c-fdce-4e3e-8c53-4481dd42fcd7", - "shared": true, - "status": "waiting", - "summary": "string", - "title": "string", - "updated_at": "2019-08-24T14:15:22Z", - "warnings": [ - "string" - ], - "workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9" + "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 | -|--------|---------------------------------------------------------|-------------|------------------------------------------| -| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.Chat](schemas.md#codersdkchat) | +| 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). -## Stream chat events via WebSockets +## Upload chat file ### Code samples ```sh # Example request using curl -curl -X GET http://coder-server:8080/api/experimental/chats/{chat}/stream \ +curl -X POST http://coder-server:8080/api/v2/chats/files?organization=497f6eca-6276-4993-bfeb-53cbbbba6f08 \ -H 'Accept: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`GET /api/experimental/chats/{chat}/stream` - -Experimental: this endpoint is subject to change. +`POST /api/v2/chats/files` ### Parameters -| Name | In | Type | Required | Description | -|--------|------|--------------|----------|-------------| -| `chat` | path | string(uuid) | true | Chat ID | +| Name | In | Type | Required | Description | +|----------------|-------|--------------|----------|-----------------| +| `organization` | query | string(uuid) | true | Organization ID | ### Example responses -> 200 Response +> 201 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": [ + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +|--------|-------------------------------------------------------------------------|-----------------------------|------------------------------------------------------------------------------| +| 201 | [Created](https://tools.ietf.org/html/rfc7231#section-6.3.2) | Created | [codersdk.UploadChatFileResponse](schemas.md#codersdkuploadchatfileresponse) | +| 413 | [Payload Too Large](https://tools.ietf.org/html/rfc7231#section-6.5.11) | Request body exceeds 10 MiB | [codersdk.Response](schemas.md#codersdkresponse) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Get chat file + +### Code samples + +```sh +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/chats/files/{file} \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /api/v2/chats/files/{file}` + +### Parameters + +| Name | In | Type | Required | Description | +|--------|------|--------------|----------|-------------| +| `file` | path | string(uuid) | true | File ID | + +### Responses + +| Status | Meaning | Description | Schema | +|--------|---------------------------------------------------------|-------------|--------| +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Download chat file with signed token + +### Code samples + +```sh +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/chats/files/{file}/download?token=string + +``` + +`GET /api/v2/chats/files/{file}/download` + +### Parameters + +| Name | In | Type | Required | Description | +|---------|-------|--------------|----------|-----------------------| +| `file` | path | string(uuid) | true | File ID | +| `token` | query | string | true | Signed download token | + +### Responses + +| Status | Meaning | Description | Schema | +|--------|---------------------------------------------------------|-------------|--------| +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | | + +## Create chat file download URL + +### Code samples + +```sh +# Example request using curl +curl -X POST http://coder-server:8080/api/v2/chats/files/{file}/download-url \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`POST /api/v2/chats/files/{file}/download-url` + +### Parameters + +| Name | In | Type | Required | Description | +|--------|------|--------------|----------|-------------| +| `file` | path | string(uuid) | true | File ID | + +### Example responses + +> 200 Response + +```json +{ + "expires_at": "2019-08-24T14:15:22Z", + "mime_type": "string", + "name": "string", + "sha256": "string", + "size_bytes": 0, + "url": "http://example.com" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +|--------|---------------------------------------------------------|-------------|----------------------------------------------------------------------------------------| +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.ChatFileDownloadURLResponse](schemas.md#codersdkchatfiledownloadurlresponse) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## List AI models and provider descriptors in the default organization + +### Code samples + +```sh +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/chats/models \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /api/v2/chats/models` + +### Example responses + +> 200 Response + +```json +{ + "models": [ + { + "ai_provider_id": "5a3b8ff9-20e7-4c37-ba1a-5b433e355819", + "compression_threshold": 0, + "context_limit": 0, + "created_at": "2019-08-24T14:15:22Z", + "display_name": "string", + "enabled": true, + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "is_default": true, + "model": "string", + "model_config": { + "frequency_penalty": 0, + "max_output_tokens": 0, + "openai_config": { + "use_responses_api": true + }, + "presence_penalty": 0, + "provider_options": { + "anthropic": { + "allowed_domains": [ + "string" + ], + "blocked_domains": [ + "string" + ], + "context_1m_enabled": true, + "disable_parallel_tool_use": true, + "send_reasoning": true, + "thinking": { + "budget_tokens": 0 + }, + "thinking_display": "string", + "web_search_enabled": true + }, + "google": { + "cached_content": "string", + "safety_settings": [ + { + "category": "string", + "threshold": "string" + } + ], + "thinking_config": { + "include_thoughts": true, + "thinking_budget": 0, + "thinking_level": "string" + }, + "threshold": "string", + "web_search_enabled": true + }, + "openai": { + "allowed_domains": [ + "string" + ], + "include": [ + "string" + ], + "instructions": "string", + "log_probs": true, + "logit_bias": { + "property1": 0, + "property2": 0 + }, + "max_completion_tokens": 0, + "max_tool_calls": 0, + "metadata": { + "property1": null, + "property2": null + }, + "parallel_tool_calls": true, + "prediction": { + "property1": null, + "property2": null + }, + "prompt_cache_key": "string", + "reasoning_summary": "string", + "safety_identifier": "string", + "search_context_size": "string", + "service_tier": "string", + "store": true, + "strict_json_schema": true, + "structured_outputs": true, + "text_verbosity": "string", + "top_log_probs": 0, + "user": "string", + "web_search_enabled": true + }, + "openaicompat": { + "user": "string" + }, + "openrouter": { + "extra_body": { + "property1": null, + "property2": null + }, + "include_usage": true, + "log_probs": true, + "logit_bias": { + "property1": 0, + "property2": 0 + }, + "parallel_tool_calls": true, + "provider": { + "allow_fallbacks": true, + "data_collection": "string", + "ignore": [ + "string" + ], + "only": [ + "string" + ], + "order": [ + "string" + ], + "quantizations": [ + "string" + ], + "require_parameters": true, + "sort": "string" + }, + "reasoning": { + "enabled": true, + "exclude": true, + "max_tokens": 0 + }, + "user": "string" + }, + "vercel": { + "extra_body": { + "property1": null, + "property2": null + }, + "logit_bias": { + "property1": 0, + "property2": 0 + }, + "logprobs": true, + "parallel_tool_calls": true, + "providerOptions": { + "models": [ + "string" + ], + "order": [ + "string" + ] + }, + "reasoning": { + "enabled": true, + "exclude": true, + "max_tokens": 0 + }, + "top_logprobs": 0, + "user": "string" + } + }, + "reasoning_effort": { + "default": "string", + "max": "string" + }, + "temperature": 0, + "top_k": 0, + "top_p": 0 + }, + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "reasoning_efforts": [ + "string" + ], + "updated_at": "2019-08-24T14:15:22Z" + } + ], + "providers": [ + { + "allow_user_api_key": true, + "available": true, + "display_name": "string", + "enabled": true, + "has_api_key": true, + "has_effective_api_key": true, + "has_user_api_key": true, + "icon": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "type": "string", + "unavailable_reason": "missing_api_key" + } + ], + "unsupported_providers": [ + { + "display_name": "string", + "provider": "string" + } + ] +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +|--------|---------------------------------------------------------|-------------|----------------------------------------------------------------------------------------------| +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.OrganizationChatModelsResponse](schemas.md#codersdkorganizationchatmodelsresponse) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Watch chat events for a user via WebSockets + +### Code samples + +```sh +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/chats/watch \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /api/v2/chats/watch` + +### Example responses + +> 200 Response + +```json +{ + "chat": { + "agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e98978", + "archived": true, + "build_id": "bfb1f3fa-bf7b-43a5-9e0b-26cc050e44cb", + "children": [ + {} + ], + "client_type": "ui", + "context": { + "dirty": true, + "dirty_since": "2019-08-24T14:15:22Z", + "error": "string", + "resources": [ + { + "error": "string", + "kind": "instruction_file", + "size_bytes": 0, + "skill_description": "string", + "skill_name": "string", + "source": "string", + "status": "ok", + "tools": [ + { + "description": "string", + "name": "string" + } + ] + } + ] + }, + "created_at": "2019-08-24T14:15:22Z", + "diff_status": { + "additions": 0, + "approved": true, + "author_avatar_url": "string", + "author_login": "string", + "base_branch": "string", + "changed_files": 0, + "changes_requested": true, + "chat_id": "efc9fe20-a1e5-4a8c-9c48-f1b30c1e4f86", + "commits": 0, + "deletions": 0, + "head_branch": "string", + "pr_number": 0, + "pull_request_draft": true, + "pull_request_state": "string", + "pull_request_title": "string", + "refreshed_at": "2019-08-24T14:15:22Z", + "reviewer_count": 0, + "stale_at": "2019-08-24T14:15:22Z", + "url": "string" + }, + "files": [ + { + "created_at": "2019-08-24T14:15:22Z", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "mime_type": "string", + "name": "string", + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", + "size_bytes": 0 + } + ], + "has_unread": true, + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "labels": { + "property1": "string", + "property2": "string" + }, + "last_error": { + "detail": "string", + "kind": "generic", + "message": "string", + "provider": "string", + "retryable": true, + "status_code": 0 + }, + "last_model_config_id": "30ebb95f-c255-4759-9429-89aa4ec1554c", + "last_reasoning_effort": "string", + "last_turn_summary": "string", + "mcp_server_ids": [ + "497f6eca-6276-4993-bfeb-53cbbbba6f08" + ], + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", + "owner_name": "string", + "owner_username": "string", + "parent_chat_id": "c3609ee6-3b11-4a93-b9ae-e4fabcc99359", + "pin_order": 0, + "plan_mode": "plan", + "queued_for_capacity": true, + "root_chat_id": "2898031c-fdce-4e3e-8c53-4481dd42fcd7", + "shared": true, + "status": "waiting", + "summary": "string", + "title": "string", + "updated_at": "2019-08-24T14:15:22Z", + "warnings": [ + "string" + ], + "workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9" + }, + "kind": "status_change", + "tool_calls": [ + { + "args": "string", + "tool_call_id": "string", + "tool_name": "string" + } + ] +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +|--------|---------------------------------------------------------|-------------|--------------------------------------------------------------| +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.ChatWatchEvent](schemas.md#codersdkchatwatchevent) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Get chat by ID + +### Code samples + +```sh +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/chats/{chat} \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /api/v2/chats/{chat}` + +### Parameters + +| Name | In | Type | Required | Description | +|--------|------|--------------|----------|-------------| +| `chat` | path | string(uuid) | true | Chat ID | + +### Example responses + +> 200 Response + +```json +{ + "agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e98978", + "archived": true, + "build_id": "bfb1f3fa-bf7b-43a5-9e0b-26cc050e44cb", + "children": [ + { + "agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e98978", + "archived": true, + "build_id": "bfb1f3fa-bf7b-43a5-9e0b-26cc050e44cb", + "children": [], + "client_type": "ui", + "context": { + "dirty": true, + "dirty_since": "2019-08-24T14:15:22Z", + "error": "string", + "resources": [ + { + "error": "string", + "kind": "instruction_file", + "size_bytes": 0, + "skill_description": "string", + "skill_name": "string", + "source": "string", + "status": "ok", + "tools": [ + { + "description": "string", + "name": "string" + } + ] + } + ] + }, + "created_at": "2019-08-24T14:15:22Z", + "diff_status": { + "additions": 0, + "approved": true, + "author_avatar_url": "string", + "author_login": "string", + "base_branch": "string", + "changed_files": 0, + "changes_requested": true, + "chat_id": "efc9fe20-a1e5-4a8c-9c48-f1b30c1e4f86", + "commits": 0, + "deletions": 0, + "head_branch": "string", + "pr_number": 0, + "pull_request_draft": true, + "pull_request_state": "string", + "pull_request_title": "string", + "refreshed_at": "2019-08-24T14:15:22Z", + "reviewer_count": 0, + "stale_at": "2019-08-24T14:15:22Z", + "url": "string" + }, + "files": [ + { + "created_at": "2019-08-24T14:15:22Z", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "mime_type": "string", + "name": "string", + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", + "size_bytes": 0 + } + ], + "has_unread": true, + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "labels": { + "property1": "string", + "property2": "string" + }, + "last_error": { + "detail": "string", + "kind": "generic", + "message": "string", + "provider": "string", + "retryable": true, + "status_code": 0 + }, + "last_model_config_id": "30ebb95f-c255-4759-9429-89aa4ec1554c", + "last_reasoning_effort": "string", + "last_turn_summary": "string", + "mcp_server_ids": [ + "497f6eca-6276-4993-bfeb-53cbbbba6f08" + ], + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", + "owner_name": "string", + "owner_username": "string", + "parent_chat_id": "c3609ee6-3b11-4a93-b9ae-e4fabcc99359", + "pin_order": 0, + "plan_mode": "plan", + "queued_for_capacity": true, + "root_chat_id": "2898031c-fdce-4e3e-8c53-4481dd42fcd7", + "shared": true, + "status": "waiting", + "summary": "string", + "title": "string", + "updated_at": "2019-08-24T14:15:22Z", + "warnings": [ + "string" + ], + "workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9" + } + ], + "client_type": "ui", + "context": { + "dirty": true, + "dirty_since": "2019-08-24T14:15:22Z", + "error": "string", + "resources": [ + { + "error": "string", + "kind": "instruction_file", + "size_bytes": 0, + "skill_description": "string", + "skill_name": "string", + "source": "string", + "status": "ok", + "tools": [ + { + "description": "string", + "name": "string" + } + ] + } + ] + }, + "created_at": "2019-08-24T14:15:22Z", + "diff_status": { + "additions": 0, + "approved": true, + "author_avatar_url": "string", + "author_login": "string", + "base_branch": "string", + "changed_files": 0, + "changes_requested": true, + "chat_id": "efc9fe20-a1e5-4a8c-9c48-f1b30c1e4f86", + "commits": 0, + "deletions": 0, + "head_branch": "string", + "pr_number": 0, + "pull_request_draft": true, + "pull_request_state": "string", + "pull_request_title": "string", + "refreshed_at": "2019-08-24T14:15:22Z", + "reviewer_count": 0, + "stale_at": "2019-08-24T14:15:22Z", + "url": "string" + }, + "files": [ + { + "created_at": "2019-08-24T14:15:22Z", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "mime_type": "string", + "name": "string", + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", + "size_bytes": 0 + } + ], + "has_unread": true, + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "labels": { + "property1": "string", + "property2": "string" + }, + "last_error": { + "detail": "string", + "kind": "generic", + "message": "string", + "provider": "string", + "retryable": true, + "status_code": 0 + }, + "last_model_config_id": "30ebb95f-c255-4759-9429-89aa4ec1554c", + "last_reasoning_effort": "string", + "last_turn_summary": "string", + "mcp_server_ids": [ + "497f6eca-6276-4993-bfeb-53cbbbba6f08" + ], + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", + "owner_name": "string", + "owner_username": "string", + "parent_chat_id": "c3609ee6-3b11-4a93-b9ae-e4fabcc99359", + "pin_order": 0, + "plan_mode": "plan", + "queued_for_capacity": true, + "root_chat_id": "2898031c-fdce-4e3e-8c53-4481dd42fcd7", + "shared": true, + "status": "waiting", + "summary": "string", + "title": "string", + "updated_at": "2019-08-24T14:15:22Z", + "warnings": [ + "string" + ], + "workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +|--------|---------------------------------------------------------|-------------|------------------------------------------| +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.Chat](schemas.md#codersdkchat) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Update chat + +### Code samples + +```sh +# Example request using curl +curl -X PATCH http://coder-server:8080/api/v2/chats/{chat} \ + -H 'Content-Type: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`PATCH /api/v2/chats/{chat}` + +> Body parameter + +```json +{ + "archived": true, + "labels": { + "property1": "string", + "property2": "string" + }, + "pin_order": 0, + "plan_mode": "plan", + "title": "string", + "workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9" +} +``` + +### Parameters + +| Name | In | Type | Required | Description | +|--------|------|--------------------------------------------------------------------|----------|---------------------| +| `chat` | path | string(uuid) | true | Chat ID | +| `body` | body | [codersdk.UpdateChatRequest](schemas.md#codersdkupdatechatrequest) | true | Update chat request | + +### 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 ACLs + +### Code samples + +```sh +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/chats/{chat}/acl \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /api/v2/chats/{chat}/acl` + +### Parameters + +| Name | In | Type | Required | Description | +|--------|------|--------------|----------|-------------| +| `chat` | path | string(uuid) | true | Chat ID | + +### Example responses + +> 200 Response + +```json +{ + "groups": [ + { + "avatar_url": "http://example.com", + "display_name": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "members": [ + { + "avatar_url": "http://example.com", + "created_at": "2019-08-24T14:15:22Z", + "email": "user@example.com", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "is_service_account": true, + "last_seen_at": "2019-08-24T14:15:22Z", + "login_type": "", + "name": "string", + "status": "active", + "theme_preference": "string", + "updated_at": "2019-08-24T14:15:22Z", + "username": "string" + } + ], + "name": "string", + "organization_display_name": "string", + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "organization_name": "string", + "quota_allowance": 0, + "role": "read", + "source": "user", + "total_member_count": 0 + } + ], + "users": [ + { + "avatar_url": "http://example.com", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "name": "string", + "role": "read", + "username": "string" + } + ] +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +|--------|---------------------------------------------------------|-------------|------------------------------------------------| +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.ChatACL](schemas.md#codersdkchatacl) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Update chat ACL + +### Code samples + +```sh +# Example request using curl +curl -X PATCH http://coder-server:8080/api/v2/chats/{chat}/acl \ + -H 'Content-Type: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`PATCH /api/v2/chats/{chat}/acl` + +> Body parameter + +```json +{ + "group_roles": { + "property1": "read", + "property2": "read" + }, + "user_roles": { + "property1": "read", + "property2": "read" + } +} +``` + +### Parameters + +| Name | In | Type | Required | Description | +|--------|------|------------------------------------------------------------|----------|-------------------------| +| `chat` | path | string(uuid) | true | Chat ID | +| `body` | body | [codersdk.UpdateChatACL](schemas.md#codersdkupdatechatacl) | true | Update chat ACL request | + +### 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). + +## Compact chat + +### Code samples + +```sh +# Example request using curl +curl -X POST http://coder-server:8080/api/v2/chats/{chat}/compact \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`POST /api/v2/chats/{chat}/compact` + +Requests a manual context compaction on an idle or errored +chat, clearing any stored error. The compaction runs +asynchronously through the chat worker and bypasses the +automatic usage threshold. + +### Parameters + +| Name | In | Type | Required | Description | +|--------|------|--------------|----------|-------------| +| `chat` | path | string(uuid) | true | Chat ID | + +### Example responses + +> 200 Response + +```json +{ + "agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e98978", + "archived": true, + "build_id": "bfb1f3fa-bf7b-43a5-9e0b-26cc050e44cb", + "children": [ + { + "agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e98978", + "archived": true, + "build_id": "bfb1f3fa-bf7b-43a5-9e0b-26cc050e44cb", + "children": [], + "client_type": "ui", + "context": { + "dirty": true, + "dirty_since": "2019-08-24T14:15:22Z", + "error": "string", + "resources": [ + { + "error": "string", + "kind": "instruction_file", + "size_bytes": 0, + "skill_description": "string", + "skill_name": "string", + "source": "string", + "status": "ok", + "tools": [ + { + "description": "string", + "name": "string" + } + ] + } + ] + }, + "created_at": "2019-08-24T14:15:22Z", + "diff_status": { + "additions": 0, + "approved": true, + "author_avatar_url": "string", + "author_login": "string", + "base_branch": "string", + "changed_files": 0, + "changes_requested": true, + "chat_id": "efc9fe20-a1e5-4a8c-9c48-f1b30c1e4f86", + "commits": 0, + "deletions": 0, + "head_branch": "string", + "pr_number": 0, + "pull_request_draft": true, + "pull_request_state": "string", + "pull_request_title": "string", + "refreshed_at": "2019-08-24T14:15:22Z", + "reviewer_count": 0, + "stale_at": "2019-08-24T14:15:22Z", + "url": "string" + }, + "files": [ + { + "created_at": "2019-08-24T14:15:22Z", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "mime_type": "string", + "name": "string", + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", + "size_bytes": 0 + } + ], + "has_unread": true, + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "labels": { + "property1": "string", + "property2": "string" + }, + "last_error": { + "detail": "string", + "kind": "generic", + "message": "string", + "provider": "string", + "retryable": true, + "status_code": 0 + }, + "last_model_config_id": "30ebb95f-c255-4759-9429-89aa4ec1554c", + "last_reasoning_effort": "string", + "last_turn_summary": "string", + "mcp_server_ids": [ + "497f6eca-6276-4993-bfeb-53cbbbba6f08" + ], + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", + "owner_name": "string", + "owner_username": "string", + "parent_chat_id": "c3609ee6-3b11-4a93-b9ae-e4fabcc99359", + "pin_order": 0, + "plan_mode": "plan", + "queued_for_capacity": true, + "root_chat_id": "2898031c-fdce-4e3e-8c53-4481dd42fcd7", + "shared": true, + "status": "waiting", + "summary": "string", + "title": "string", + "updated_at": "2019-08-24T14:15:22Z", + "warnings": [ + "string" + ], + "workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9" + } + ], + "client_type": "ui", + "context": { + "dirty": true, + "dirty_since": "2019-08-24T14:15:22Z", + "error": "string", + "resources": [ + { + "error": "string", + "kind": "instruction_file", + "size_bytes": 0, + "skill_description": "string", + "skill_name": "string", + "source": "string", + "status": "ok", + "tools": [ + { + "description": "string", + "name": "string" + } + ] + } + ] + }, + "created_at": "2019-08-24T14:15:22Z", + "diff_status": { + "additions": 0, + "approved": true, + "author_avatar_url": "string", + "author_login": "string", + "base_branch": "string", + "changed_files": 0, + "changes_requested": true, + "chat_id": "efc9fe20-a1e5-4a8c-9c48-f1b30c1e4f86", + "commits": 0, + "deletions": 0, + "head_branch": "string", + "pr_number": 0, + "pull_request_draft": true, + "pull_request_state": "string", + "pull_request_title": "string", + "refreshed_at": "2019-08-24T14:15:22Z", + "reviewer_count": 0, + "stale_at": "2019-08-24T14:15:22Z", + "url": "string" + }, + "files": [ + { + "created_at": "2019-08-24T14:15:22Z", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "mime_type": "string", + "name": "string", + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", + "size_bytes": 0 + } + ], + "has_unread": true, + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "labels": { + "property1": "string", + "property2": "string" + }, + "last_error": { + "detail": "string", + "kind": "generic", + "message": "string", + "provider": "string", + "retryable": true, + "status_code": 0 + }, + "last_model_config_id": "30ebb95f-c255-4759-9429-89aa4ec1554c", + "last_reasoning_effort": "string", + "last_turn_summary": "string", + "mcp_server_ids": [ + "497f6eca-6276-4993-bfeb-53cbbbba6f08" + ], + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", + "owner_name": "string", + "owner_username": "string", + "parent_chat_id": "c3609ee6-3b11-4a93-b9ae-e4fabcc99359", + "pin_order": 0, + "plan_mode": "plan", + "queued_for_capacity": true, + "root_chat_id": "2898031c-fdce-4e3e-8c53-4481dd42fcd7", + "shared": true, + "status": "waiting", + "summary": "string", + "title": "string", + "updated_at": "2019-08-24T14:15:22Z", + "warnings": [ + "string" + ], + "workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +|--------|---------------------------------------------------------|-------------|------------------------------------------| +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.Chat](schemas.md#codersdkchat) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Refresh chat context + +### Code samples + +```sh +# Example request using curl +curl -X PUT http://coder-server:8080/api/v2/chats/{chat}/context \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`PUT /api/v2/chats/{chat}/context` + +### Parameters + +| Name | In | Type | Required | Description | +|--------|------|--------------|----------|-------------| +| `chat` | path | string(uuid) | true | Chat ID | + +### Example responses + +> 200 Response + +```json +{ + "agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e98978", + "archived": true, + "build_id": "bfb1f3fa-bf7b-43a5-9e0b-26cc050e44cb", + "children": [ + { + "agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e98978", + "archived": true, + "build_id": "bfb1f3fa-bf7b-43a5-9e0b-26cc050e44cb", + "children": [], + "client_type": "ui", + "context": { + "dirty": true, + "dirty_since": "2019-08-24T14:15:22Z", + "error": "string", + "resources": [ + { + "error": "string", + "kind": "instruction_file", + "size_bytes": 0, + "skill_description": "string", + "skill_name": "string", + "source": "string", + "status": "ok", + "tools": [ + { + "description": "string", + "name": "string" + } + ] + } + ] + }, + "created_at": "2019-08-24T14:15:22Z", + "diff_status": { + "additions": 0, + "approved": true, + "author_avatar_url": "string", + "author_login": "string", + "base_branch": "string", + "changed_files": 0, + "changes_requested": true, + "chat_id": "efc9fe20-a1e5-4a8c-9c48-f1b30c1e4f86", + "commits": 0, + "deletions": 0, + "head_branch": "string", + "pr_number": 0, + "pull_request_draft": true, + "pull_request_state": "string", + "pull_request_title": "string", + "refreshed_at": "2019-08-24T14:15:22Z", + "reviewer_count": 0, + "stale_at": "2019-08-24T14:15:22Z", + "url": "string" + }, + "files": [ + { + "created_at": "2019-08-24T14:15:22Z", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "mime_type": "string", + "name": "string", + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", + "size_bytes": 0 + } + ], + "has_unread": true, + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "labels": { + "property1": "string", + "property2": "string" + }, + "last_error": { + "detail": "string", + "kind": "generic", + "message": "string", + "provider": "string", + "retryable": true, + "status_code": 0 + }, + "last_model_config_id": "30ebb95f-c255-4759-9429-89aa4ec1554c", + "last_reasoning_effort": "string", + "last_turn_summary": "string", + "mcp_server_ids": [ + "497f6eca-6276-4993-bfeb-53cbbbba6f08" + ], + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", + "owner_name": "string", + "owner_username": "string", + "parent_chat_id": "c3609ee6-3b11-4a93-b9ae-e4fabcc99359", + "pin_order": 0, + "plan_mode": "plan", + "queued_for_capacity": true, + "root_chat_id": "2898031c-fdce-4e3e-8c53-4481dd42fcd7", + "shared": true, + "status": "waiting", + "summary": "string", + "title": "string", + "updated_at": "2019-08-24T14:15:22Z", + "warnings": [ + "string" + ], + "workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9" + } + ], + "client_type": "ui", + "context": { + "dirty": true, + "dirty_since": "2019-08-24T14:15:22Z", + "error": "string", + "resources": [ + { + "error": "string", + "kind": "instruction_file", + "size_bytes": 0, + "skill_description": "string", + "skill_name": "string", + "source": "string", + "status": "ok", + "tools": [ + { + "description": "string", + "name": "string" + } + ] + } + ] + }, + "created_at": "2019-08-24T14:15:22Z", + "diff_status": { + "additions": 0, + "approved": true, + "author_avatar_url": "string", + "author_login": "string", + "base_branch": "string", + "changed_files": 0, + "changes_requested": true, + "chat_id": "efc9fe20-a1e5-4a8c-9c48-f1b30c1e4f86", + "commits": 0, + "deletions": 0, + "head_branch": "string", + "pr_number": 0, + "pull_request_draft": true, + "pull_request_state": "string", + "pull_request_title": "string", + "refreshed_at": "2019-08-24T14:15:22Z", + "reviewer_count": 0, + "stale_at": "2019-08-24T14:15:22Z", + "url": "string" + }, + "files": [ + { + "created_at": "2019-08-24T14:15:22Z", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "mime_type": "string", + "name": "string", + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", + "size_bytes": 0 + } + ], + "has_unread": true, + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "labels": { + "property1": "string", + "property2": "string" + }, + "last_error": { + "detail": "string", + "kind": "generic", + "message": "string", + "provider": "string", + "retryable": true, + "status_code": 0 + }, + "last_model_config_id": "30ebb95f-c255-4759-9429-89aa4ec1554c", + "last_reasoning_effort": "string", + "last_turn_summary": "string", + "mcp_server_ids": [ + "497f6eca-6276-4993-bfeb-53cbbbba6f08" + ], + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", + "owner_name": "string", + "owner_username": "string", + "parent_chat_id": "c3609ee6-3b11-4a93-b9ae-e4fabcc99359", + "pin_order": 0, + "plan_mode": "plan", + "queued_for_capacity": true, + "root_chat_id": "2898031c-fdce-4e3e-8c53-4481dd42fcd7", + "shared": true, + "status": "waiting", + "summary": "string", + "title": "string", + "updated_at": "2019-08-24T14:15:22Z", + "warnings": [ + "string" + ], + "workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +|--------|---------------------------------------------------------|-------------|------------------------------------------| +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.Chat](schemas.md#codersdkchat) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Get chat cost + +### Code samples + +```sh +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/chats/{chat}/cost \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`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 +returns that same total. + +Cost is derived from AI Gateway data, which is subject to its +own retention period, 60 days by default, configured +independently of chat retention. Spend for requests older than +that period is no longer reported, so a chat whose requests +have all been purged reports zero cost. + +### Parameters + +| Name | In | Type | Required | Description | +|--------|------|--------------|----------|-------------| +| `chat` | path | string(uuid) | true | Chat ID | + +### Example responses + +> 200 Response + +```json +{ + "chat_id": "efc9fe20-a1e5-4a8c-9c48-f1b30c1e4f86", + "request_count": 0, + "total_cost_micros": 0, + "unpriced_request_count": 0 +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +|--------|---------------------------------------------------------|-------------|--------------------------------------------------| +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.ChatCost](schemas.md#codersdkchatcost) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Get chat diff contents + +### Code samples + +```sh +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/chats/{chat}/diff \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /api/v2/chats/{chat}/diff` + +### Parameters + +| Name | In | Type | Required | Description | +|--------|------|--------------|----------|-------------| +| `chat` | path | string(uuid) | true | Chat ID | + +### Example responses + +> 200 Response + +```json +{ + "branch": "string", + "chat_id": "efc9fe20-a1e5-4a8c-9c48-f1b30c1e4f86", + "diff": "string", + "provider": "string", + "pull_request_url": "string", + "remote_origin": "string" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +|--------|---------------------------------------------------------|-------------|------------------------------------------------------------------| +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.ChatDiffContents](schemas.md#codersdkchatdiffcontents) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Interrupt chat + +### Code samples + +```sh +# Example request using curl +curl -X POST http://coder-server:8080/api/v2/chats/{chat}/interrupt \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`POST /api/v2/chats/{chat}/interrupt` + +### Parameters + +| Name | In | Type | Required | Description | +|--------|------|--------------|----------|-------------| +| `chat` | path | string(uuid) | true | Chat ID | + +### Example responses + +> 200 Response + +```json +{ + "agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e98978", + "archived": true, + "build_id": "bfb1f3fa-bf7b-43a5-9e0b-26cc050e44cb", + "children": [ + { + "agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e98978", + "archived": true, + "build_id": "bfb1f3fa-bf7b-43a5-9e0b-26cc050e44cb", + "children": [], + "client_type": "ui", + "context": { + "dirty": true, + "dirty_since": "2019-08-24T14:15:22Z", + "error": "string", + "resources": [ + { + "error": "string", + "kind": "instruction_file", + "size_bytes": 0, + "skill_description": "string", + "skill_name": "string", + "source": "string", + "status": "ok", + "tools": [ + { + "description": "string", + "name": "string" + } + ] + } + ] + }, + "created_at": "2019-08-24T14:15:22Z", + "diff_status": { + "additions": 0, + "approved": true, + "author_avatar_url": "string", + "author_login": "string", + "base_branch": "string", + "changed_files": 0, + "changes_requested": true, + "chat_id": "efc9fe20-a1e5-4a8c-9c48-f1b30c1e4f86", + "commits": 0, + "deletions": 0, + "head_branch": "string", + "pr_number": 0, + "pull_request_draft": true, + "pull_request_state": "string", + "pull_request_title": "string", + "refreshed_at": "2019-08-24T14:15:22Z", + "reviewer_count": 0, + "stale_at": "2019-08-24T14:15:22Z", + "url": "string" + }, + "files": [ + { + "created_at": "2019-08-24T14:15:22Z", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "mime_type": "string", + "name": "string", + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", + "size_bytes": 0 + } + ], + "has_unread": true, + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "labels": { + "property1": "string", + "property2": "string" + }, + "last_error": { + "detail": "string", + "kind": "generic", + "message": "string", + "provider": "string", + "retryable": true, + "status_code": 0 + }, + "last_model_config_id": "30ebb95f-c255-4759-9429-89aa4ec1554c", + "last_reasoning_effort": "string", + "last_turn_summary": "string", + "mcp_server_ids": [ + "497f6eca-6276-4993-bfeb-53cbbbba6f08" + ], + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", + "owner_name": "string", + "owner_username": "string", + "parent_chat_id": "c3609ee6-3b11-4a93-b9ae-e4fabcc99359", + "pin_order": 0, + "plan_mode": "plan", + "queued_for_capacity": true, + "root_chat_id": "2898031c-fdce-4e3e-8c53-4481dd42fcd7", + "shared": true, + "status": "waiting", + "summary": "string", + "title": "string", + "updated_at": "2019-08-24T14:15:22Z", + "warnings": [ + "string" + ], + "workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9" + } + ], + "client_type": "ui", + "context": { + "dirty": true, + "dirty_since": "2019-08-24T14:15:22Z", + "error": "string", + "resources": [ + { + "error": "string", + "kind": "instruction_file", + "size_bytes": 0, + "skill_description": "string", + "skill_name": "string", + "source": "string", + "status": "ok", + "tools": [ + { + "description": "string", + "name": "string" + } + ] + } + ] + }, + "created_at": "2019-08-24T14:15:22Z", + "diff_status": { + "additions": 0, + "approved": true, + "author_avatar_url": "string", + "author_login": "string", + "base_branch": "string", + "changed_files": 0, + "changes_requested": true, + "chat_id": "efc9fe20-a1e5-4a8c-9c48-f1b30c1e4f86", + "commits": 0, + "deletions": 0, + "head_branch": "string", + "pr_number": 0, + "pull_request_draft": true, + "pull_request_state": "string", + "pull_request_title": "string", + "refreshed_at": "2019-08-24T14:15:22Z", + "reviewer_count": 0, + "stale_at": "2019-08-24T14:15:22Z", + "url": "string" + }, + "files": [ + { + "created_at": "2019-08-24T14:15:22Z", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "mime_type": "string", + "name": "string", + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", + "size_bytes": 0 + } + ], + "has_unread": true, + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "labels": { + "property1": "string", + "property2": "string" + }, + "last_error": { + "detail": "string", + "kind": "generic", + "message": "string", + "provider": "string", + "retryable": true, + "status_code": 0 + }, + "last_model_config_id": "30ebb95f-c255-4759-9429-89aa4ec1554c", + "last_reasoning_effort": "string", + "last_turn_summary": "string", + "mcp_server_ids": [ + "497f6eca-6276-4993-bfeb-53cbbbba6f08" + ], + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", + "owner_name": "string", + "owner_username": "string", + "parent_chat_id": "c3609ee6-3b11-4a93-b9ae-e4fabcc99359", + "pin_order": 0, + "plan_mode": "plan", + "queued_for_capacity": true, + "root_chat_id": "2898031c-fdce-4e3e-8c53-4481dd42fcd7", + "shared": true, + "status": "waiting", + "summary": "string", + "title": "string", + "updated_at": "2019-08-24T14:15:22Z", + "warnings": [ + "string" + ], + "workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +|--------|---------------------------------------------------------|-------------|------------------------------------------| +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.Chat](schemas.md#codersdkchat) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## List chat messages + +### Code samples + +```sh +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/chats/{chat}/messages \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /api/v2/chats/{chat}/messages` + +### Parameters + +| Name | In | Type | Required | Description | +|-------------|-------|--------------|----------|--------------------------------------| +| `chat` | path | string(uuid) | true | Chat ID | +| `before_id` | query | integer | false | Return messages with id < before_id | +| `after_id` | query | integer | false | Return messages with id > after_id | +| `limit` | query | integer | false | Page size, 1 to 200. Defaults to 50. | + +### Example responses + +> 200 Response + +```json +{ + "has_more": true, + "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", + "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 + } + } + ], + "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" + } + ] +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +|--------|---------------------------------------------------------|-------------|--------------------------------------------------------------------------| +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.ChatMessagesResponse](schemas.md#codersdkchatmessagesresponse) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Send chat message + +### Code samples + +```sh +# Example request using curl +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/v2/chats/{chat}/messages` + +> Body parameter + +```json +{ + "busy_behavior": "queue", + "content": [ + { + "content": "string", + "end_line": 0, + "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", + "file_name": "string", + "start_line": 0, + "text": "string", + "type": "text" + } + ], + "mcp_server_ids": [ + "497f6eca-6276-4993-bfeb-53cbbbba6f08" + ], + "model_config_id": "f5fb4d91-62ca-4377-9ee6-5d43ba00d205", + "plan_mode": "plan", + "reasoning_effort": "string" +} +``` + +### Parameters + +| Name | In | Type | Required | Description | +|--------|------|----------------------------------------------------------------------------------|----------|-----------------------------| +| `chat` | path | string(uuid) | true | Chat ID | +| `body` | body | [codersdk.CreateChatMessageRequest](schemas.md#codersdkcreatechatmessagerequest) | true | Create chat message request | + +### Example responses + +> 200 Response + +```json +{ + "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 + } + }, + "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", + "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 + } + } + ], + "queued": true, + "queued_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", + "id": 0, + "model_config_id": "f5fb4d91-62ca-4377-9ee6-5d43ba00d205" + }, + "warnings": [ + "string" + ] +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +|--------|---------------------------------------------------------|-------------|------------------------------------------------------------------------------------| +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.CreateChatMessageResponse](schemas.md#codersdkcreatechatmessageresponse) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Edit chat message + +### Code samples + +```sh +# Example request using curl +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/v2/chats/{chat}/messages/{message}` + +> Body parameter + +```json +{ + "content": [ + { + "content": "string", + "end_line": 0, + "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", + "file_name": "string", + "start_line": 0, + "text": "string", + "type": "text" + } + ], + "mcp_server_ids": [ + "497f6eca-6276-4993-bfeb-53cbbbba6f08" + ], + "model_config_id": "f5fb4d91-62ca-4377-9ee6-5d43ba00d205", + "reasoning_effort": "string" +} +``` + +### Parameters + +| Name | In | Type | Required | Description | +|-----------|------|------------------------------------------------------------------------------|----------|---------------------------| +| `chat` | path | string(uuid) | true | Chat ID | +| `message` | path | integer | true | Message ID | +| `body` | body | [codersdk.EditChatMessageRequest](schemas.md#codersdkeditchatmessagerequest) | true | Edit chat message request | + +### Example responses + +> 200 Response + +```json +{ + "deleted_message_ids": [ + 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 + } + }, + "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", + "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 + } + } + ], + "warnings": [ + "string" + ] +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +|--------|---------------------------------------------------------|-------------|--------------------------------------------------------------------------------| +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.EditChatMessageResponse](schemas.md#codersdkeditchatmessageresponse) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## List chat user prompts + +### Code samples + +```sh +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/chats/{chat}/prompts \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`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 +were authored. Used by the composer to power the up/down +arrow prompt-history cycle without paging through every +message in the chat. + +### Parameters + +| Name | In | Type | Required | Description | +|---------|-------|--------------|----------|-----------------------------------------------------------------------------| +| `chat` | path | string(uuid) | true | Chat ID | +| `limit` | query | integer | false | Page size, 0 to 2000. 0 (the default) means the server-side default of 500. | + +### Example responses + +> 200 Response + +```json +{ + "prompts": [ + { + "id": 0, + "text": "string" + } + ] +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +|--------|---------------------------------------------------------|-------------|------------------------------------------------------------------------| +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.ChatPromptsResponse](schemas.md#codersdkchatpromptsresponse) | + +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 | true | Chat ID | +| `queuedMessage` | path | string | 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 | true | Chat ID | +| `queuedMessage` | path | string | 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/v2/chats/{chat}/reconcile-invalid \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`POST /api/v2/chats/{chat}/reconcile-invalid` + +### Parameters + +| Name | In | Type | Required | Description | +|--------|------|--------------|----------|-------------| +| `chat` | path | string(uuid) | true | Chat ID | + +### Example responses + +> 200 Response + +```json +{ + "agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e98978", + "archived": true, + "build_id": "bfb1f3fa-bf7b-43a5-9e0b-26cc050e44cb", + "children": [ + { + "agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e98978", + "archived": true, + "build_id": "bfb1f3fa-bf7b-43a5-9e0b-26cc050e44cb", + "children": [], + "client_type": "ui", + "context": { + "dirty": true, + "dirty_since": "2019-08-24T14:15:22Z", + "error": "string", + "resources": [ + { + "error": "string", + "kind": "instruction_file", + "size_bytes": 0, + "skill_description": "string", + "skill_name": "string", + "source": "string", + "status": "ok", + "tools": [ + { + "description": "string", + "name": "string" + } + ] + } + ] + }, + "created_at": "2019-08-24T14:15:22Z", + "diff_status": { + "additions": 0, + "approved": true, + "author_avatar_url": "string", + "author_login": "string", + "base_branch": "string", + "changed_files": 0, + "changes_requested": true, + "chat_id": "efc9fe20-a1e5-4a8c-9c48-f1b30c1e4f86", + "commits": 0, + "deletions": 0, + "head_branch": "string", + "pr_number": 0, + "pull_request_draft": true, + "pull_request_state": "string", + "pull_request_title": "string", + "refreshed_at": "2019-08-24T14:15:22Z", + "reviewer_count": 0, + "stale_at": "2019-08-24T14:15:22Z", + "url": "string" + }, + "files": [ + { + "created_at": "2019-08-24T14:15:22Z", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "mime_type": "string", + "name": "string", + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", + "size_bytes": 0 + } + ], + "has_unread": true, + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "labels": { + "property1": "string", + "property2": "string" + }, + "last_error": { + "detail": "string", + "kind": "generic", + "message": "string", + "provider": "string", + "retryable": true, + "status_code": 0 + }, + "last_model_config_id": "30ebb95f-c255-4759-9429-89aa4ec1554c", + "last_reasoning_effort": "string", + "last_turn_summary": "string", + "mcp_server_ids": [ + "497f6eca-6276-4993-bfeb-53cbbbba6f08" + ], + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", + "owner_name": "string", + "owner_username": "string", + "parent_chat_id": "c3609ee6-3b11-4a93-b9ae-e4fabcc99359", + "pin_order": 0, + "plan_mode": "plan", + "queued_for_capacity": true, + "root_chat_id": "2898031c-fdce-4e3e-8c53-4481dd42fcd7", + "shared": true, + "status": "waiting", + "summary": "string", + "title": "string", + "updated_at": "2019-08-24T14:15:22Z", + "warnings": [ + "string" + ], + "workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9" + } + ], + "client_type": "ui", + "context": { + "dirty": true, + "dirty_since": "2019-08-24T14:15:22Z", + "error": "string", + "resources": [ + { + "error": "string", + "kind": "instruction_file", + "size_bytes": 0, + "skill_description": "string", + "skill_name": "string", + "source": "string", + "status": "ok", + "tools": [ + { + "description": "string", + "name": "string" + } + ] + } + ] + }, + "created_at": "2019-08-24T14:15:22Z", + "diff_status": { + "additions": 0, + "approved": true, + "author_avatar_url": "string", + "author_login": "string", + "base_branch": "string", + "changed_files": 0, + "changes_requested": true, + "chat_id": "efc9fe20-a1e5-4a8c-9c48-f1b30c1e4f86", + "commits": 0, + "deletions": 0, + "head_branch": "string", + "pr_number": 0, + "pull_request_draft": true, + "pull_request_state": "string", + "pull_request_title": "string", + "refreshed_at": "2019-08-24T14:15:22Z", + "reviewer_count": 0, + "stale_at": "2019-08-24T14:15:22Z", + "url": "string" + }, + "files": [ + { + "created_at": "2019-08-24T14:15:22Z", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "mime_type": "string", + "name": "string", + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", + "size_bytes": 0 + } + ], + "has_unread": true, + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "labels": { + "property1": "string", + "property2": "string" + }, + "last_error": { + "detail": "string", + "kind": "generic", + "message": "string", + "provider": "string", + "retryable": true, + "status_code": 0 + }, + "last_model_config_id": "30ebb95f-c255-4759-9429-89aa4ec1554c", + "last_reasoning_effort": "string", + "last_turn_summary": "string", + "mcp_server_ids": [ + "497f6eca-6276-4993-bfeb-53cbbbba6f08" + ], + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", + "owner_name": "string", + "owner_username": "string", + "parent_chat_id": "c3609ee6-3b11-4a93-b9ae-e4fabcc99359", + "pin_order": 0, + "plan_mode": "plan", + "queued_for_capacity": true, + "root_chat_id": "2898031c-fdce-4e3e-8c53-4481dd42fcd7", + "shared": true, + "status": "waiting", + "summary": "string", + "title": "string", + "updated_at": "2019-08-24T14:15:22Z", + "warnings": [ + "string" + ], + "workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +|--------|---------------------------------------------------------|-------------|------------------------------------------| +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.Chat](schemas.md#codersdkchat) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Stream chat events via WebSockets + +### Code samples + +```sh +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/chats/{chat}/stream \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /api/v2/chats/{chat}/stream` + +### Parameters + +| Name | In | Type | Required | Description | +|--------|------|--------------|----------|-------------| +| `chat` | path | string(uuid) | true | Chat ID | + +### 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" + }, + "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) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Watch chat workspace git state via WebSockets + +### Code samples + +```sh +# Example request using curl +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/v2/chats/{chat}/stream/git` + +### Parameters + +| Name | In | Type | Required | Description | +|--------|------|--------------|----------|-------------| +| `chat` | path | string(uuid) | true | Chat ID | + +### Example responses + +> 200 Response + +```json +{ + "message": "string", + "repositories": [ + { + "branch": "string", + "remote_origin": "string", + "removed": true, + "repo_root": "string", + "unified_diff": "string" + } + ], + "scanned_at": "2019-08-24T14:15:22Z", + "type": "changes" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +|--------|---------------------------------------------------------|-------------|----------------------------------------------------------------------------------------------| +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.WorkspaceAgentGitServerMessage](schemas.md#codersdkworkspaceagentgitservermessage) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Stream chat parts via WebSockets + +### Code samples + +```sh +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/chats/{chat}/stream/parts \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /api/v2/chats/{chat}/stream/parts` + +### Parameters + +| Name | In | Type | Required | Description | +|--------|------|--------------|----------|-------------| +| `chat` | path | string(uuid) | true | Chat ID | + +### 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 ], - "end_line": 0, - "file_id": { - "uuid": "string", - "valid": true + "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" + }, + "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) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Propose chat title + +### Code samples + +```sh +# Example request using curl +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/v2/chats/{chat}/title/propose` + +### Parameters + +| Name | In | Type | Required | Description | +|--------|------|--------|----------|-------------| +| `chat` | path | string | true | Chat ID | + +### Example responses + +> 200 Response + +```json +{ + "title": "string" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +|--------|---------------------------------------------------------|-------------|----------------------------------------------------------------------------------| +| 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). + +## Regenerate chat title + +### Code samples + +```sh +# Example request using curl +curl -X POST http://coder-server:8080/api/v2/chats/{chat}/title/regenerate \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`POST /api/v2/chats/{chat}/title/regenerate` + +### Parameters + +| Name | In | Type | Required | Description | +|--------|------|--------------|----------|-------------| +| `chat` | path | string(uuid) | true | Chat ID | + +### Example responses + +> 200 Response + +```json +{ + "agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e98978", + "archived": true, + "build_id": "bfb1f3fa-bf7b-43a5-9e0b-26cc050e44cb", + "children": [ + { + "agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e98978", + "archived": true, + "build_id": "bfb1f3fa-bf7b-43a5-9e0b-26cc050e44cb", + "children": [], + "client_type": "ui", + "context": { + "dirty": true, + "dirty_since": "2019-08-24T14:15:22Z", + "error": "string", + "resources": [ + { + "error": "string", + "kind": "instruction_file", + "size_bytes": 0, + "skill_description": "string", + "skill_name": "string", + "source": "string", + "status": "ok", + "tools": [ + { + "description": "string", + "name": "string" + } + ] + } + ] + }, + "created_at": "2019-08-24T14:15:22Z", + "diff_status": { + "additions": 0, + "approved": true, + "author_avatar_url": "string", + "author_login": "string", + "base_branch": "string", + "changed_files": 0, + "changes_requested": true, + "chat_id": "efc9fe20-a1e5-4a8c-9c48-f1b30c1e4f86", + "commits": 0, + "deletions": 0, + "head_branch": "string", + "pr_number": 0, + "pull_request_draft": true, + "pull_request_state": "string", + "pull_request_title": "string", + "refreshed_at": "2019-08-24T14:15:22Z", + "reviewer_count": 0, + "stale_at": "2019-08-24T14:15:22Z", + "url": "string" + }, + "files": [ + { + "created_at": "2019-08-24T14:15:22Z", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "mime_type": "string", + "name": "string", + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", + "size_bytes": 0 + } + ], + "has_unread": true, + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "labels": { + "property1": "string", + "property2": "string" + }, + "last_error": { + "detail": "string", + "kind": "generic", + "message": "string", + "provider": "string", + "retryable": true, + "status_code": 0 + }, + "last_model_config_id": "30ebb95f-c255-4759-9429-89aa4ec1554c", + "last_reasoning_effort": "string", + "last_turn_summary": "string", + "mcp_server_ids": [ + "497f6eca-6276-4993-bfeb-53cbbbba6f08" + ], + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", + "owner_name": "string", + "owner_username": "string", + "parent_chat_id": "c3609ee6-3b11-4a93-b9ae-e4fabcc99359", + "pin_order": 0, + "plan_mode": "plan", + "queued_for_capacity": true, + "root_chat_id": "2898031c-fdce-4e3e-8c53-4481dd42fcd7", + "shared": true, + "status": "waiting", + "summary": "string", + "title": "string", + "updated_at": "2019-08-24T14:15:22Z", + "warnings": [ + "string" + ], + "workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9" + } + ], + "client_type": "ui", + "context": { + "dirty": true, + "dirty_since": "2019-08-24T14:15:22Z", + "error": "string", + "resources": [ + { + "error": "string", + "kind": "instruction_file", + "size_bytes": 0, + "skill_description": "string", + "skill_name": "string", + "source": "string", + "status": "ok", + "tools": [ + { + "description": "string", + "name": "string" + } + ] + } + ] + }, + "created_at": "2019-08-24T14:15:22Z", + "diff_status": { + "additions": 0, + "approved": true, + "author_avatar_url": "string", + "author_login": "string", + "base_branch": "string", + "changed_files": 0, + "changes_requested": true, + "chat_id": "efc9fe20-a1e5-4a8c-9c48-f1b30c1e4f86", + "commits": 0, + "deletions": 0, + "head_branch": "string", + "pr_number": 0, + "pull_request_draft": true, + "pull_request_state": "string", + "pull_request_title": "string", + "refreshed_at": "2019-08-24T14:15:22Z", + "reviewer_count": 0, + "stale_at": "2019-08-24T14:15:22Z", + "url": "string" + }, + "files": [ + { + "created_at": "2019-08-24T14:15:22Z", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "mime_type": "string", + "name": "string", + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", + "size_bytes": 0 + } + ], + "has_unread": true, + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "labels": { + "property1": "string", + "property2": "string" + }, + "last_error": { + "detail": "string", + "kind": "generic", + "message": "string", + "provider": "string", + "retryable": true, + "status_code": 0 + }, + "last_model_config_id": "30ebb95f-c255-4759-9429-89aa4ec1554c", + "last_reasoning_effort": "string", + "last_turn_summary": "string", + "mcp_server_ids": [ + "497f6eca-6276-4993-bfeb-53cbbbba6f08" + ], + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", + "owner_name": "string", + "owner_username": "string", + "parent_chat_id": "c3609ee6-3b11-4a93-b9ae-e4fabcc99359", + "pin_order": 0, + "plan_mode": "plan", + "queued_for_capacity": true, + "root_chat_id": "2898031c-fdce-4e3e-8c53-4481dd42fcd7", + "shared": true, + "status": "waiting", + "summary": "string", + "title": "string", + "updated_at": "2019-08-24T14:15:22Z", + "warnings": [ + "string" + ], + "workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +|--------|---------------------------------------------------------|-------------|------------------------------------------| +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.Chat](schemas.md#codersdkchat) | + +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 | 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 organization chat model overrides + +### Code samples + +```sh +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/chats/model-overrides \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /api/v2/organizations/{organization}/chats/model-overrides` + +### Parameters + +| Name | In | Type | Required | Description | +|----------------|------|--------|----------|-------------------------| +| `organization` | path | string | true | Organization name or ID | + +### Example responses + +> 200 Response + +```json +{ + "overrides": [ + { + "context": "general", + "model_config_id": "string", + "reasoning_effort": "string" + } + ] +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +|--------|---------------------------------------------------------|-------------|--------------------------------------------------------------------------------------| +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.ChatModelOverridesResponse](schemas.md#codersdkchatmodeloverridesresponse) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Update organization chat model override + +### Code samples + +```sh +# Example request using curl +curl -X PUT http://coder-server:8080/api/v2/organizations/{organization}/chats/model-overrides/{context} \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`PUT /api/v2/organizations/{organization}/chats/model-overrides/{context}` + +> Body parameter + +```json +{ + "model_config_id": "string", + "reasoning_effort": "string" +} +``` + +### Parameters + +| Name | In | Type | Required | Description | +|----------------|------|----------------------------------------------------------------------------------------------|----------|-------------------------| +| `organization` | path | string | true | Organization name or ID | +| `context` | path | string | true | Override context | +| `body` | body | [codersdk.UpdateChatModelOverrideRequest](schemas.md#codersdkupdatechatmodeloverriderequest) | true | Model override | + +#### Enumerated Values + +| Parameter | Value(s) | +|-----------|-------------------------------------------------------------------| +| `context` | `advisor`, `compaction`, `explore`, `general`, `title_generation` | + +### Example responses + +> 200 Response + +```json +{ + "context": "general", + "model_config_id": "string", + "reasoning_effort": "string" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +|--------|---------------------------------------------------------|-------------|------------------------------------------------------------------------------------| +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.ChatModelOverrideResponse](schemas.md#codersdkchatmodeloverrideresponse) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## List AI models and provider descriptors in an organization + +### Code samples + +```sh +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/chats/models \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /api/v2/organizations/{organization}/chats/models` + +### Parameters + +| Name | In | Type | Required | Description | +|----------------|------|--------|----------|-------------------------| +| `organization` | path | string | true | Organization name or ID | + +### Example responses + +> 200 Response + +```json +{ + "models": [ + { + "ai_provider_id": "5a3b8ff9-20e7-4c37-ba1a-5b433e355819", + "compression_threshold": 0, + "context_limit": 0, + "created_at": "2019-08-24T14:15:22Z", + "display_name": "string", + "enabled": true, + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "is_default": true, + "model": "string", + "model_config": { + "frequency_penalty": 0, + "max_output_tokens": 0, + "openai_config": { + "use_responses_api": true + }, + "presence_penalty": 0, + "provider_options": { + "anthropic": { + "allowed_domains": [ + "string" + ], + "blocked_domains": [ + "string" + ], + "context_1m_enabled": true, + "disable_parallel_tool_use": true, + "send_reasoning": true, + "thinking": { + "budget_tokens": 0 + }, + "thinking_display": "string", + "web_search_enabled": true + }, + "google": { + "cached_content": "string", + "safety_settings": [ + { + "category": "string", + "threshold": "string" + } + ], + "thinking_config": { + "include_thoughts": true, + "thinking_budget": 0, + "thinking_level": "string" + }, + "threshold": "string", + "web_search_enabled": true + }, + "openai": { + "allowed_domains": [ + "string" + ], + "include": [ + "string" + ], + "instructions": "string", + "log_probs": true, + "logit_bias": { + "property1": 0, + "property2": 0 + }, + "max_completion_tokens": 0, + "max_tool_calls": 0, + "metadata": { + "property1": null, + "property2": null + }, + "parallel_tool_calls": true, + "prediction": { + "property1": null, + "property2": null + }, + "prompt_cache_key": "string", + "reasoning_summary": "string", + "safety_identifier": "string", + "search_context_size": "string", + "service_tier": "string", + "store": true, + "strict_json_schema": true, + "structured_outputs": true, + "text_verbosity": "string", + "top_log_probs": 0, + "user": "string", + "web_search_enabled": true + }, + "openaicompat": { + "user": "string" + }, + "openrouter": { + "extra_body": { + "property1": null, + "property2": null + }, + "include_usage": true, + "log_probs": true, + "logit_bias": { + "property1": 0, + "property2": 0 + }, + "parallel_tool_calls": true, + "provider": { + "allow_fallbacks": true, + "data_collection": "string", + "ignore": [ + "string" + ], + "only": [ + "string" + ], + "order": [ + "string" + ], + "quantizations": [ + "string" + ], + "require_parameters": true, + "sort": "string" + }, + "reasoning": { + "enabled": true, + "exclude": true, + "max_tokens": 0 + }, + "user": "string" + }, + "vercel": { + "extra_body": { + "property1": null, + "property2": null + }, + "logit_bias": { + "property1": 0, + "property2": 0 + }, + "logprobs": true, + "parallel_tool_calls": true, + "providerOptions": { + "models": [ + "string" + ], + "order": [ + "string" + ] + }, + "reasoning": { + "enabled": true, + "exclude": true, + "max_tokens": 0 + }, + "top_logprobs": 0, + "user": "string" + } + }, + "reasoning_effort": { + "default": "string", + "max": "string" + }, + "temperature": 0, + "top_k": 0, + "top_p": 0 + }, + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "reasoning_efforts": [ + "string" + ], + "updated_at": "2019-08-24T14:15:22Z" + } + ], + "providers": [ + { + "allow_user_api_key": true, + "available": true, + "display_name": "string", + "enabled": true, + "has_api_key": true, + "has_effective_api_key": true, + "has_user_api_key": true, + "icon": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "type": "string", + "unavailable_reason": "missing_api_key" + } + ], + "unsupported_providers": [ + { + "display_name": "string", + "provider": "string" + } + ] +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +|--------|---------------------------------------------------------|-------------|----------------------------------------------------------------------------------------------| +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.OrganizationChatModelsResponse](schemas.md#codersdkorganizationchatmodelsresponse) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Create an AI model in an organization + +### Code samples + +```sh +# Example request using curl +curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/chats/models \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`POST /api/v2/organizations/{organization}/chats/models` + +> Body parameter + +```json +{ + "ai_provider_id": "5a3b8ff9-20e7-4c37-ba1a-5b433e355819", + "compression_threshold": 0, + "context_limit": 0, + "display_name": "string", + "enabled": true, + "is_default": true, + "model": "string", + "model_config": { + "frequency_penalty": 0, + "max_output_tokens": 0, + "openai_config": { + "use_responses_api": true + }, + "presence_penalty": 0, + "provider_options": { + "anthropic": { + "allowed_domains": [ + "string" + ], + "blocked_domains": [ + "string" + ], + "context_1m_enabled": true, + "disable_parallel_tool_use": true, + "send_reasoning": true, + "thinking": { + "budget_tokens": 0 + }, + "thinking_display": "string", + "web_search_enabled": true + }, + "google": { + "cached_content": "string", + "safety_settings": [ + { + "category": "string", + "threshold": "string" + } + ], + "thinking_config": { + "include_thoughts": true, + "thinking_budget": 0, + "thinking_level": "string" + }, + "threshold": "string", + "web_search_enabled": true + }, + "openai": { + "allowed_domains": [ + "string" + ], + "include": [ + "string" + ], + "instructions": "string", + "log_probs": true, + "logit_bias": { + "property1": 0, + "property2": 0 + }, + "max_completion_tokens": 0, + "max_tool_calls": 0, + "metadata": { + "property1": null, + "property2": null + }, + "parallel_tool_calls": true, + "prediction": { + "property1": null, + "property2": null + }, + "prompt_cache_key": "string", + "reasoning_summary": "string", + "safety_identifier": "string", + "search_context_size": "string", + "service_tier": "string", + "store": true, + "strict_json_schema": true, + "structured_outputs": true, + "text_verbosity": "string", + "top_log_probs": 0, + "user": "string", + "web_search_enabled": true + }, + "openaicompat": { + "user": "string" + }, + "openrouter": { + "extra_body": { + "property1": null, + "property2": null + }, + "include_usage": true, + "log_probs": true, + "logit_bias": { + "property1": 0, + "property2": 0 + }, + "parallel_tool_calls": true, + "provider": { + "allow_fallbacks": true, + "data_collection": "string", + "ignore": [ + "string" + ], + "only": [ + "string" + ], + "order": [ + "string" + ], + "quantizations": [ + "string" + ], + "require_parameters": true, + "sort": "string" + }, + "reasoning": { + "enabled": true, + "exclude": true, + "max_tokens": 0 + }, + "user": "string" + }, + "vercel": { + "extra_body": { + "property1": null, + "property2": null + }, + "logit_bias": { + "property1": 0, + "property2": 0 + }, + "logprobs": true, + "parallel_tool_calls": true, + "providerOptions": { + "models": [ + "string" + ], + "order": [ + "string" + ] + }, + "reasoning": { + "enabled": true, + "exclude": true, + "max_tokens": 0 + }, + "top_logprobs": 0, + "user": "string" + } + }, + "reasoning_effort": { + "default": "string", + "max": "string" + }, + "temperature": 0, + "top_k": 0, + "top_p": 0 + } +} +``` + +### Parameters + +| Name | In | Type | Required | Description | +|----------------|------|------------------------------------------------------------------------------|----------|-------------------------| +| `organization` | path | string | true | Organization name or ID | +| `body` | body | [codersdk.CreateChatModelRequest](schemas.md#codersdkcreatechatmodelrequest) | true | Model | + +### Example responses + +> 201 Response + +```json +{ + "ai_provider_id": "5a3b8ff9-20e7-4c37-ba1a-5b433e355819", + "compression_threshold": 0, + "context_limit": 0, + "created_at": "2019-08-24T14:15:22Z", + "display_name": "string", + "enabled": true, + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "is_default": true, + "model": "string", + "model_config": { + "frequency_penalty": 0, + "max_output_tokens": 0, + "openai_config": { + "use_responses_api": true + }, + "presence_penalty": 0, + "provider_options": { + "anthropic": { + "allowed_domains": [ + "string" + ], + "blocked_domains": [ + "string" + ], + "context_1m_enabled": true, + "disable_parallel_tool_use": true, + "send_reasoning": true, + "thinking": { + "budget_tokens": 0 + }, + "thinking_display": "string", + "web_search_enabled": true + }, + "google": { + "cached_content": "string", + "safety_settings": [ + { + "category": "string", + "threshold": "string" + } + ], + "thinking_config": { + "include_thoughts": true, + "thinking_budget": 0, + "thinking_level": "string" + }, + "threshold": "string", + "web_search_enabled": true + }, + "openai": { + "allowed_domains": [ + "string" + ], + "include": [ + "string" + ], + "instructions": "string", + "log_probs": true, + "logit_bias": { + "property1": 0, + "property2": 0 + }, + "max_completion_tokens": 0, + "max_tool_calls": 0, + "metadata": { + "property1": null, + "property2": null + }, + "parallel_tool_calls": true, + "prediction": { + "property1": null, + "property2": null + }, + "prompt_cache_key": "string", + "reasoning_summary": "string", + "safety_identifier": "string", + "search_context_size": "string", + "service_tier": "string", + "store": true, + "strict_json_schema": true, + "structured_outputs": true, + "text_verbosity": "string", + "top_log_probs": 0, + "user": "string", + "web_search_enabled": true + }, + "openaicompat": { + "user": "string" + }, + "openrouter": { + "extra_body": { + "property1": null, + "property2": null + }, + "include_usage": true, + "log_probs": true, + "logit_bias": { + "property1": 0, + "property2": 0 + }, + "parallel_tool_calls": true, + "provider": { + "allow_fallbacks": true, + "data_collection": "string", + "ignore": [ + "string" + ], + "only": [ + "string" + ], + "order": [ + "string" + ], + "quantizations": [ + "string" + ], + "require_parameters": true, + "sort": "string" + }, + "reasoning": { + "enabled": true, + "exclude": true, + "max_tokens": 0 + }, + "user": "string" + }, + "vercel": { + "extra_body": { + "property1": null, + "property2": null + }, + "logit_bias": { + "property1": 0, + "property2": 0 + }, + "logprobs": true, + "parallel_tool_calls": true, + "providerOptions": { + "models": [ + "string" + ], + "order": [ + "string" + ] + }, + "reasoning": { + "enabled": true, + "exclude": true, + "max_tokens": 0 + }, + "top_logprobs": 0, + "user": "string" + } + }, + "reasoning_effort": { + "default": "string", + "max": "string" + }, + "temperature": 0, + "top_k": 0, + "top_p": 0 + }, + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "reasoning_efforts": [ + "string" + ], + "updated_at": "2019-08-24T14:15:22Z" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +|--------|--------------------------------------------------------------|-------------|----------------------------------------------------| +| 201 | [Created](https://tools.ietf.org/html/rfc7231#section-6.3.2) | Created | [codersdk.ChatModel](schemas.md#codersdkchatmodel) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Get an AI model + +### Code samples + +```sh +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/chats/models/{model} \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /api/v2/organizations/{organization}/chats/models/{model}` + +### Parameters + +| Name | In | Type | Required | Description | +|----------------|------|--------|----------|-------------------------| +| `organization` | path | string | true | Organization name or ID | +| `model` | path | string | true | Model ID | + +### Example responses + +> 200 Response + +```json +{ + "ai_provider_id": "5a3b8ff9-20e7-4c37-ba1a-5b433e355819", + "compression_threshold": 0, + "context_limit": 0, + "created_at": "2019-08-24T14:15:22Z", + "display_name": "string", + "enabled": true, + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "is_default": true, + "model": "string", + "model_config": { + "frequency_penalty": 0, + "max_output_tokens": 0, + "openai_config": { + "use_responses_api": true + }, + "presence_penalty": 0, + "provider_options": { + "anthropic": { + "allowed_domains": [ + "string" + ], + "blocked_domains": [ + "string" + ], + "context_1m_enabled": true, + "disable_parallel_tool_use": true, + "send_reasoning": true, + "thinking": { + "budget_tokens": 0 + }, + "thinking_display": "string", + "web_search_enabled": true + }, + "google": { + "cached_content": "string", + "safety_settings": [ + { + "category": "string", + "threshold": "string" + } + ], + "thinking_config": { + "include_thoughts": true, + "thinking_budget": 0, + "thinking_level": "string" + }, + "threshold": "string", + "web_search_enabled": true + }, + "openai": { + "allowed_domains": [ + "string" + ], + "include": [ + "string" + ], + "instructions": "string", + "log_probs": true, + "logit_bias": { + "property1": 0, + "property2": 0 + }, + "max_completion_tokens": 0, + "max_tool_calls": 0, + "metadata": { + "property1": null, + "property2": null + }, + "parallel_tool_calls": true, + "prediction": { + "property1": null, + "property2": null + }, + "prompt_cache_key": "string", + "reasoning_summary": "string", + "safety_identifier": "string", + "search_context_size": "string", + "service_tier": "string", + "store": true, + "strict_json_schema": true, + "structured_outputs": true, + "text_verbosity": "string", + "top_log_probs": 0, + "user": "string", + "web_search_enabled": true + }, + "openaicompat": { + "user": "string" + }, + "openrouter": { + "extra_body": { + "property1": null, + "property2": null + }, + "include_usage": true, + "log_probs": true, + "logit_bias": { + "property1": 0, + "property2": 0 + }, + "parallel_tool_calls": true, + "provider": { + "allow_fallbacks": true, + "data_collection": "string", + "ignore": [ + "string" + ], + "only": [ + "string" + ], + "order": [ + "string" + ], + "quantizations": [ + "string" + ], + "require_parameters": true, + "sort": "string" + }, + "reasoning": { + "enabled": true, + "exclude": true, + "max_tokens": 0 + }, + "user": "string" + }, + "vercel": { + "extra_body": { + "property1": null, + "property2": null + }, + "logit_bias": { + "property1": 0, + "property2": 0 + }, + "logprobs": true, + "parallel_tool_calls": true, + "providerOptions": { + "models": [ + "string" + ], + "order": [ + "string" + ] + }, + "reasoning": { + "enabled": true, + "exclude": true, + "max_tokens": 0 + }, + "top_logprobs": 0, + "user": "string" + } + }, + "reasoning_effort": { + "default": "string", + "max": "string" + }, + "temperature": 0, + "top_k": 0, + "top_p": 0 + }, + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "reasoning_efforts": [ + "string" + ], + "updated_at": "2019-08-24T14:15:22Z" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +|--------|---------------------------------------------------------|-------------|----------------------------------------------------| +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.ChatModel](schemas.md#codersdkchatmodel) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Delete an AI model + +### Code samples + +```sh +# Example request using curl +curl -X DELETE http://coder-server:8080/api/v2/organizations/{organization}/chats/models/{model} \ + -H 'Coder-Session-Token: API_KEY' +``` + +`DELETE /api/v2/organizations/{organization}/chats/models/{model}` + +### Parameters + +| Name | In | Type | Required | Description | +|----------------|------|--------|----------|-------------------------| +| `organization` | path | string | true | Organization name or ID | +| `model` | path | string | true | Model 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). + +## Update an AI model + +### Code samples + +```sh +# Example request using curl +curl -X PATCH http://coder-server:8080/api/v2/organizations/{organization}/chats/models/{model} \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`PATCH /api/v2/organizations/{organization}/chats/models/{model}` + +> Body parameter + +```json +{ + "ai_provider_id": "5a3b8ff9-20e7-4c37-ba1a-5b433e355819", + "compression_threshold": 0, + "context_limit": 0, + "display_name": "string", + "enabled": true, + "is_default": true, + "model": "string", + "model_config": { + "frequency_penalty": 0, + "max_output_tokens": 0, + "openai_config": { + "use_responses_api": true + }, + "presence_penalty": 0, + "provider_options": { + "anthropic": { + "allowed_domains": [ + "string" + ], + "blocked_domains": [ + "string" + ], + "context_1m_enabled": true, + "disable_parallel_tool_use": true, + "send_reasoning": true, + "thinking": { + "budget_tokens": 0 }, - "file_name": "string", - "hook_rewritten": true, - "is_error": true, - "is_media": true, - "mcp_server_config_id": { - "uuid": "string", - "valid": true + "thinking_display": "string", + "web_search_enabled": true + }, + "google": { + "cached_content": "string", + "safety_settings": [ + { + "category": "string", + "threshold": "string" + } + ], + "thinking_config": { + "include_thoughts": true, + "thinking_budget": 0, + "thinking_level": "string" }, - "media_type": "string", - "name": "string", - "parsed_commands": [ - [ + "threshold": "string", + "web_search_enabled": true + }, + "openai": { + "allowed_domains": [ + "string" + ], + "include": [ + "string" + ], + "instructions": "string", + "log_probs": true, + "logit_bias": { + "property1": 0, + "property2": 0 + }, + "max_completion_tokens": 0, + "max_tool_calls": 0, + "metadata": { + "property1": null, + "property2": null + }, + "parallel_tool_calls": true, + "prediction": { + "property1": null, + "property2": null + }, + "prompt_cache_key": "string", + "reasoning_summary": "string", + "safety_identifier": "string", + "search_context_size": "string", + "service_tier": "string", + "store": true, + "strict_json_schema": true, + "structured_outputs": true, + "text_verbosity": "string", + "top_log_probs": 0, + "user": "string", + "web_search_enabled": true + }, + "openaicompat": { + "user": "string" + }, + "openrouter": { + "extra_body": { + "property1": null, + "property2": null + }, + "include_usage": true, + "log_probs": true, + "logit_bias": { + "property1": 0, + "property2": 0 + }, + "parallel_tool_calls": true, + "provider": { + "allow_fallbacks": true, + "data_collection": "string", + "ignore": [ + "string" + ], + "only": [ + "string" + ], + "order": [ + "string" + ], + "quantizations": [ + "string" + ], + "require_parameters": true, + "sort": "string" + }, + "reasoning": { + "enabled": true, + "exclude": true, + "max_tokens": 0 + }, + "user": "string" + }, + "vercel": { + "extra_body": { + "property1": null, + "property2": null + }, + "logit_bias": { + "property1": 0, + "property2": 0 + }, + "logprobs": true, + "parallel_tool_calls": true, + "providerOptions": { + "models": [ + "string" + ], + "order": [ "string" ] + }, + "reasoning": { + "enabled": true, + "exclude": true, + "max_tokens": 0 + }, + "top_logprobs": 0, + "user": "string" + } + }, + "reasoning_effort": { + "default": "string", + "max": "string" + }, + "temperature": 0, + "top_k": 0, + "top_p": 0 + } +} +``` + +### Parameters + +| Name | In | Type | Required | Description | +|----------------|------|------------------------------------------------------------------------------|----------|-------------------------| +| `organization` | path | string | true | Organization name or ID | +| `model` | path | string | true | Model ID | +| `body` | body | [codersdk.UpdateChatModelRequest](schemas.md#codersdkupdatechatmodelrequest) | true | Model updates | + +### Example responses + +> 200 Response + +```json +{ + "ai_provider_id": "5a3b8ff9-20e7-4c37-ba1a-5b433e355819", + "compression_threshold": 0, + "context_limit": 0, + "created_at": "2019-08-24T14:15:22Z", + "display_name": "string", + "enabled": true, + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "is_default": true, + "model": "string", + "model_config": { + "frequency_penalty": 0, + "max_output_tokens": 0, + "openai_config": { + "use_responses_api": true + }, + "presence_penalty": 0, + "provider_options": { + "anthropic": { + "allowed_domains": [ + "string" ], - "provider_executed": true, - "provider_metadata": [ - 0 - ], - "result": [ - 0 + "blocked_domains": [ + "string" ], - "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 + "context_1m_enabled": true, + "disable_parallel_tool_use": true, + "send_reasoning": true, + "thinking": { + "budget_tokens": 0 + }, + "thinking_display": "string", + "web_search_enabled": true }, - "file_name": "string", - "hook_rewritten": true, - "is_error": true, - "is_media": true, - "mcp_server_config_id": { - "uuid": "string", - "valid": true + "google": { + "cached_content": "string", + "safety_settings": [ + { + "category": "string", + "threshold": "string" + } + ], + "thinking_config": { + "include_thoughts": true, + "thinking_budget": 0, + "thinking_level": "string" + }, + "threshold": "string", + "web_search_enabled": true }, - "media_type": "string", - "name": "string", - "parsed_commands": [ - [ + "openai": { + "allowed_domains": [ "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" - ] + ], + "include": [ + "string" + ], + "instructions": "string", + "log_probs": true, + "logit_bias": { + "property1": 0, + "property2": 0 + }, + "max_completion_tokens": 0, + "max_tool_calls": 0, + "metadata": { + "property1": null, + "property2": null + }, + "parallel_tool_calls": true, + "prediction": { + "property1": null, + "property2": null + }, + "prompt_cache_key": "string", + "reasoning_summary": "string", + "safety_identifier": "string", + "search_context_size": "string", + "service_tier": "string", + "store": true, + "strict_json_schema": true, + "structured_outputs": true, + "text_verbosity": "string", + "top_log_probs": 0, + "user": "string", + "web_search_enabled": true + }, + "openaicompat": { + "user": "string" + }, + "openrouter": { + "extra_body": { + "property1": null, + "property2": null + }, + "include_usage": true, + "log_probs": true, + "logit_bias": { + "property1": 0, + "property2": 0 + }, + "parallel_tool_calls": true, + "provider": { + "allow_fallbacks": true, + "data_collection": "string", + "ignore": [ + "string" ], - "provider_executed": true, - "provider_metadata": [ - 0 + "only": [ + "string" ], - "result": [ - 0 + "order": [ + "string" ], - "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 + "quantizations": [ + "string" + ], + "require_parameters": true, + "sort": "string" + }, + "reasoning": { + "enabled": true, + "exclude": true, + "max_tokens": 0 + }, + "user": "string" + }, + "vercel": { + "extra_body": { + "property1": null, + "property2": null + }, + "logit_bias": { + "property1": 0, + "property2": 0 + }, + "logprobs": true, + "parallel_tool_calls": true, + "providerOptions": { + "models": [ + "string" + ], + "order": [ + "string" + ] + }, + "reasoning": { + "enabled": true, + "exclude": true, + "max_tokens": 0 + }, + "top_logprobs": 0, + "user": "string" + } + }, + "reasoning_effort": { + "default": "string", + "max": "string" + }, + "temperature": 0, + "top_k": 0, + "top_p": 0 }, - "status": { - "status": "waiting" + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "reasoning_efforts": [ + "string" + ], + "updated_at": "2019-08-24T14:15:22Z" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +|--------|---------------------------------------------------------|-------------|----------------------------------------------------| +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.ChatModel](schemas.md#codersdkchatmodel) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Get an AI model ACL + +### Code samples + +```sh +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/chats/models/{model}/acl \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /api/v2/organizations/{organization}/chats/models/{model}/acl` + +### Parameters + +| Name | In | Type | Required | Description | +|----------------|------|--------------|----------|-------------------------| +| `organization` | path | string | true | Organization name or ID | +| `model` | path | string(uuid) | true | Model ID | + +### Example responses + +> 200 Response + +```json +{ + "group_roles": { + "property1": "read", + "property2": "read" }, - "type": "message_part" + "user_roles": { + "property1": "read", + "property2": "read" + } } ``` ### 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 | [codersdk.ChatModelACL](schemas.md#codersdkchatmodelacl) | To perform this operation, you must be authenticated. [Learn more](authentication.md). -## Connect to chat workspace desktop via WebSockets +## Update an AI model ACL ### Code samples ```sh # Example request using curl -curl -X GET http://coder-server:8080/api/experimental/chats/{chat}/stream/desktop \ +curl -X PATCH http://coder-server:8080/api/v2/organizations/{organization}/chats/models/{model}/acl \ + -H 'Content-Type: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`GET /api/experimental/chats/{chat}/stream/desktop` +`PATCH /api/v2/organizations/{organization}/chats/models/{model}/acl` -Raw binary WebSocket stream of the chat workspace desktop. -Experimental: this endpoint is subject to change. +> Body parameter + +```json +{ + "group_roles": { + "property1": "read", + "property2": "read" + }, + "user_roles": { + "property1": "read", + "property2": "read" + } +} +``` ### Parameters -| Name | In | Type | Required | Description | -|--------|------|--------------|----------|-------------| -| `chat` | path | string(uuid) | true | Chat ID | +| Name | In | Type | Required | Description | +|----------------|------|------------------------------------------------------------------------------------|----------|-------------------------| +| `organization` | path | string | true | Organization name or ID | +| `model` | path | string(uuid) | true | Model ID | +| `body` | body | [codersdk.UpdateChatModelACLRequest](schemas.md#codersdkupdatechatmodelaclrequest) | true | Sparse model ACL update | ### Responses -| Status | Meaning | Description | Schema | -|--------|--------------------------------------------------------------------------|---------------------|--------| -| 101 | [Switching Protocols](https://tools.ietf.org/html/rfc7231#section-6.2.2) | Switching Protocols | | +| 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). -## Watch chat workspace git state via WebSockets +## Get organization member chat model overrides ### Code samples ```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/organizations/{organization}/members/{user}/chats/model-overrides \ -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/organizations/{organization}/members/{user}/chats/model-overrides` ### Parameters -| Name | In | Type | Required | Description | -|--------|------|--------------|----------|-------------| -| `chat` | path | string(uuid) | true | Chat ID | +| Name | In | Type | Required | Description | +|----------------|------|--------|----------|-------------------------| +| `organization` | path | string | true | Organization name or ID | +| `user` | path | string | true | User name, ID, or me | ### Example responses @@ -2959,49 +6514,201 @@ Experimental: this endpoint is subject to change. ```json { - "message": "string", - "repositories": [ - { - "branch": "string", - "remote_origin": "string", - "removed": true, - "repo_root": "string", - "unified_diff": "string" + "deployment_defaults": { + "explore": { + "context": "general", + "model_config_id": "string", + "reasoning_effort": "string" + }, + "general": { + "context": "general", + "model_config_id": "string", + "reasoning_effort": "string" } - ], - "scanned_at": "2019-08-24T14:15:22Z", - "type": "changes" + }, + "enabled": true, + "explore": { + "context": "root", + "is_set": true, + "mode": "deployment_default", + "model_config_id": "string", + "reasoning_effort": "string" + }, + "general": { + "context": "root", + "is_set": true, + "mode": "deployment_default", + "model_config_id": "string", + "reasoning_effort": "string" + }, + "root": { + "context": "root", + "is_set": true, + "mode": "deployment_default", + "model_config_id": "string", + "reasoning_effort": "string" + } } ``` ### Responses -| Status | Meaning | Description | Schema | -|--------|---------------------------------------------------------|-------------|----------------------------------------------------------------------------------------------| -| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.WorkspaceAgentGitServerMessage](schemas.md#codersdkworkspaceagentgitservermessage) | +| Status | Meaning | Description | Schema | +|--------|---------------------------------------------------------|-------------|--------------------------------------------------------------------------------------------------------------| +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.UserChatPersonalModelOverridesResponse](schemas.md#codersdkuserchatpersonalmodeloverridesresponse) | To perform this operation, you must be authenticated. [Learn more](authentication.md). -## Propose chat title +## Update organization member chat model override + +### Code samples + +```sh +# Example request using curl +curl -X PUT http://coder-server:8080/api/v2/organizations/{organization}/members/{user}/chats/model-overrides/{context} \ + -H 'Content-Type: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`PUT /api/v2/organizations/{organization}/members/{user}/chats/model-overrides/{context}` + +> Body parameter + +```json +{ + "mode": "deployment_default", + "model_config_id": "string", + "reasoning_effort": "string" +} +``` + +### Parameters + +| Name | In | Type | Required | Description | +|----------------|------|----------------------------------------------------------------------------------------------------------------------|----------|-------------------------| +| `organization` | path | string | true | Organization name or ID | +| `user` | path | string | true | User name, ID, or me | +| `context` | path | string | true | Override context | +| `body` | body | [codersdk.UpdateUserChatPersonalModelOverrideRequest](schemas.md#codersdkupdateuserchatpersonalmodeloverriderequest) | true | Personal model override | + +#### Enumerated Values + +| Parameter | Value(s) | +|-----------|------------------------------| +| `context` | `explore`, `general`, `root` | + +### 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 POST http://coder-server:8080/api/experimental/chats/{chat}/title/propose \ +curl -X GET http://coder-server:8080/api/v2/users/{user}/ai-provider-keys \ -H 'Accept: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`POST /api/experimental/chats/{chat}/title/propose` +`GET /api/v2/users/{user}/ai-provider-keys` -Experimental: this endpoint is subject to change. +### 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 | -|--------|------|--------------|----------|-------------| -| `chat` | path | string(uuid) | true | Chat ID | +| Name | In | Type | Required | Description | +|--------------|------|----------------------------------------------------------------------------------------------|----------|--------------------------| +| `user` | path | string | true | User ID, username, or me | +| `aiProvider` | path | string | true | AI provider ID | +| `body` | body | [codersdk.CreateUserAIProviderKeyRequest](schemas.md#codersdkcreateuseraiproviderkeyrequest) | true | Request body | ### Example responses @@ -3009,14 +6716,52 @@ Experimental: this endpoint is subject to change. ```json { - "title": "string" + "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.ProposeChatTitleResponse](schemas.md#codersdkproposechattitleresponse) | +| 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 | 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/mcp.md b/docs/reference/api/mcp.md index 9feb9375878..432e730db05 100644 --- a/docs/reference/api/mcp.md +++ b/docs/reference/api/mcp.md @@ -4,3 +4,636 @@ title: MCP --- + +## Handle MCP server OAuth2 callback + +### Code samples + +```sh +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/mcp/servers/{mcpServer}/oauth2/callback \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /api/v2/mcp/servers/{mcpServer}/oauth2/callback` + +### Parameters + +| Name | In | Type | Required | Description | +|---------------------|-------|--------------|----------|--------------------------------------------------------------------------------------| +| `mcpServer` | path | string(uuid) | true | MCP server config ID | +| `code` | query | string | false | Authorization code issued by the provider. Required together with state on success. | +| `state` | query | string | false | Opaque state issued by the connect endpoint. Required together with code on success. | +| `error` | query | string | false | Provider error code. Present instead of code when authorization fails. | +| `error_description` | query | string | false | Provider error description accompanying error. | + +### Responses + +| Status | Meaning | Description | Schema | +|--------|---------------------------------------------------------|-------------|--------| +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Disconnect MCP server OAuth2 token + +### Code samples + +```sh +# Example request using curl +curl -X DELETE http://coder-server:8080/api/v2/mcp/servers/{mcpServer}/oauth2/disconnect \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`DELETE /api/v2/mcp/servers/{mcpServer}/oauth2/disconnect` + +### Parameters + +| Name | In | Type | Required | Description | +|-------------|------|--------------|----------|----------------------| +| `mcpServer` | path | string(uuid) | true | MCP server config ID | + +### Example responses + +> 200 Response + +```json +{ + "token_revocation_error": "string", + "token_revoked": true +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +|--------|---------------------------------------------------------|-------------|----------------------------------------------------------------------------------------------------| +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.MCPServerOAuth2DisconnectResponse](schemas.md#codersdkmcpserveroauth2disconnectresponse) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## List MCP server configs + +### Code samples + +```sh +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/mcp-servers \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /api/v2/organizations/{organization}/mcp-servers` + +### Parameters + +| Name | In | Type | Required | Description | +|----------------|------|--------------|----------|-----------------| +| `organization` | path | string(uuid) | true | Organization ID | + +### Example responses + +> 200 Response + +```json +[ + { + "allow_in_plan_mode": true, + "api_key_header": "string", + "auth_connected": true, + "auth_type": "string", + "availability": "string", + "created_at": "2019-08-24T14:15:22Z", + "description": "string", + "display_name": "string", + "enabled": true, + "forward_coder_headers": true, + "has_api_key": true, + "has_custom_headers": true, + "has_oauth2_secret": true, + "icon_url": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "model_intent": true, + "oauth2_auth_url": "string", + "oauth2_client_id": "string", + "oauth2_revocation_url": "string", + "oauth2_scopes": "string", + "oauth2_token_url": "string", + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "slug": "string", + "tool_allow_list": [ + "string" + ], + "tool_deny_list": [ + "string" + ], + "transport": "string", + "updated_at": "2019-08-24T14:15:22Z", + "url": "string" + } +] +``` + +### Responses + +| Status | Meaning | Description | Schema | +|--------|---------------------------------------------------------|-------------|-------------------------------------------------------------------------| +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | array of [codersdk.MCPServerConfig](schemas.md#codersdkmcpserverconfig) | + +

Response Schema

+ +Status Code **200** + +| Name | Type | Required | Restrictions | Description | +|---------------------------|-------------------|----------|--------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `[array item]` | array | false | | | +| `» allow_in_plan_mode` | boolean | false | | | +| `» api_key_header` | string | false | | Api key header key fields (only populated for admins). | +| `» auth_connected` | boolean | false | | Per-user state (populated for non-admin requests). | +| `» auth_type` | string | false | | "none", "oauth2", "api_key", "custom_headers", "user_oidc" | +| `» availability` | string | false | | Availability policy set by admin. | +| `» created_at` | string(date-time) | false | | | +| `» description` | string | false | | | +| `» display_name` | string | false | | | +| `» enabled` | boolean | false | | | +| `» forward_coder_headers` | boolean | false | | Forward coder headers forwards the same Coder identity headers we send to LLM providers (X-Coder-Owner-Id, X-Coder-Chat-Id, and the optional X-Coder-Subchat-Id and X-Coder-Workspace-Id) to this MCP server on every request. Off by default to avoid leaking chat identity to third-party servers. | +| `» has_api_key` | boolean | false | | | +| `» has_custom_headers` | boolean | false | | | +| `» has_oauth2_secret` | boolean | false | | | +| `» icon_url` | string | false | | | +| `» id` | string(uuid) | false | | | +| `» model_intent` | boolean | false | | | +| `» oauth2_auth_url` | string | false | | | +| `» oauth2_client_id` | string | false | | Oauth2 client ID fields (only populated for admins). | +| `» oauth2_revocation_url` | string | false | | | +| `» oauth2_scopes` | string | false | | | +| `» oauth2_token_url` | string | false | | | +| `» organization_id` | string(uuid) | false | | | +| `» slug` | string | false | | | +| `» tool_allow_list` | array | false | | Tool governance. | +| `» tool_deny_list` | array | false | | | +| `» transport` | string | false | | "streamable_http" or "sse" | +| `» updated_at` | string(date-time) | false | | | +| `» url` | string | false | | | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Create MCP server config + +### Code samples + +```sh +# Example request using curl +curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/mcp-servers \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`POST /api/v2/organizations/{organization}/mcp-servers` + +> Body parameter + +```json +{ + "allow_in_plan_mode": true, + "api_key_header": "string", + "api_key_value": "string", + "auth_type": "none", + "availability": "force_on", + "custom_headers": { + "property1": "string", + "property2": "string" + }, + "description": "string", + "display_name": "string", + "enabled": true, + "forward_coder_headers": true, + "icon_url": "string", + "model_intent": true, + "oauth2_auth_url": "string", + "oauth2_client_id": "string", + "oauth2_client_secret": "string", + "oauth2_revocation_url": "string", + "oauth2_scopes": "string", + "oauth2_token_url": "string", + "slug": "string", + "tool_allow_list": [ + "string" + ], + "tool_deny_list": [ + "string" + ], + "transport": "streamable_http", + "url": "string" +} +``` + +### Parameters + +| Name | In | Type | Required | Description | +|----------------|------|------------------------------------------------------------------------------------------|----------|----------------------------------| +| `organization` | path | string(uuid) | true | Organization ID | +| `body` | body | [codersdk.CreateMCPServerConfigRequest](schemas.md#codersdkcreatemcpserverconfigrequest) | true | Create MCP server config request | + +### Example responses + +> 201 Response + +```json +{ + "allow_in_plan_mode": true, + "api_key_header": "string", + "auth_connected": true, + "auth_type": "string", + "availability": "string", + "created_at": "2019-08-24T14:15:22Z", + "description": "string", + "display_name": "string", + "enabled": true, + "forward_coder_headers": true, + "has_api_key": true, + "has_custom_headers": true, + "has_oauth2_secret": true, + "icon_url": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "model_intent": true, + "oauth2_auth_url": "string", + "oauth2_client_id": "string", + "oauth2_revocation_url": "string", + "oauth2_scopes": "string", + "oauth2_token_url": "string", + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "slug": "string", + "tool_allow_list": [ + "string" + ], + "tool_deny_list": [ + "string" + ], + "transport": "string", + "updated_at": "2019-08-24T14:15:22Z", + "url": "string" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +|--------|--------------------------------------------------------------|-------------|----------------------------------------------------------------| +| 201 | [Created](https://tools.ietf.org/html/rfc7231#section-6.3.2) | Created | [codersdk.MCPServerConfig](schemas.md#codersdkmcpserverconfig) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Get MCP server config + +### Code samples + +```sh +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/mcp-servers/{mcpserverconfig} \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /api/v2/organizations/{organization}/mcp-servers/{mcpserverconfig}` + +### Parameters + +| Name | In | Type | Required | Description | +|-------------------|------|--------------|----------|----------------------| +| `organization` | path | string(uuid) | true | Organization ID | +| `mcpserverconfig` | path | string(uuid) | true | MCP server config ID | + +### Example responses + +> 200 Response + +```json +{ + "allow_in_plan_mode": true, + "api_key_header": "string", + "auth_connected": true, + "auth_type": "string", + "availability": "string", + "created_at": "2019-08-24T14:15:22Z", + "description": "string", + "display_name": "string", + "enabled": true, + "forward_coder_headers": true, + "has_api_key": true, + "has_custom_headers": true, + "has_oauth2_secret": true, + "icon_url": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "model_intent": true, + "oauth2_auth_url": "string", + "oauth2_client_id": "string", + "oauth2_revocation_url": "string", + "oauth2_scopes": "string", + "oauth2_token_url": "string", + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "slug": "string", + "tool_allow_list": [ + "string" + ], + "tool_deny_list": [ + "string" + ], + "transport": "string", + "updated_at": "2019-08-24T14:15:22Z", + "url": "string" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +|--------|---------------------------------------------------------|-------------|----------------------------------------------------------------| +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.MCPServerConfig](schemas.md#codersdkmcpserverconfig) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Delete MCP server config + +### Code samples + +```sh +# Example request using curl +curl -X DELETE http://coder-server:8080/api/v2/organizations/{organization}/mcp-servers/{mcpserverconfig} \ + -H 'Coder-Session-Token: API_KEY' +``` + +`DELETE /api/v2/organizations/{organization}/mcp-servers/{mcpserverconfig}` + +### Parameters + +| Name | In | Type | Required | Description | +|-------------------|------|--------------|----------|----------------------| +| `organization` | path | string(uuid) | true | Organization ID | +| `mcpserverconfig` | path | string(uuid) | true | MCP server config 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). + +## Update MCP server config + +### Code samples + +```sh +# Example request using curl +curl -X PATCH http://coder-server:8080/api/v2/organizations/{organization}/mcp-servers/{mcpserverconfig} \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`PATCH /api/v2/organizations/{organization}/mcp-servers/{mcpserverconfig}` + +> Body parameter + +```json +{ + "allow_in_plan_mode": true, + "api_key_header": "string", + "api_key_value": "string", + "auth_type": "none", + "availability": "force_on", + "custom_headers": { + "property1": "string", + "property2": "string" + }, + "description": "string", + "display_name": "string", + "enabled": true, + "forward_coder_headers": true, + "icon_url": "string", + "model_intent": true, + "oauth2_auth_url": "string", + "oauth2_client_id": "string", + "oauth2_client_secret": "string", + "oauth2_revocation_url": "string", + "oauth2_scopes": "string", + "oauth2_token_url": "string", + "slug": "string", + "tool_allow_list": [ + "string" + ], + "tool_deny_list": [ + "string" + ], + "transport": "streamable_http", + "url": "string" +} +``` + +### Parameters + +| Name | In | Type | Required | Description | +|-------------------|------|------------------------------------------------------------------------------------------|----------|----------------------------------| +| `organization` | path | string(uuid) | true | Organization ID | +| `mcpserverconfig` | path | string(uuid) | true | MCP server config ID | +| `body` | body | [codersdk.UpdateMCPServerConfigRequest](schemas.md#codersdkupdatemcpserverconfigrequest) | true | Update MCP server config request | + +### Example responses + +> 200 Response + +```json +{ + "allow_in_plan_mode": true, + "api_key_header": "string", + "auth_connected": true, + "auth_type": "string", + "availability": "string", + "created_at": "2019-08-24T14:15:22Z", + "description": "string", + "display_name": "string", + "enabled": true, + "forward_coder_headers": true, + "has_api_key": true, + "has_custom_headers": true, + "has_oauth2_secret": true, + "icon_url": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "model_intent": true, + "oauth2_auth_url": "string", + "oauth2_client_id": "string", + "oauth2_revocation_url": "string", + "oauth2_scopes": "string", + "oauth2_token_url": "string", + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "slug": "string", + "tool_allow_list": [ + "string" + ], + "tool_deny_list": [ + "string" + ], + "transport": "string", + "updated_at": "2019-08-24T14:15:22Z", + "url": "string" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +|--------|---------------------------------------------------------|-------------|----------------------------------------------------------------| +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.MCPServerConfig](schemas.md#codersdkmcpserverconfig) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Get MCP server config ACL + +### Code samples + +```sh +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/mcp-servers/{mcpserverconfig}/acl \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /api/v2/organizations/{organization}/mcp-servers/{mcpserverconfig}/acl` + +### Parameters + +| Name | In | Type | Required | Description | +|-------------------|------|--------------|----------|----------------------| +| `organization` | path | string(uuid) | true | Organization ID | +| `mcpserverconfig` | path | string(uuid) | true | MCP server config ID | + +### Example responses + +> 200 Response + +```json +{ + "groups": [ + { + "avatar_url": "http://example.com", + "display_name": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "members": [ + { + "avatar_url": "http://example.com", + "created_at": "2019-08-24T14:15:22Z", + "email": "user@example.com", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "is_service_account": true, + "last_seen_at": "2019-08-24T14:15:22Z", + "login_type": "", + "name": "string", + "status": "active", + "theme_preference": "string", + "updated_at": "2019-08-24T14:15:22Z", + "username": "string" + } + ], + "name": "string", + "organization_display_name": "string", + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "organization_name": "string", + "quota_allowance": 0, + "role": "read", + "source": "user", + "total_member_count": 0 + } + ], + "users": [ + { + "avatar_url": "http://example.com", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "name": "string", + "role": "read", + "username": "string" + } + ] +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +|--------|---------------------------------------------------------|-------------|----------------------------------------------------------------------| +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.MCPServerConfigACL](schemas.md#codersdkmcpserverconfigacl) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Update MCP server config ACL + +### Code samples + +```sh +# Example request using curl +curl -X PATCH http://coder-server:8080/api/v2/organizations/{organization}/mcp-servers/{mcpserverconfig}/acl \ + -H 'Content-Type: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`PATCH /api/v2/organizations/{organization}/mcp-servers/{mcpserverconfig}/acl` + +> Body parameter + +```json +{ + "group_roles": { + "property1": "read", + "property2": "read" + }, + "user_roles": { + "property1": "read", + "property2": "read" + } +} +``` + +### Parameters + +| Name | In | Type | Required | Description | +|-------------------|------|------------------------------------------------------------------------------------------------|----------|--------------------------------------| +| `organization` | path | string(uuid) | true | Organization ID | +| `mcpserverconfig` | path | string(uuid) | true | MCP server config ID | +| `body` | body | [codersdk.UpdateMCPServerConfigACLRequest](schemas.md#codersdkupdatemcpserverconfigaclrequest) | true | Update MCP server config ACL request | + +### 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). + +## Initiate MCP server OAuth2 connect + +### Code samples + +```sh +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/mcp-servers/{mcpserverconfig}/oauth2/connect \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /api/v2/organizations/{organization}/mcp-servers/{mcpserverconfig}/oauth2/connect` + +### Parameters + +| Name | In | Type | Required | Description | +|-------------------|------|--------------|----------|----------------------| +| `organization` | path | string(uuid) | true | Organization ID | +| `mcpserverconfig` | path | string(uuid) | true | MCP server config ID | + +### Responses + +| Status | Meaning | Description | Schema | +|--------|-------------------------------------------------------------------------|--------------------|--------| +| 307 | [Temporary Redirect](https://tools.ietf.org/html/rfc7231#section-6.4.7) | Temporary Redirect | | + +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 e8b89c5c593..45668e12165 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/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 4502fd9eac4..741afa032bc 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; @@ -9922,7 +9922,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; } From 4db23eefc888b82958ca11413e0f8b7d591d1812 Mon Sep 17 00:00:00 2001 From: Michael Suchacz <203725896+ibetitsmike@users.noreply.github.com> Date: Mon, 24 Aug 2026 18:12:50 +0000 Subject: [PATCH 02/15] fix: address Codex review on /api/v2 chat mounts Publish pagination params on GET /chats, describe the stream frame as an event array, document the raw upload body via form data instead of a parser exemption, restore the media-type contracts on the file download routes and the OAuth callback, extend permissive MCP CORS to /api/v2/mcp/, and share one FilesRateLimit limiter across both prefix mounts with prefix-stripped endpoint keys. --- coderd/apidoc/docs.go | 64 +++- coderd/apidoc/swagger.json | 62 +++- coderd/chat_routes.go | 15 +- coderd/coderd.go | 6 + coderd/coderdtest/swaggerparser.go | 5 +- coderd/exp_chats.go | 10 +- coderd/httpmw/cors.go | 1 + coderd/httpmw/ratelimit.go | 13 +- coderd/mcp.go | 1 + docs/reference/api/chats.md | 514 ++++++++++++++++++----------- 10 files changed, 489 insertions(+), 202 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index f29e0cf8bf9..db8fb0b891f 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -1326,6 +1326,25 @@ const docTemplate = `{ "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": { @@ -2087,6 +2106,7 @@ const docTemplate = `{ }, "/api/v2/chats/files": { "post": { + "description": "Swagger notice: Swagger 2.0 cannot model the raw binary request body this endpoint reads, so the upload is described as form data.", "consumes": [ "image/png", "image/jpeg", @@ -2114,6 +2134,20 @@ const docTemplate = `{ "name": "organization", "in": "query", "required": true + }, + { + "type": "string", + "description": "Attachment disposition carrying the file name, e.g. ` + "`" + `attachment; filename=\\", + "name": "Content-Disposition", + "in": "header", + "required": true + }, + { + "type": "file", + "description": "File to be uploaded, sent as the raw request body", + "name": "file", + "in": "formData", + "required": true } ], "responses": { @@ -2139,6 +2173,17 @@ const docTemplate = `{ }, "/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" ], @@ -2168,6 +2213,17 @@ const docTemplate = `{ }, "/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" ], @@ -2936,7 +2992,10 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.ChatStreamEvent" + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.ChatStreamEvent" + } } } }, @@ -4924,6 +4983,9 @@ const docTemplate = `{ }, "/api/v2/mcp/servers/{mcpServer}/oauth2/callback": { "get": { + "produces": [ + "text/html" + ], "tags": [ "MCP" ], diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 5720007029e..38b65af5583 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -1157,6 +1157,25 @@ "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": { @@ -1814,6 +1833,7 @@ }, "/api/v2/chats/files": { "post": { + "description": "Swagger notice: Swagger 2.0 cannot model the raw binary request body this endpoint reads, so the upload is described as form data.", "consumes": [ "image/png", "image/jpeg", @@ -1837,6 +1857,20 @@ "name": "organization", "in": "query", "required": true + }, + { + "type": "string", + "description": "Attachment disposition carrying the file name, e.g. `attachment; filename=\\", + "name": "Content-Disposition", + "in": "header", + "required": true + }, + { + "type": "file", + "description": "File to be uploaded, sent as the raw request body", + "name": "file", + "in": "formData", + "required": true } ], "responses": { @@ -1862,6 +1896,17 @@ }, "/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", @@ -1889,6 +1934,17 @@ }, "/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", @@ -2573,7 +2629,10 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.ChatStreamEvent" + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.ChatStreamEvent" + } } } }, @@ -4334,6 +4393,7 @@ }, "/api/v2/mcp/servers/{mcpServer}/oauth2/callback": { "get": { + "produces": ["text/html"], "tags": ["MCP"], "summary": "Handle MCP server OAuth2 callback", "operationId": "handle-mcp-server-oauth2-callback", diff --git a/coderd/chat_routes.go b/coderd/chat_routes.go index dcc40a14dc7..8ac65cc5bd6 100644 --- a/coderd/chat_routes.go +++ b/coderd/chat_routes.go @@ -11,9 +11,20 @@ import ( "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.RateLimit(api.FilesRateLimit, time.Minute) + }) + return api.chatFilesRateLimit +} + func (api *API) registerChatFileDownloadRoute(r chi.Router) { r.Group(func(r chi.Router) { - r.Use(httpmw.RateLimit(api.FilesRateLimit, time.Minute)) + r.Use(api.chatFilesRateLimitMW()) r.Get("/chats/files/{file}/download", api.downloadChatFile) }) } @@ -93,7 +104,7 @@ func (api *API) registerChatCollectionRoutes(r chi.Router) { r.Post("/", api.postChats) r.Get("/watch", api.watchChats) r.Route("/files", func(r chi.Router) { - r.Use(httpmw.RateLimit(api.FilesRateLimit, time.Minute)) + r.Use(api.chatFilesRateLimitMW()) r.Post("/", api.postChatFile) r.Post("/{file}/download-url", api.postChatFileDownloadURL) r.Get("/{file}", api.chatFileByID) diff --git a/coderd/coderd.go b/coderd/coderd.go index c497b6fd840..a9db7d56df0 100644 --- a/coderd/coderd.go +++ b/coderd/coderd.go @@ -2307,6 +2307,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/swaggerparser.go b/coderd/coderdtest/swaggerparser.go index 6b4818bc613..980b6d39728 100644 --- a/coderd/coderdtest/swaggerparser.go +++ b/coderd/coderdtest/swaggerparser.go @@ -382,10 +382,6 @@ func assertSecurityDefined(t *testing.T, comment SwaggerComment) { } func assertAccept(t *testing.T, comment SwaggerComment) { - if comment.method == "post" && comment.router == "/api/v2/chats/files" { - return - } - var hasRequestBody bool for _, c := range comment.parameters { if c.name == "request" && c.kind == "body" || @@ -434,6 +430,7 @@ func assertProduce(t *testing.T, comment SwaggerComment) { (comment.router == "/api/v2/workspaceagents/me/startup/logs" && comment.method == "patch") || (comment.router == "/api/v2/licenses/{id}" && comment.method == "delete") || (comment.router == "/api/v2/debug/coordinator" && comment.method == "get") || + (comment.router == "/api/v2/mcp/servers/{mcpServer}/oauth2/callback" && comment.method == "get") || (comment.router == "/api/v2/debug/tailnet" && comment.method == "get") || (comment.router == "/api/v2/workspaces/{workspace}/acl" && comment.method == "patch") || (comment.router == "/api/v2/init-script/{os}/{arch}" && comment.method == "get") || diff --git a/coderd/exp_chats.go b/coderd/exp_chats.go index 6d104f537fd..331383f5a7b 100644 --- a/coderd/exp_chats.go +++ b/coderd/exp_chats.go @@ -394,6 +394,9 @@ func (api *API) chatsByWorkspace(rw http.ResponseWriter, r *http.Request) { // @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 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/v2/chats [get] func (api *API) listChats(rw http.ResponseWriter, r *http.Request) { @@ -3087,7 +3090,7 @@ func (api *API) markChatAsRead(ctx context.Context, chatID uuid.UUID) { // @Tags Chats // @Produce json // @Param chat path string true "Chat ID" format(uuid) -// @Success 200 {object} codersdk.ChatStreamEvent +// @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() @@ -6070,12 +6073,15 @@ func (api *API) deleteUserChatCompactionThreshold(rw http.ResponseWriter, r *htt } // @Summary Upload chat file +// @Description Swagger notice: Swagger 2.0 cannot model the raw binary request body this endpoint reads, so the upload is described as form data. // @ID upload-chat-file // @Security CoderSessionToken // @Tags Chats // @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, e.g. `attachment; filename=\"img.png\"`" +// @Param file formData file true "File to be uploaded, sent as the raw request body" // @Success 201 {object} codersdk.UploadChatFileResponse // @Failure 413 {object} codersdk.Response "Request body exceeds 10 MiB" // @Router /api/v2/chats/files [post] @@ -6297,6 +6303,7 @@ func (api *API) postChatFileDownloadURL(rw http.ResponseWriter, r *http.Request) // @Summary Download chat file with signed token // @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 @@ -6345,6 +6352,7 @@ func (api *API) downloadChatFile(rw http.ResponseWriter, r *http.Request) { // @ID get-chat-file // @Security CoderSessionToken // @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) // @Success 200 // @Router /api/v2/chats/files/{file} [get] diff --git a/coderd/httpmw/cors.go b/coderd/httpmw/cors.go index 218aab6609f..56d8956fc4a 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 17af4be2421..8b7de2f71b1 100644 --- a/coderd/httpmw/ratelimit.go +++ b/coderd/httpmw/ratelimit.go @@ -5,6 +5,7 @@ import ( "net/http" "path" "strconv" + "strings" "sync/atomic" "time" @@ -107,7 +108,17 @@ func keyByNormalizedEndpoint(r *http.Request) (string, error) { if p == "" { p = "/" } - return path.Clean(p), nil + p = path.Clean(p) + // Routes mounted under both prefixes during the /api/experimental to + // /api/v2 compatibility window share one limiter instance; stripping + // the prefix keeps them in one bucket per endpoint. + for _, prefix := range []string{"/api/v2/", "/api/experimental/"} { + if strings.HasPrefix(p, prefix) { + p = p[len(prefix)-1:] + break + } + } + return p, nil } // RateLimitByAuthToken returns a handler that limits requests based on the diff --git a/coderd/mcp.go b/coderd/mcp.go index 718560274ce..4add6de3fde 100644 --- a/coderd/mcp.go +++ b/coderd/mcp.go @@ -1111,6 +1111,7 @@ func (api *API) mcpServerOAuth2Connect(rw http.ResponseWriter, r *http.Request) // @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/v2/mcp/servers/{mcpServer}/oauth2/callback [get] // diff --git a/docs/reference/api/chats.md b/docs/reference/api/chats.md index af6786dd480..a93030a8071 100644 --- a/docs/reference/api/chats.md +++ b/docs/reference/api/chats.md @@ -54,10 +54,13 @@ curl -X GET http://coder-server:8080/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; quote multi-word values; cannot be combined with title, pr_title, or pr; a value that tokenizes to no searchable words 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; quote multi-word values; cannot be combined with title, pr_title, or pr; a value that tokenizes to no searchable words 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). | +| `after_id` | query | string(uuid) | false | After ID | +| `limit` | query | integer | false | Page limit | +| `offset` | query | integer | false | Page offset | ### Example responses @@ -1386,16 +1389,27 @@ To perform this operation, you must be authenticated. [Learn more](authenticatio # Example request using curl curl -X POST http://coder-server:8080/api/v2/chats/files?organization=497f6eca-6276-4993-bfeb-53cbbbba6f08 \ -H 'Accept: application/json' \ + -H 'Content-Disposition: string' \ -H 'Coder-Session-Token: API_KEY' ``` `POST /api/v2/chats/files` +> Body parameter + +```yaml +file: string + +``` + ### Parameters | Name | In | Type | Required | Description | |----------------|-------|--------------|----------|-----------------| | `organization` | query | string(uuid) | true | Organization ID | +|`Content-Disposition`|header|string|true|Attachment disposition carrying the file name, e.g. `attachment; filename=\| +|`body`|body|object|true|| +|`» file`|body|binary|true|File to be uploaded, sent as the raw request body| ### Example responses @@ -4179,176 +4193,27 @@ curl -X GET http://coder-server:8080/api/v2/chats/{chat}/stream \ > 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": { +[ + { + "action_required": { + "tool_calls": [ + { + "args": "string", + "tool_call_id": "string", + "tool_name": "string" + } + ] + }, "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" + "error": { + "detail": "string", + "kind": "generic", + "message": "string", + "provider": "string", + "retryable": true, + "status_code": 0 }, - "role": "system", - "seq": 0 - }, - "queued_messages": [ - { + "message": { "chat_id": "efc9fe20-a1e5-4a8c-9c48-f1b30c1e4f86", "content": [ { @@ -4415,31 +4280,296 @@ curl -X GET http://coder-server:8080/api/v2/chats/{chat}/stream \ } ], "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) | + +

Response Schema

+ +Status Code **200** + +| 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 | | | + +#### Enumerated Values + +| 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). From f86992a66d3bbea301dedd997590d64f71a0b421 Mon Sep 17 00:00:00 2001 From: Michael Suchacz <203725896+ibetitsmike@users.noreply.github.com> Date: Mon, 24 Aug 2026 18:36:15 +0000 Subject: [PATCH 03/15] fix: address second Codex round on /api/v2 chat mounts Publish the stream parts frame as an event array and the stream after_id resume cursor, type the queued-message path IDs as integers, and move the chatd architecture TODO markers into this PR where the dual mounts land. --- coderd/apidoc/docs.go | 15 +- coderd/apidoc/swagger.json | 15 +- coderd/exp_chats.go | 7 +- coderd/x/chatd/ARCHITECTURE.md | 4 + docs/reference/api/chats.md | 515 ++++++++++++++++++++------------- 5 files changed, 348 insertions(+), 208 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index db8fb0b891f..7fccdd30613 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -2873,7 +2873,7 @@ const docTemplate = `{ "required": true }, { - "type": "string", + "type": "integer", "description": "Queued message ID", "name": "queuedMessage", "in": "path", @@ -2911,7 +2911,7 @@ const docTemplate = `{ "required": true }, { - "type": "string", + "type": "integer", "description": "Queued message ID", "name": "queuedMessage", "in": "path", @@ -2986,6 +2986,12 @@ const docTemplate = `{ "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": { @@ -3065,7 +3071,10 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.ChatStreamEvent" + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.ChatStreamEvent" + } } } }, diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 38b65af5583..389480f4e83 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -2522,7 +2522,7 @@ "required": true }, { - "type": "string", + "type": "integer", "description": "Queued message ID", "name": "queuedMessage", "in": "path", @@ -2556,7 +2556,7 @@ "required": true }, { - "type": "string", + "type": "integer", "description": "Queued message ID", "name": "queuedMessage", "in": "path", @@ -2623,6 +2623,12 @@ "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": { @@ -2694,7 +2700,10 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.ChatStreamEvent" + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.ChatStreamEvent" + } } } }, diff --git a/coderd/exp_chats.go b/coderd/exp_chats.go index 331383f5a7b..d59912f3d61 100644 --- a/coderd/exp_chats.go +++ b/coderd/exp_chats.go @@ -2903,7 +2903,7 @@ func (api *API) patchChatMessage(rw http.ResponseWriter, r *http.Request) { // @Security CoderSessionToken // @Tags Chats // @Param chat path string true "Chat ID" -// @Param queuedMessage path string true "Queued message ID" +// @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) { @@ -2963,7 +2963,7 @@ func (api *API) deleteChatQueuedMessage(rw http.ResponseWriter, r *http.Request) // @Security CoderSessionToken // @Tags Chats // @Param chat path string true "Chat ID" -// @Param queuedMessage path string true "Queued message ID" +// @Param queuedMessage path int true "Queued message ID" // @Produce json // @Success 202 {object} codersdk.Response // @Router /api/v2/chats/{chat}/queue/{queuedMessage}/promote [post] @@ -3090,6 +3090,7 @@ func (api *API) markChatAsRead(ctx context.Context, chatID uuid.UUID) { // @Tags Chats // @Produce json // @Param chat path string true "Chat ID" format(uuid) +// @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) { @@ -8262,7 +8263,7 @@ func (api *API) getChatDebugRun(rw http.ResponseWriter, r *http.Request) { // @Tags Chats // @Produce json // @Param chat path string true "Chat ID" format(uuid) -// @Success 200 {object} codersdk.ChatStreamEvent +// @Success 200 {array} codersdk.ChatStreamEvent // @Router /api/v2/chats/{chat}/stream/parts [get] func (api *API) streamChatParts(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() diff --git a/coderd/x/chatd/ARCHITECTURE.md b/coderd/x/chatd/ARCHITECTURE.md index d91f91d4f0b..1f2b27e3479 100644 --- a/coderd/x/chatd/ARCHITECTURE.md +++ b/coderd/x/chatd/ARCHITECTURE.md @@ -1260,6 +1260,8 @@ WHERE id = ANY($1::uuid[]); ## Relay mechanism +TODO(CODAGT-921): update this section for the /api/v2 promotion. The stream and parts endpoints are dual-mounted under /api/experimental and /api/v2 during the compatibility window, while the internal replica-to-replica relay dial stays on /api/experimental until CODAGT-922 removes the experimental mounts. + 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. @@ -1294,6 +1296,8 @@ The forwarder must only pass parts for the currently requested episode to the st ### Parts endpoint +TODO(CODAGT-921): update the endpoint paths in this section for the /api/v2 promotion and compatibility window. + The parts endpoint is a WebSocket endpoint. Connection setup: diff --git a/docs/reference/api/chats.md b/docs/reference/api/chats.md index a93030a8071..b10034a5d47 100644 --- a/docs/reference/api/chats.md +++ b/docs/reference/api/chats.md @@ -3876,10 +3876,10 @@ curl -X DELETE http://coder-server:8080/api/v2/chats/{chat}/queue/{queuedMessage ### Parameters -| Name | In | Type | Required | Description | -|-----------------|------|--------|----------|-------------------| -| `chat` | path | string | true | Chat ID | -| `queuedMessage` | path | string | true | Queued message ID | +| Name | In | Type | Required | Description | +|-----------------|------|---------|----------|-------------------| +| `chat` | path | string | true | Chat ID | +| `queuedMessage` | path | integer | true | Queued message ID | ### Responses @@ -3904,10 +3904,10 @@ curl -X POST http://coder-server:8080/api/v2/chats/{chat}/queue/{queuedMessage}/ ### Parameters -| Name | In | Type | Required | Description | -|-----------------|------|--------|----------|-------------------| -| `chat` | path | string | true | Chat ID | -| `queuedMessage` | path | string | true | Queued message ID | +| Name | In | Type | Required | Description | +|-----------------|------|---------|----------|-------------------| +| `chat` | path | string | true | Chat ID | +| `queuedMessage` | path | integer | true | Queued message ID | ### Example responses @@ -4184,9 +4184,10 @@ curl -X GET http://coder-server:8080/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 @@ -4645,176 +4646,27 @@ curl -X GET http://coder-server:8080/api/v2/chats/{chat}/stream/parts \ > 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": { +[ + { + "action_required": { + "tool_calls": [ + { + "args": "string", + "tool_call_id": "string", + "tool_name": "string" + } + ] + }, "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" + "error": { + "detail": "string", + "kind": "generic", + "message": "string", + "provider": "string", + "retryable": true, + "status_code": 0 }, - "role": "system", - "seq": 0 - }, - "queued_messages": [ - { + "message": { "chat_id": "efc9fe20-a1e5-4a8c-9c48-f1b30c1e4f86", "content": [ { @@ -4881,31 +4733,296 @@ curl -X GET http://coder-server:8080/api/v2/chats/{chat}/stream/parts \ } ], "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) | + +

Response Schema

+ +Status Code **200** + +| 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 | | | + +#### Enumerated Values + +| 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). From df2b78fcd8960d4b0624a8e3f24fbd38094da101 Mon Sep 17 00:00:00 2001 From: Michael Suchacz <203725896+ibetitsmike@users.noreply.github.com> Date: Mon, 24 Aug 2026 18:55:55 +0000 Subject: [PATCH 04/15] fix: correct label and organization param contracts in chat API docs --- coderd/apidoc/docs.go | 30 +++++++++++-------------- coderd/apidoc/swagger.json | 30 +++++++++++-------------- coderd/exp_chats.go | 2 +- coderd/mcp.go | 12 +++++----- coderd/mcp_acl.go | 4 ++-- docs/reference/api/chats.md | 14 ++++++------ docs/reference/api/mcp.md | 44 ++++++++++++++++++------------------- 7 files changed, 64 insertions(+), 72 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 7fccdd30613..92dd2cbd2d1 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -1322,7 +1322,11 @@ const docTemplate = `{ "in": "query" }, { - "type": "string", + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "multi", "description": "Filter by label as key:value. Repeat for multiple (AND logic).", "name": "label", "in": "query" @@ -6760,8 +6764,7 @@ const docTemplate = `{ "parameters": [ { "type": "string", - "format": "uuid", - "description": "Organization ID", + "description": "Organization name or ID", "name": "organization", "in": "path", "required": true @@ -6799,8 +6802,7 @@ const docTemplate = `{ "parameters": [ { "type": "string", - "format": "uuid", - "description": "Organization ID", + "description": "Organization name or ID", "name": "organization", "in": "path", "required": true @@ -6843,8 +6845,7 @@ const docTemplate = `{ "parameters": [ { "type": "string", - "format": "uuid", - "description": "Organization ID", + "description": "Organization name or ID", "name": "organization", "in": "path", "required": true @@ -6881,8 +6882,7 @@ const docTemplate = `{ "parameters": [ { "type": "string", - "format": "uuid", - "description": "Organization ID", + "description": "Organization name or ID", "name": "organization", "in": "path", "required": true @@ -6922,8 +6922,7 @@ const docTemplate = `{ "parameters": [ { "type": "string", - "format": "uuid", - "description": "Organization ID", + "description": "Organization name or ID", "name": "organization", "in": "path", "required": true @@ -6974,8 +6973,7 @@ const docTemplate = `{ "parameters": [ { "type": "string", - "format": "uuid", - "description": "Organization ID", + "description": "Organization name or ID", "name": "organization", "in": "path", "required": true @@ -7015,8 +7013,7 @@ const docTemplate = `{ "parameters": [ { "type": "string", - "format": "uuid", - "description": "Organization ID", + "description": "Organization name or ID", "name": "organization", "in": "path", "required": true @@ -7061,8 +7058,7 @@ const docTemplate = `{ "parameters": [ { "type": "string", - "format": "uuid", - "description": "Organization ID", + "description": "Organization name or ID", "name": "organization", "in": "path", "required": true diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 389480f4e83..fb2e782f3bd 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -1153,7 +1153,11 @@ "in": "query" }, { - "type": "string", + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "multi", "description": "Filter by label as key:value. Repeat for multiple (AND logic).", "name": "label", "in": "query" @@ -5971,8 +5975,7 @@ "parameters": [ { "type": "string", - "format": "uuid", - "description": "Organization ID", + "description": "Organization name or ID", "name": "organization", "in": "path", "required": true @@ -6004,8 +6007,7 @@ "parameters": [ { "type": "string", - "format": "uuid", - "description": "Organization ID", + "description": "Organization name or ID", "name": "organization", "in": "path", "required": true @@ -6044,8 +6046,7 @@ "parameters": [ { "type": "string", - "format": "uuid", - "description": "Organization ID", + "description": "Organization name or ID", "name": "organization", "in": "path", "required": true @@ -6080,8 +6081,7 @@ "parameters": [ { "type": "string", - "format": "uuid", - "description": "Organization ID", + "description": "Organization name or ID", "name": "organization", "in": "path", "required": true @@ -6115,8 +6115,7 @@ "parameters": [ { "type": "string", - "format": "uuid", - "description": "Organization ID", + "description": "Organization name or ID", "name": "organization", "in": "path", "required": true @@ -6163,8 +6162,7 @@ "parameters": [ { "type": "string", - "format": "uuid", - "description": "Organization ID", + "description": "Organization name or ID", "name": "organization", "in": "path", "required": true @@ -6200,8 +6198,7 @@ "parameters": [ { "type": "string", - "format": "uuid", - "description": "Organization ID", + "description": "Organization name or ID", "name": "organization", "in": "path", "required": true @@ -6244,8 +6241,7 @@ "parameters": [ { "type": "string", - "format": "uuid", - "description": "Organization ID", + "description": "Organization name or ID", "name": "organization", "in": "path", "required": true diff --git a/coderd/exp_chats.go b/coderd/exp_chats.go index d59912f3d61..7aad8d0a439 100644 --- a/coderd/exp_chats.go +++ b/coderd/exp_chats.go @@ -393,7 +393,7 @@ func (api *API) chatsByWorkspace(rw http.ResponseWriter, r *http.Request) { // @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" diff --git a/coderd/mcp.go b/coderd/mcp.go index 4add6de3fde..445e32e57a5 100644 --- a/coderd/mcp.go +++ b/coderd/mcp.go @@ -145,7 +145,7 @@ 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/v2/organizations/{organization}/mcp-servers [get] // @@ -284,7 +284,7 @@ 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/v2/organizations/{organization}/mcp-servers [post] @@ -510,7 +510,7 @@ 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/v2/organizations/{organization}/mcp-servers/{mcpserverconfig} [get] @@ -596,7 +596,7 @@ 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 @@ -960,7 +960,7 @@ 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/v2/organizations/{organization}/mcp-servers/{mcpserverconfig} [delete] @@ -1018,7 +1018,7 @@ func (api *API) deleteMCPServerConfig(rw http.ResponseWriter, r *http.Request) { // @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/v2/organizations/{organization}/mcp-servers/{mcpserverconfig}/oauth2/connect [get] diff --git a/coderd/mcp_acl.go b/coderd/mcp_acl.go index 1d7ef27649f..ea53a4fc0b0 100644 --- a/coderd/mcp_acl.go +++ b/coderd/mcp_acl.go @@ -26,7 +26,7 @@ import ( // @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/v2/organizations/{organization}/mcp-servers/{mcpserverconfig}/acl [get] @@ -60,7 +60,7 @@ func (api *API) mcpServerConfigACL(rw http.ResponseWriter, r *http.Request) { // @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 diff --git a/docs/reference/api/chats.md b/docs/reference/api/chats.md index b10034a5d47..c0af4491c6f 100644 --- a/docs/reference/api/chats.md +++ b/docs/reference/api/chats.md @@ -54,13 +54,13 @@ curl -X GET http://coder-server:8080/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; quote multi-word values; cannot be combined with title, pr_title, or pr; a value that tokenizes to no searchable words 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). | -| `after_id` | query | string(uuid) | false | After ID | -| `limit` | query | integer | false | Page limit | -| `offset` | query | integer | false | Page offset | +| 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; quote multi-word values; cannot be combined with title, pr_title, or pr; a value that tokenizes to no searchable words 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 diff --git a/docs/reference/api/mcp.md b/docs/reference/api/mcp.md index 432e730db05..29295af0d40 100644 --- a/docs/reference/api/mcp.md +++ b/docs/reference/api/mcp.md @@ -88,9 +88,9 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/mcp-ser ### Parameters -| Name | In | Type | Required | Description | -|----------------|------|--------------|----------|-----------------| -| `organization` | path | string(uuid) | true | Organization ID | +| Name | In | Type | Required | Description | +|----------------|------|--------|----------|-------------------------| +| `organization` | path | string | true | Organization name or ID | ### Example responses @@ -234,7 +234,7 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/mcp-se | Name | In | Type | Required | Description | |----------------|------|------------------------------------------------------------------------------------------|----------|----------------------------------| -| `organization` | path | string(uuid) | true | Organization ID | +| `organization` | path | string | true | Organization name or ID | | `body` | body | [codersdk.CreateMCPServerConfigRequest](schemas.md#codersdkcreatemcpserverconfigrequest) | true | Create MCP server config request | ### Example responses @@ -301,10 +301,10 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/mcp-ser ### Parameters -| Name | In | Type | Required | Description | -|-------------------|------|--------------|----------|----------------------| -| `organization` | path | string(uuid) | true | Organization ID | -| `mcpserverconfig` | path | string(uuid) | true | MCP server config ID | +| Name | In | Type | Required | Description | +|-------------------|------|--------------|----------|-------------------------| +| `organization` | path | string | true | Organization name or ID | +| `mcpserverconfig` | path | string(uuid) | true | MCP server config ID | ### Example responses @@ -369,10 +369,10 @@ curl -X DELETE http://coder-server:8080/api/v2/organizations/{organization}/mcp- ### Parameters -| Name | In | Type | Required | Description | -|-------------------|------|--------------|----------|----------------------| -| `organization` | path | string(uuid) | true | Organization ID | -| `mcpserverconfig` | path | string(uuid) | true | MCP server config ID | +| Name | In | Type | Required | Description | +|-------------------|------|--------------|----------|-------------------------| +| `organization` | path | string | true | Organization name or ID | +| `mcpserverconfig` | path | string(uuid) | true | MCP server config ID | ### Responses @@ -437,7 +437,7 @@ curl -X PATCH http://coder-server:8080/api/v2/organizations/{organization}/mcp-s | Name | In | Type | Required | Description | |-------------------|------|------------------------------------------------------------------------------------------|----------|----------------------------------| -| `organization` | path | string(uuid) | true | Organization ID | +| `organization` | path | string | true | Organization name or ID | | `mcpserverconfig` | path | string(uuid) | true | MCP server config ID | | `body` | body | [codersdk.UpdateMCPServerConfigRequest](schemas.md#codersdkupdatemcpserverconfigrequest) | true | Update MCP server config request | @@ -505,10 +505,10 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/mcp-ser ### Parameters -| Name | In | Type | Required | Description | -|-------------------|------|--------------|----------|----------------------| -| `organization` | path | string(uuid) | true | Organization ID | -| `mcpserverconfig` | path | string(uuid) | true | MCP server config ID | +| Name | In | Type | Required | Description | +|-------------------|------|--------------|----------|-------------------------| +| `organization` | path | string | true | Organization name or ID | +| `mcpserverconfig` | path | string(uuid) | true | MCP server config ID | ### Example responses @@ -599,7 +599,7 @@ curl -X PATCH http://coder-server:8080/api/v2/organizations/{organization}/mcp-s | Name | In | Type | Required | Description | |-------------------|------|------------------------------------------------------------------------------------------------|----------|--------------------------------------| -| `organization` | path | string(uuid) | true | Organization ID | +| `organization` | path | string | true | Organization name or ID | | `mcpserverconfig` | path | string(uuid) | true | MCP server config ID | | `body` | body | [codersdk.UpdateMCPServerConfigACLRequest](schemas.md#codersdkupdatemcpserverconfigaclrequest) | true | Update MCP server config ACL request | @@ -625,10 +625,10 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/mcp-ser ### Parameters -| Name | In | Type | Required | Description | -|-------------------|------|--------------|----------|----------------------| -| `organization` | path | string(uuid) | true | Organization ID | -| `mcpserverconfig` | path | string(uuid) | true | MCP server config ID | +| Name | In | Type | Required | Description | +|-------------------|------|--------------|----------|-------------------------| +| `organization` | path | string | true | Organization name or ID | +| `mcpserverconfig` | path | string(uuid) | true | MCP server config ID | ### Responses From 61dd57fcb49aac4801d097c32e3937801154d350 Mon Sep 17 00:00:00 2001 From: Michael Suchacz <203725896+ibetitsmike@users.noreply.github.com> Date: Tue, 25 Aug 2026 00:59:50 +0000 Subject: [PATCH 05/15] fix: reduce chat API promotion churn --- coderd/apidoc/docs.go | 163 +- coderd/apidoc/swagger.json | 163 +- coderd/chat_routes_test.go | 172 +- coderd/exp_chats.go | 19 + coderd/exp_chats_acl.go | 2 + coderd/exp_chats_model_acl.go | 2 + coderd/mcp.go | 8 + coderd/mcp_acl.go | 2 + docs/reference/api/chats.md | 3236 ++++----------------------------- docs/reference/api/mcp.md | 633 ------- 10 files changed, 655 insertions(+), 3745 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 92dd2cbd2d1..28bcd2a2cbf 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -1442,7 +1442,10 @@ const docTemplate = `{ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } } }, "/api/v2/chats/config/auto-archive-days": { @@ -1747,7 +1750,10 @@ const docTemplate = `{ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } }, "put": { "consumes": [ @@ -1778,7 +1784,10 @@ const docTemplate = `{ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } } }, "/api/v2/chats/config/system-prompt": { @@ -1859,7 +1868,10 @@ const docTemplate = `{ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } } }, "/api/v2/chats/config/user-compaction-thresholds/{modelConfig}": { @@ -1905,7 +1917,10 @@ const docTemplate = `{ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } }, "delete": { "tags": [ @@ -1931,7 +1946,10 @@ const docTemplate = `{ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } } }, "/api/v2/chats/config/user-debug-logging": { @@ -2254,6 +2272,9 @@ const docTemplate = `{ "200": { "description": "OK" } + }, + "x-apidocgen": { + "skip": true } } }, @@ -2289,7 +2310,10 @@ const docTemplate = `{ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } } }, "/api/v2/chats/models": { @@ -2448,7 +2472,10 @@ const docTemplate = `{ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } }, "patch": { "consumes": [ @@ -2487,7 +2514,10 @@ const docTemplate = `{ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } } }, "/api/v2/chats/{chat}/compact": { @@ -2523,7 +2553,10 @@ const docTemplate = `{ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } } }, "/api/v2/chats/{chat}/context": { @@ -3086,7 +3119,10 @@ const docTemplate = `{ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } } }, "/api/v2/chats/{chat}/title/propose": { @@ -5047,7 +5083,10 @@ const docTemplate = `{ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } } }, "/api/v2/mcp/servers/{mcpServer}/oauth2/disconnect": { @@ -5082,7 +5121,10 @@ const docTemplate = `{ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } } }, "/api/v2/notifications/custom": { @@ -6113,7 +6155,10 @@ const docTemplate = `{ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } } }, "/api/v2/organizations/{organization}/chats/model-overrides/{context}": { @@ -6173,7 +6218,10 @@ const docTemplate = `{ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } } }, "/api/v2/organizations/{organization}/chats/models": { @@ -6207,7 +6255,10 @@ const docTemplate = `{ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } }, "post": { "consumes": [ @@ -6251,7 +6302,10 @@ const docTemplate = `{ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } } }, "/api/v2/organizations/{organization}/chats/models/{model}": { @@ -6292,7 +6346,10 @@ const docTemplate = `{ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } }, "delete": { "tags": [ @@ -6325,7 +6382,10 @@ const docTemplate = `{ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } }, "patch": { "consumes": [ @@ -6376,7 +6436,10 @@ const docTemplate = `{ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } } }, "/api/v2/organizations/{organization}/chats/models/{model}/acl": { @@ -6418,7 +6481,10 @@ const docTemplate = `{ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } }, "patch": { "consumes": [ @@ -6464,7 +6530,10 @@ const docTemplate = `{ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } } }, "/api/v2/organizations/{organization}/groups": { @@ -6785,7 +6854,10 @@ const docTemplate = `{ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } }, "post": { "consumes": [ @@ -6829,7 +6901,10 @@ const docTemplate = `{ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } } }, "/api/v2/organizations/{organization}/mcp-servers/{mcpserverconfig}": { @@ -6871,7 +6946,10 @@ const docTemplate = `{ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } }, "delete": { "tags": [ @@ -6905,7 +6983,10 @@ const docTemplate = `{ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } }, "patch": { "consumes": [ @@ -6957,7 +7038,10 @@ const docTemplate = `{ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } } }, "/api/v2/organizations/{organization}/mcp-servers/{mcpserverconfig}/acl": { @@ -6999,7 +7083,10 @@ const docTemplate = `{ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } }, "patch": { "consumes": [ @@ -7045,7 +7132,10 @@ const docTemplate = `{ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } } }, "/api/v2/organizations/{organization}/mcp-servers/{mcpserverconfig}/oauth2/connect": { @@ -7081,7 +7171,10 @@ const docTemplate = `{ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } } }, "/api/v2/organizations/{organization}/members": { @@ -7452,7 +7545,10 @@ const docTemplate = `{ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } } }, "/api/v2/organizations/{organization}/members/{user}/chats/model-overrides/{context}": { @@ -7511,7 +7607,10 @@ const docTemplate = `{ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } } }, "/api/v2/organizations/{organization}/members/{user}/roles": { diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index fb2e782f3bd..5fc08871ec1 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -1263,7 +1263,10 @@ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } } }, "/api/v2/chats/config/auto-archive-days": { @@ -1524,7 +1527,10 @@ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } }, "put": { "consumes": ["application/json"], @@ -1551,7 +1557,10 @@ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } } }, "/api/v2/chats/config/system-prompt": { @@ -1620,7 +1629,10 @@ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } } }, "/api/v2/chats/config/user-compaction-thresholds/{modelConfig}": { @@ -1660,7 +1672,10 @@ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } }, "delete": { "tags": ["Chats"], @@ -1684,7 +1699,10 @@ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } } }, "/api/v2/chats/config/user-debug-logging": { @@ -1973,6 +1991,9 @@ "200": { "description": "OK" } + }, + "x-apidocgen": { + "skip": true } } }, @@ -2004,7 +2025,10 @@ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } } }, "/api/v2/chats/models": { @@ -2143,7 +2167,10 @@ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } }, "patch": { "consumes": ["application/json"], @@ -2178,7 +2205,10 @@ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } } }, "/api/v2/chats/{chat}/compact": { @@ -2210,7 +2240,10 @@ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } } }, "/api/v2/chats/{chat}/context": { @@ -2715,7 +2748,10 @@ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } } }, "/api/v2/chats/{chat}/title/propose": { @@ -4453,7 +4489,10 @@ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } } }, "/api/v2/mcp/servers/{mcpServer}/oauth2/disconnect": { @@ -4484,7 +4523,10 @@ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } } }, "/api/v2/notifications/custom": { @@ -5390,7 +5432,10 @@ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } } }, "/api/v2/organizations/{organization}/chats/model-overrides/{context}": { @@ -5444,7 +5489,10 @@ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } } }, "/api/v2/organizations/{organization}/chats/models": { @@ -5474,7 +5522,10 @@ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } }, "post": { "consumes": ["application/json"], @@ -5512,7 +5563,10 @@ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } } }, "/api/v2/organizations/{organization}/chats/models/{model}": { @@ -5549,7 +5603,10 @@ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } }, "delete": { "tags": ["Chats"], @@ -5580,7 +5637,10 @@ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } }, "patch": { "consumes": ["application/json"], @@ -5625,7 +5685,10 @@ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } } }, "/api/v2/organizations/{organization}/chats/models/{model}/acl": { @@ -5663,7 +5726,10 @@ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } }, "patch": { "consumes": ["application/json"], @@ -5705,7 +5771,10 @@ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } } }, "/api/v2/organizations/{organization}/groups": { @@ -5996,7 +6065,10 @@ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } }, "post": { "consumes": ["application/json"], @@ -6034,7 +6106,10 @@ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } } }, "/api/v2/organizations/{organization}/mcp-servers/{mcpserverconfig}": { @@ -6072,7 +6147,10 @@ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } }, "delete": { "tags": ["MCP"], @@ -6104,7 +6182,10 @@ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } }, "patch": { "consumes": ["application/json"], @@ -6150,7 +6231,10 @@ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } } }, "/api/v2/organizations/{organization}/mcp-servers/{mcpserverconfig}/acl": { @@ -6188,7 +6272,10 @@ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } }, "patch": { "consumes": ["application/json"], @@ -6230,7 +6317,10 @@ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } } }, "/api/v2/organizations/{organization}/mcp-servers/{mcpserverconfig}/oauth2/connect": { @@ -6264,7 +6354,10 @@ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } } }, "/api/v2/organizations/{organization}/members": { @@ -6597,7 +6690,10 @@ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } } }, "/api/v2/organizations/{organization}/members/{user}/chats/model-overrides/{context}": { @@ -6648,7 +6744,10 @@ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "skip": true + } } }, "/api/v2/organizations/{organization}/members/{user}/roles": { diff --git a/coderd/chat_routes_test.go b/coderd/chat_routes_test.go index 9090f916758..40ff941ef62 100644 --- a/coderd/chat_routes_test.go +++ b/coderd/chat_routes_test.go @@ -2,10 +2,8 @@ package coderd_test import ( "net/http" - "strings" "testing" - "github.com/go-chi/chi/v5" "github.com/stretchr/testify/require" "github.com/coder/coder/v2/coderd/coderdtest" @@ -15,171 +13,39 @@ import ( func TestChatRoutesCompatibility(t *testing.T) { t.Parallel() - client, _, api := coderdtest.NewWithAPI(t, nil) + ctx := testutil.Context(t, testutil.WaitLong) + client := coderdtest.New(t, nil) coderdtest.CreateFirstUser(t, client) - promoted := []string{ - http.MethodGet + " /users/{user}/ai-provider-keys", - http.MethodPut + " /users/{user}/ai-provider-keys/{aiProvider}", - http.MethodDelete + " /users/{user}/ai-provider-keys/{aiProvider}", - http.MethodGet + " /chats/files/{file}/download", - http.MethodGet + " /organizations/{organization}/mcp-servers", - http.MethodPost + " /organizations/{organization}/mcp-servers", - http.MethodGet + " /organizations/{organization}/mcp-servers/{mcpserverconfig}", - http.MethodPatch + " /organizations/{organization}/mcp-servers/{mcpserverconfig}", - http.MethodDelete + " /organizations/{organization}/mcp-servers/{mcpserverconfig}", - http.MethodGet + " /organizations/{organization}/mcp-servers/{mcpserverconfig}/acl", - http.MethodPatch + " /organizations/{organization}/mcp-servers/{mcpserverconfig}/acl", - http.MethodGet + " /organizations/{organization}/mcp-servers/{mcpserverconfig}/oauth2/connect", - http.MethodGet + " /organizations/{organization}/chats/model-overrides", - http.MethodPut + " /organizations/{organization}/chats/model-overrides/{context}", - http.MethodGet + " /organizations/{organization}/members/{user}/chats/model-overrides", - http.MethodPut + " /organizations/{organization}/members/{user}/chats/model-overrides/{context}", - http.MethodGet + " /organizations/{organization}/chats/models", - http.MethodPost + " /organizations/{organization}/chats/models", - http.MethodGet + " /organizations/{organization}/chats/models/{model}", - http.MethodPatch + " /organizations/{organization}/chats/models/{model}", - http.MethodDelete + " /organizations/{organization}/chats/models/{model}", - http.MethodGet + " /organizations/{organization}/chats/models/{model}/acl", - http.MethodPatch + " /organizations/{organization}/chats/models/{model}/acl", - http.MethodGet + " /chats/models", - http.MethodGet + " /chats/by-workspace", - http.MethodGet + " /chats", - http.MethodPost + " /chats", - http.MethodGet + " /chats/watch", - http.MethodPost + " /chats/files", - http.MethodPost + " /chats/files/{file}/download-url", - http.MethodGet + " /chats/files/{file}", - http.MethodGet + " /chats/config/system-prompt", - http.MethodPut + " /chats/config/system-prompt", - http.MethodGet + " /chats/config/plan-mode-instructions", - http.MethodPut + " /chats/config/plan-mode-instructions", - http.MethodGet + " /chats/config/personal-model-overrides", - http.MethodPut + " /chats/config/personal-model-overrides", - http.MethodGet + " /chats/config/debug-logging", - http.MethodPut + " /chats/config/debug-logging", - http.MethodGet + " /chats/config/user-debug-logging", - http.MethodPut + " /chats/config/user-debug-logging", - http.MethodGet + " /chats/config/user-prompt", - http.MethodPut + " /chats/config/user-prompt", - http.MethodGet + " /chats/config/user-compaction-thresholds", - http.MethodPut + " /chats/config/user-compaction-thresholds/{modelConfig}", - http.MethodDelete + " /chats/config/user-compaction-thresholds/{modelConfig}", - http.MethodGet + " /chats/config/workspace-ttl", - http.MethodPut + " /chats/config/workspace-ttl", - http.MethodGet + " /chats/config/retention-days", - http.MethodPut + " /chats/config/retention-days", - http.MethodGet + " /chats/config/debug-retention-days", - http.MethodPut + " /chats/config/debug-retention-days", - http.MethodGet + " /chats/config/auto-archive-days", - http.MethodPut + " /chats/config/auto-archive-days", - http.MethodGet + " /chats/{chat}/acl", - http.MethodPatch + " /chats/{chat}/acl", - http.MethodGet + " /chats/{chat}", - http.MethodPatch + " /chats/{chat}", - http.MethodGet + " /chats/{chat}/cost", - http.MethodGet + " /chats/{chat}/messages", - http.MethodPost + " /chats/{chat}/messages", - http.MethodPatch + " /chats/{chat}/messages/{message}", - http.MethodGet + " /chats/{chat}/prompts", - http.MethodGet + " /chats/{chat}/stream", - http.MethodGet + " /chats/{chat}/stream/parts", - http.MethodGet + " /chats/{chat}/stream/git", - http.MethodPost + " /chats/{chat}/interrupt", - http.MethodPost + " /chats/{chat}/compact", - http.MethodPost + " /chats/{chat}/reconcile-invalid", - http.MethodPost + " /chats/{chat}/tool-results", - http.MethodPost + " /chats/{chat}/title/regenerate", - http.MethodPost + " /chats/{chat}/title/propose", - http.MethodGet + " /chats/{chat}/diff", - http.MethodPut + " /chats/{chat}/context", - http.MethodDelete + " /chats/{chat}/queue/{queuedMessage}", - http.MethodPost + " /chats/{chat}/queue/{queuedMessage}/promote", - http.MethodGet + " /mcp/servers/{mcpServer}/oauth2/callback", - http.MethodDelete + " /mcp/servers/{mcpServer}/oauth2/disconnect", - } - - for name, router := range map[string]chi.Router{ - "experimental": api.ExperimentalHandler, - "v2": api.APIHandler, - } { - routes := walkRoutes(t, router) - for _, route := range promoted { - require.Contains(t, routes, route, "%s route tree", name) - } - } - - v2Routes := walkRoutes(t, api.APIHandler) - for _, excluded := range []string{ - http.MethodGet + " /chats/providers", - http.MethodPost + " /chats/providers", - http.MethodPatch + " /chats/providers/{providerConfig}", - http.MethodDelete + " /chats/providers/{providerConfig}", - http.MethodGet + " /chats/user-provider-configs", - http.MethodPut + " /chats/user-provider-configs/{providerConfig}", - http.MethodDelete + " /chats/user-provider-configs/{providerConfig}", - http.MethodGet + " /chats/{chat}/debug/runs", - http.MethodGet + " /chats/{chat}/debug/runs/{debugRun}", - http.MethodGet + " /chats/config/computer-use-provider", - http.MethodPut + " /chats/config/computer-use-provider", - http.MethodGet + " /chats/config/advisor", - http.MethodPut + " /chats/config/advisor", - http.MethodGet + " /chats/{chat}/stream/desktop", - http.MethodGet + " /chats/model-configs", - http.MethodPost + " /chats/model-configs", + for _, route := range []string{ + "/api/experimental/chats", + "/api/experimental/chats/config/system-prompt", + "/api/v2/chats", + "/api/v2/chats/config/system-prompt", } { - require.NotContains(t, v2Routes, excluded) - } - for route := range v2Routes { - require.NotContains(t, route, " /mcp/http") + res, err := client.Request(ctx, http.MethodGet, route, nil) + require.NoError(t, err) + _ = res.Body.Close() + require.Equal(t, http.StatusOK, res.StatusCode, route) } - ctx := testutil.Context(t, testutil.WaitLong) - for _, excluded := range []struct { + for _, route := range []struct { method string path string }{ - {http.MethodGet, "/api/v2/chats/config/computer-use-provider"}, - {http.MethodPut, "/api/v2/chats/config/computer-use-provider"}, - {http.MethodGet, "/api/v2/chats/config/advisor"}, - {http.MethodPut, "/api/v2/chats/config/advisor"}, - {http.MethodPost, "/api/v2/mcp/http/server"}, - {http.MethodGet, "/api/v2/chats/00000000-0000-0000-0000-000000000000/debug/runs"}, - {http.MethodGet, "/api/v2/chats/00000000-0000-0000-0000-000000000000/stream/desktop"}, - // Experimental-only segments that would otherwise fall into the - // {chat} wildcard and 400 on UUID parsing. {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/providers/00000000-0000-0000-0000-000000000000"}, {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, "/api/v2/chats/00000000-0000-0000-0000-000000000000/debug/runs"}, + {http.MethodGet, "/api/v2/chats/00000000-0000-0000-0000-000000000000/stream/desktop"}, + {http.MethodPost, "/api/v2/mcp/http/server"}, } { - res, err := client.Request(ctx, excluded.method, excluded.path, nil) - require.NoError(t, err) - _ = res.Body.Close() - require.Equal(t, http.StatusNotFound, res.StatusCode, "%s %s", excluded.method, excluded.path) - } - - for _, path := range []string{ - "/api/v2/chats", - "/api/v2/chats/config/system-prompt", - } { - res, err := client.Request(ctx, http.MethodGet, path, nil) + res, err := client.Request(ctx, route.method, route.path, nil) require.NoError(t, err) _ = res.Body.Close() - require.Equal(t, http.StatusOK, res.StatusCode, path) + require.Equal(t, http.StatusNotFound, res.StatusCode, "%s %s", route.method, route.path) } } - -func walkRoutes(t *testing.T, router chi.Router) map[string]struct{} { - t.Helper() - - routes := make(map[string]struct{}) - err := chi.Walk(router, func(method, route string, _ http.Handler, _ ...func(http.Handler) http.Handler) error { - route = strings.TrimSuffix(route, "/") - routes[method+" "+route] = struct{}{} - return nil - }) - require.NoError(t, err) - return routes -} diff --git a/coderd/exp_chats.go b/coderd/exp_chats.go index 7aad8d0a439..f09efba7a5b 100644 --- a/coderd/exp_chats.go +++ b/coderd/exp_chats.go @@ -326,6 +326,7 @@ type chatsByWorkspaceResponse map[uuid.UUID]uuid.UUID // @Produce json // @Success 200 {object} chatsByWorkspaceResponse // @Router /api/v2/chats/by-workspace [get] +// @x-apidocgen {"skip": true} func (api *API) chatsByWorkspace(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() @@ -3279,6 +3280,7 @@ func (api *API) interruptChat(rw http.ResponseWriter, r *http.Request) { // @Produce json // @Success 200 {object} codersdk.Chat // @Router /api/v2/chats/{chat}/compact [post] +// @x-apidocgen {"skip": true} // @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 @@ -4610,6 +4612,7 @@ func readChatModelOverrideContext( // @Param organization path string true "Organization name or ID" // @Success 200 {object} codersdk.ChatModelOverridesResponse // @Router /api/v2/organizations/{organization}/chats/model-overrides [get] +// @x-apidocgen {"skip": true} // //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) { @@ -4647,6 +4650,7 @@ func (api *API) getOrganizationChatModelOverrides(rw http.ResponseWriter, r *htt // @Param request body codersdk.UpdateChatModelOverrideRequest true "Model override" // @Success 200 {object} codersdk.ChatModelOverrideResponse // @Router /api/v2/organizations/{organization}/chats/model-overrides/{context} [put] +// @x-apidocgen {"skip": true} func (api *API) putOrganizationChatModelOverride(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() organization := httpmw.OrganizationParam(r) @@ -4958,6 +4962,7 @@ func (api *API) putChatPersonalModelOverridesAdminSettings(rw http.ResponseWrite // @Param user path string true "User name, ID, or me" // @Success 200 {object} codersdk.UserChatPersonalModelOverridesResponse // @Router /api/v2/organizations/{organization}/members/{user}/chats/model-overrides [get] +// @x-apidocgen {"skip": true} // //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) { @@ -5036,6 +5041,7 @@ func (api *API) getUserChatPersonalModelOverrides(rw http.ResponseWriter, r *htt // @Param request body codersdk.UpdateUserChatPersonalModelOverrideRequest true "Personal model override" // @Success 204 // @Router /api/v2/organizations/{organization}/members/{user}/chats/model-overrides/{context} [put] +// @x-apidocgen {"skip": true} func (api *API) putUserChatPersonalModelOverride(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() apiKey := httpmw.APIKey(r) @@ -5594,6 +5600,7 @@ func (api *API) putChatWorkspaceTTL(rw http.ResponseWriter, r *http.Request) { // @Produce json // @Success 200 {object} codersdk.ChatRetentionDaysResponse // @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. func (api *API) getChatRetentionDays(rw http.ResponseWriter, r *http.Request) { @@ -5623,6 +5630,7 @@ const retentionDaysMaximum = 3650 // ~10 years // @Param request body codersdk.UpdateChatRetentionDaysRequest true "Request body" // @Success 204 // @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() aReq, commitAudit := api.initChatOperationalSettingsAudit(rw, r) @@ -5903,6 +5911,7 @@ func (api *API) putUserChatCustomPrompt(rw http.ResponseWriter, r *http.Request) // @Produce json // @Success 200 {object} codersdk.UserChatCompactionThresholds // @Router /api/v2/chats/config/user-compaction-thresholds [get] +// @x-apidocgen {"skip": true} // //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) { @@ -5971,6 +5980,7 @@ func (api *API) getUserChatCompactionThresholds(rw http.ResponseWriter, r *http. // @Produce json // @Success 200 {object} codersdk.UserChatCompactionThreshold // @Router /api/v2/chats/config/user-compaction-thresholds/{modelConfig} [put] +// @x-apidocgen {"skip": true} func (api *API) putUserChatCompactionThreshold(rw http.ResponseWriter, r *http.Request) { var ( ctx = r.Context() @@ -6048,6 +6058,7 @@ func (api *API) putUserChatCompactionThreshold(rw http.ResponseWriter, r *http.R // @Param modelConfig path string true "Model config ID" // @Success 204 // @Router /api/v2/chats/config/user-compaction-thresholds/{modelConfig} [delete] +// @x-apidocgen {"skip": true} func (api *API) deleteUserChatCompactionThreshold(rw http.ResponseWriter, r *http.Request) { var ( ctx = r.Context() @@ -6243,6 +6254,7 @@ func (c ChatFileDownloadClaims) Validate(expected jwt.Expected) error { // @Param file path string true "File ID" format(uuid) // @Success 200 {object} codersdk.ChatFileDownloadURLResponse // @Router /api/v2/chats/files/{file}/download-url [post] +// @x-apidocgen {"skip": true} func (api *API) postChatFileDownloadURL(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() fileID, err := uuid.Parse(chi.URLParam(r, "file")) @@ -6309,6 +6321,7 @@ func (api *API) postChatFileDownloadURL(rw http.ResponseWriter, r *http.Request) // @Param token query string true "Signed download token" // @Success 200 // @Router /api/v2/chats/files/{file}/download [get] +// @x-apidocgen {"skip": true} func (api *API) downloadChatFile(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() fileID, err := uuid.Parse(chi.URLParam(r, "file")) @@ -6932,6 +6945,7 @@ func (api *API) listDefaultOrganizationChatModels(rw http.ResponseWriter, r *htt // @Param organization path string true "Organization name or ID" // @Success 200 {object} codersdk.OrganizationChatModelsResponse // @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() organization := httpmw.OrganizationParam(r) @@ -7091,6 +7105,7 @@ func chatModelConfigRBACObject(config database.ChatModelConfig) rbac.Object { // @Param model path string true "Model ID" // @Success 200 {object} codersdk.ChatModel // @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. func (api *API) getChatModelConfig(rw http.ResponseWriter, r *http.Request) { @@ -7194,6 +7209,7 @@ func (api *API) auditChatModelConfigTransitions( // @Param request body codersdk.CreateChatModelRequest true "Model" // @Success 201 {object} codersdk.ChatModel // @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() apiKey := httpmw.APIKey(r) @@ -7411,6 +7427,7 @@ func (api *API) createChatModelConfig(rw http.ResponseWriter, r *http.Request) { // @Param request body codersdk.UpdateChatModelRequest true "Model updates" // @Success 200 {object} codersdk.ChatModel // @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() apiKey := httpmw.APIKey(r) @@ -7663,6 +7680,7 @@ func (api *API) updateChatModelConfig(rw http.ResponseWriter, r *http.Request) { // @Param model path string true "Model ID" // @Success 204 // @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() existing := httpmw.ChatModelConfigParam(r) @@ -8265,6 +8283,7 @@ func (api *API) getChatDebugRun(rw http.ResponseWriter, r *http.Request) { // @Param chat path string true "Chat ID" format(uuid) // @Success 200 {array} codersdk.ChatStreamEvent // @Router /api/v2/chats/{chat}/stream/parts [get] +// @x-apidocgen {"skip": true} 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 ce761883bf6..cf0cc8d0d57 100644 --- a/coderd/exp_chats_acl.go +++ b/coderd/exp_chats_acl.go @@ -34,6 +34,7 @@ import ( // @Param chat path string true "Chat ID" format(uuid) // @Success 200 {object} codersdk.ChatACL // @Router /api/v2/chats/{chat}/acl [get] +// @x-apidocgen {"skip": true} // //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) { @@ -86,6 +87,7 @@ func (api *API) getChatACL(rw http.ResponseWriter, r *http.Request) { // @Param request body codersdk.UpdateChatACL true "Update chat ACL request" // @Success 204 // @Router /api/v2/chats/{chat}/acl [patch] +// @x-apidocgen {"skip": true} 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 91274b6c905..6a63757d6de 100644 --- a/coderd/exp_chats_model_acl.go +++ b/coderd/exp_chats_model_acl.go @@ -33,6 +33,7 @@ import ( // @Param model path string true "Model ID" format(uuid) // @Success 200 {object} codersdk.ChatModelACL // @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() config := httpmw.ChatModelConfigParam(r) @@ -62,6 +63,7 @@ func (*chatModelACLValidationError) Error() string { // @Param request body codersdk.UpdateChatModelACLRequest true "Sparse model ACL update" // @Success 204 // @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() apiKey := httpmw.APIKey(r) diff --git a/coderd/mcp.go b/coderd/mcp.go index 445e32e57a5..ffdb9e1d06a 100644 --- a/coderd/mcp.go +++ b/coderd/mcp.go @@ -148,6 +148,7 @@ func shouldRefreshOIDCToken(link database.UserLink) (bool, time.Time) { // @Param organization path string true "Organization name or ID" // @Success 200 {array} codersdk.MCPServerConfig // @Router /api/v2/organizations/{organization}/mcp-servers [get] +// @x-apidocgen {"skip": true} // //nolint:revive // HTTP handler writes to ResponseWriter. func (api *API) listMCPServerConfigs(rw http.ResponseWriter, r *http.Request) { @@ -288,6 +289,7 @@ func (api *API) mcpServerConfigReadInKeyScope(r *http.Request, organizationID uu // @Param request body codersdk.CreateMCPServerConfigRequest true "Create MCP server config request" // @Success 201 {object} codersdk.MCPServerConfig // @Router /api/v2/organizations/{organization}/mcp-servers [post] +// @x-apidocgen {"skip": true} // //nolint:revive // HTTP handler writes to ResponseWriter. func (api *API) createMCPServerConfig(rw http.ResponseWriter, r *http.Request) { @@ -514,6 +516,7 @@ func (api *API) createMCPServerConfig(rw http.ResponseWriter, r *http.Request) { // @Param mcpserverconfig path string true "MCP server config ID" format(uuid) // @Success 200 {object} codersdk.MCPServerConfig // @Router /api/v2/organizations/{organization}/mcp-servers/{mcpserverconfig} [get] +// @x-apidocgen {"skip": true} // //nolint:revive // HTTP handler writes to ResponseWriter. func (api *API) getMCPServerConfig(rw http.ResponseWriter, r *http.Request) { @@ -601,6 +604,7 @@ func (api *API) getMCPServerConfigForMutation(rw http.ResponseWriter, r *http.Re // @Param request body codersdk.UpdateMCPServerConfigRequest true "Update MCP server config request" // @Success 200 {object} codersdk.MCPServerConfig // @Router /api/v2/organizations/{organization}/mcp-servers/{mcpserverconfig} [patch] +// @x-apidocgen {"skip": true} // //nolint:revive // HTTP handler writes to ResponseWriter. func (api *API) updateMCPServerConfig(rw http.ResponseWriter, r *http.Request) { @@ -964,6 +968,7 @@ func (api *API) updateMCPServerConfig(rw http.ResponseWriter, r *http.Request) { // @Param mcpserverconfig path string true "MCP server config ID" format(uuid) // @Success 204 // @Router /api/v2/organizations/{organization}/mcp-servers/{mcpserverconfig} [delete] +// @x-apidocgen {"skip": true} func (api *API) deleteMCPServerConfig(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() auditor := api.Auditor.Load() @@ -1022,6 +1027,7 @@ func (api *API) deleteMCPServerConfig(rw http.ResponseWriter, r *http.Request) { // @Param mcpserverconfig path string true "MCP server config ID" format(uuid) // @Success 307 // @Router /api/v2/organizations/{organization}/mcp-servers/{mcpserverconfig}/oauth2/connect [get] +// @x-apidocgen {"skip": true} // //nolint:revive // HTTP handler writes to ResponseWriter. func (api *API) mcpServerOAuth2Connect(rw http.ResponseWriter, r *http.Request) { @@ -1114,6 +1120,7 @@ func (api *API) mcpServerOAuth2Connect(rw http.ResponseWriter, r *http.Request) // @Produce text/html // @Success 200 // @Router /api/v2/mcp/servers/{mcpServer}/oauth2/callback [get] +// @x-apidocgen {"skip": true} // //nolint:revive // HTTP handler writes to ResponseWriter. func (api *API) mcpServerOAuth2Callback(rw http.ResponseWriter, r *http.Request) { @@ -1325,6 +1332,7 @@ func (api *API) mcpServerOAuth2Callback(rw http.ResponseWriter, r *http.Request) // @Param mcpServer path string true "MCP server config ID" format(uuid) // @Success 200 {object} codersdk.MCPServerOAuth2DisconnectResponse // @Router /api/v2/mcp/servers/{mcpServer}/oauth2/disconnect [delete] +// @x-apidocgen {"skip": true} func (api *API) mcpServerOAuth2Disconnect(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() apiKey := httpmw.APIKey(r) diff --git a/coderd/mcp_acl.go b/coderd/mcp_acl.go index ea53a4fc0b0..6b793ed04c6 100644 --- a/coderd/mcp_acl.go +++ b/coderd/mcp_acl.go @@ -30,6 +30,7 @@ import ( // @Param mcpserverconfig path string true "MCP server config ID" format(uuid) // @Success 200 {object} codersdk.MCPServerConfigACL // @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() config := httpmw.MCPServerConfigParam(r) @@ -65,6 +66,7 @@ func (api *API) mcpServerConfigACL(rw http.ResponseWriter, r *http.Request) { // @Param request body codersdk.UpdateMCPServerConfigACLRequest true "Update MCP server config ACL request" // @Success 204 // @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() config := httpmw.MCPServerConfigParam(r) diff --git a/docs/reference/api/chats.md b/docs/reference/api/chats.md index c0af4491c6f..cd360d60241 100644 --- a/docs/reference/api/chats.md +++ b/docs/reference/api/chats.md @@ -556,44 +556,6 @@ curl -X POST http://coder-server:8080/api/v2/chats \ 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 @@ -925,72 +887,6 @@ curl -X PUT http://coder-server:8080/api/v2/chats/config/plan-mode-instructions To perform this operation, you must be authenticated. [Learn more](authentication.md). -## Get chat retention days - -### Code samples - -```sh -# Example request using curl -curl -X GET http://coder-server:8080/api/v2/chats/config/retention-days \ - -H 'Accept: application/json' \ - -H 'Coder-Session-Token: API_KEY' -``` - -`GET /api/v2/chats/config/retention-days` - -### Example responses - -> 200 Response - -```json -{ - "retention_days": 0 -} -``` - -### Responses - -| Status | Meaning | Description | Schema | -|--------|---------------------------------------------------------|-------------|------------------------------------------------------------------------------------| -| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.ChatRetentionDaysResponse](schemas.md#codersdkchatretentiondaysresponse) | - -To perform this operation, you must be authenticated. [Learn more](authentication.md). - -## Update chat retention days - -### Code samples - -```sh -# Example request using curl -curl -X PUT http://coder-server:8080/api/v2/chats/config/retention-days \ - -H 'Content-Type: application/json' \ - -H 'Coder-Session-Token: API_KEY' -``` - -`PUT /api/v2/chats/config/retention-days` - -> Body parameter - -```json -{ - "retention_days": 0 -} -``` - -### Parameters - -| Name | In | Type | Required | Description | -|--------|------|----------------------------------------------------------------------------------------------|----------|--------------| -| `body` | body | [codersdk.UpdateChatRetentionDaysRequest](schemas.md#codersdkupdatechatretentiondaysrequest) | 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 @@ -1060,116 +956,6 @@ curl -X PUT http://coder-server:8080/api/v2/chats/config/system-prompt \ To perform this operation, you must be authenticated. [Learn more](authentication.md). -## Get user chat compaction thresholds - -### Code samples - -```sh -# Example request using curl -curl -X GET http://coder-server:8080/api/v2/chats/config/user-compaction-thresholds \ - -H 'Accept: application/json' \ - -H 'Coder-Session-Token: API_KEY' -``` - -`GET /api/v2/chats/config/user-compaction-thresholds` - -### Example responses - -> 200 Response - -```json -{ - "thresholds": [ - { - "model_config_id": "f5fb4d91-62ca-4377-9ee6-5d43ba00d205", - "threshold_percent": 0 - } - ] -} -``` - -### Responses - -| Status | Meaning | Description | Schema | -|--------|---------------------------------------------------------|-------------|------------------------------------------------------------------------------------------| -| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.UserChatCompactionThresholds](schemas.md#codersdkuserchatcompactionthresholds) | - -To perform this operation, you must be authenticated. [Learn more](authentication.md). - -## Update user chat compaction threshold - -### Code samples - -```sh -# Example request using curl -curl -X PUT http://coder-server:8080/api/v2/chats/config/user-compaction-thresholds/{modelConfig} \ - -H 'Content-Type: application/json' \ - -H 'Accept: application/json' \ - -H 'Coder-Session-Token: API_KEY' -``` - -`PUT /api/v2/chats/config/user-compaction-thresholds/{modelConfig}` - -> Body parameter - -```json -{ - "threshold_percent": 100 -} -``` - -### Parameters - -| Name | In | Type | Required | Description | -|---------------|------|------------------------------------------------------------------------------------------------------------------|----------|-----------------| -| `modelConfig` | path | string | true | Model config ID | -| `body` | body | [codersdk.UpdateUserChatCompactionThresholdRequest](schemas.md#codersdkupdateuserchatcompactionthresholdrequest) | true | Request body | - -### Example responses - -> 200 Response - -```json -{ - "model_config_id": "f5fb4d91-62ca-4377-9ee6-5d43ba00d205", - "threshold_percent": 0 -} -``` - -### Responses - -| Status | Meaning | Description | Schema | -|--------|---------------------------------------------------------|-------------|----------------------------------------------------------------------------------------| -| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.UserChatCompactionThreshold](schemas.md#codersdkuserchatcompactionthreshold) | - -To perform this operation, you must be authenticated. [Learn more](authentication.md). - -## Delete user chat compaction threshold - -### Code samples - -```sh -# Example request using curl -curl -X DELETE http://coder-server:8080/api/v2/chats/config/user-compaction-thresholds/{modelConfig} \ - -H 'Coder-Session-Token: API_KEY' -``` - -`DELETE /api/v2/chats/config/user-compaction-thresholds/{modelConfig}` - -### Parameters - -| Name | In | Type | Required | Description | -|---------------|------|--------|----------|-----------------| -| `modelConfig` | path | string | true | Model config 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). - ## Get user chat debug logging setting ### Code samples @@ -1456,73 +1242,6 @@ curl -X GET http://coder-server:8080/api/v2/chats/files/{file} \ To perform this operation, you must be authenticated. [Learn more](authentication.md). -## Download chat file with signed token - -### Code samples - -```sh -# Example request using curl -curl -X GET http://coder-server:8080/api/v2/chats/files/{file}/download?token=string - -``` - -`GET /api/v2/chats/files/{file}/download` - -### Parameters - -| Name | In | Type | Required | Description | -|---------|-------|--------------|----------|-----------------------| -| `file` | path | string(uuid) | true | File ID | -| `token` | query | string | true | Signed download token | - -### Responses - -| Status | Meaning | Description | Schema | -|--------|---------------------------------------------------------|-------------|--------| -| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | | - -## Create chat file download URL - -### Code samples - -```sh -# Example request using curl -curl -X POST http://coder-server:8080/api/v2/chats/files/{file}/download-url \ - -H 'Accept: application/json' \ - -H 'Coder-Session-Token: API_KEY' -``` - -`POST /api/v2/chats/files/{file}/download-url` - -### Parameters - -| Name | In | Type | Required | Description | -|--------|------|--------------|----------|-------------| -| `file` | path | string(uuid) | true | File ID | - -### Example responses - -> 200 Response - -```json -{ - "expires_at": "2019-08-24T14:15:22Z", - "mime_type": "string", - "name": "string", - "sha256": "string", - "size_bytes": 0, - "url": "http://example.com" -} -``` - -### Responses - -| Status | Meaning | Description | Schema | -|--------|---------------------------------------------------------|-------------|----------------------------------------------------------------------------------------| -| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.ChatFileDownloadURLResponse](schemas.md#codersdkchatfiledownloadurlresponse) | - -To perform this operation, you must be authenticated. [Learn more](authentication.md). - ## List AI models and provider descriptors in the default organization ### Code samples @@ -2164,18 +1883,18 @@ curl -X PATCH http://coder-server:8080/api/v2/chats/{chat} \ To perform this operation, you must be authenticated. [Learn more](authentication.md). -## Get chat ACLs +## Refresh chat context ### Code samples ```sh # Example request using curl -curl -X GET http://coder-server:8080/api/v2/chats/{chat}/acl \ +curl -X PUT http://coder-server:8080/api/v2/chats/{chat}/context \ -H 'Accept: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`GET /api/v2/chats/{chat}/acl` +`PUT /api/v2/chats/{chat}/context` ### Parameters @@ -2189,134 +1908,10 @@ curl -X GET http://coder-server:8080/api/v2/chats/{chat}/acl \ ```json { - "groups": [ - { - "avatar_url": "http://example.com", - "display_name": "string", - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "members": [ - { - "avatar_url": "http://example.com", - "created_at": "2019-08-24T14:15:22Z", - "email": "user@example.com", - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "is_service_account": true, - "last_seen_at": "2019-08-24T14:15:22Z", - "login_type": "", - "name": "string", - "status": "active", - "theme_preference": "string", - "updated_at": "2019-08-24T14:15:22Z", - "username": "string" - } - ], - "name": "string", - "organization_display_name": "string", - "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", - "organization_name": "string", - "quota_allowance": 0, - "role": "read", - "source": "user", - "total_member_count": 0 - } - ], - "users": [ - { - "avatar_url": "http://example.com", - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "name": "string", - "role": "read", - "username": "string" - } - ] -} -``` - -### Responses - -| Status | Meaning | Description | Schema | -|--------|---------------------------------------------------------|-------------|------------------------------------------------| -| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.ChatACL](schemas.md#codersdkchatacl) | - -To perform this operation, you must be authenticated. [Learn more](authentication.md). - -## Update chat ACL - -### Code samples - -```sh -# Example request using curl -curl -X PATCH http://coder-server:8080/api/v2/chats/{chat}/acl \ - -H 'Content-Type: application/json' \ - -H 'Coder-Session-Token: API_KEY' -``` - -`PATCH /api/v2/chats/{chat}/acl` - -> Body parameter - -```json -{ - "group_roles": { - "property1": "read", - "property2": "read" - }, - "user_roles": { - "property1": "read", - "property2": "read" - } -} -``` - -### Parameters - -| Name | In | Type | Required | Description | -|--------|------|------------------------------------------------------------|----------|-------------------------| -| `chat` | path | string(uuid) | true | Chat ID | -| `body` | body | [codersdk.UpdateChatACL](schemas.md#codersdkupdatechatacl) | true | Update chat ACL request | - -### 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). - -## Compact chat - -### Code samples - -```sh -# Example request using curl -curl -X POST http://coder-server:8080/api/v2/chats/{chat}/compact \ - -H 'Accept: application/json' \ - -H 'Coder-Session-Token: API_KEY' -``` - -`POST /api/v2/chats/{chat}/compact` - -Requests a manual context compaction on an idle or errored -chat, clearing any stored error. The compaction runs -asynchronously through the chat worker and bypasses the -automatic usage threshold. - -### Parameters - -| Name | In | Type | Required | Description | -|--------|------|--------------|----------|-------------| -| `chat` | path | string(uuid) | true | Chat ID | - -### Example responses - -> 200 Response - -```json -{ - "agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e98978", - "archived": true, - "build_id": "bfb1f3fa-bf7b-43a5-9e0b-26cc050e44cb", - "children": [ + "agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e98978", + "archived": true, + "build_id": "bfb1f3fa-bf7b-43a5-9e0b-26cc050e44cb", + "children": [ { "agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e98978", "archived": true, @@ -2523,18 +2118,110 @@ automatic usage threshold. To perform this operation, you must be authenticated. [Learn more](authentication.md). -## Refresh chat context +## Get chat cost ### Code samples ```sh # Example request using curl -curl -X PUT http://coder-server:8080/api/v2/chats/{chat}/context \ +curl -X GET http://coder-server:8080/api/v2/chats/{chat}/cost \ -H 'Accept: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`PUT /api/v2/chats/{chat}/context` +`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 +returns that same total. + +Cost is derived from AI Gateway data, which is subject to its +own retention period, 60 days by default, configured +independently of chat retention. Spend for requests older than +that period is no longer reported, so a chat whose requests +have all been purged reports zero cost. + +### Parameters + +| Name | In | Type | Required | Description | +|--------|------|--------------|----------|-------------| +| `chat` | path | string(uuid) | true | Chat ID | + +### Example responses + +> 200 Response + +```json +{ + "chat_id": "efc9fe20-a1e5-4a8c-9c48-f1b30c1e4f86", + "request_count": 0, + "total_cost_micros": 0, + "unpriced_request_count": 0 +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +|--------|---------------------------------------------------------|-------------|--------------------------------------------------| +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.ChatCost](schemas.md#codersdkchatcost) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Get chat diff contents + +### Code samples + +```sh +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/chats/{chat}/diff \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /api/v2/chats/{chat}/diff` + +### Parameters + +| Name | In | Type | Required | Description | +|--------|------|--------------|----------|-------------| +| `chat` | path | string(uuid) | true | Chat ID | + +### Example responses + +> 200 Response + +```json +{ + "branch": "string", + "chat_id": "efc9fe20-a1e5-4a8c-9c48-f1b30c1e4f86", + "diff": "string", + "provider": "string", + "pull_request_url": "string", + "remote_origin": "string" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +|--------|---------------------------------------------------------|-------------|------------------------------------------------------------------| +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.ChatDiffContents](schemas.md#codersdkchatdiffcontents) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Interrupt chat + +### Code samples + +```sh +# Example request using curl +curl -X POST http://coder-server:8080/api/v2/chats/{chat}/interrupt \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`POST /api/v2/chats/{chat}/interrupt` ### Parameters @@ -2758,354 +2445,27 @@ curl -X PUT http://coder-server:8080/api/v2/chats/{chat}/context \ To perform this operation, you must be authenticated. [Learn more](authentication.md). -## Get chat cost - -### Code samples - -```sh -# Example request using curl -curl -X GET http://coder-server:8080/api/v2/chats/{chat}/cost \ - -H 'Accept: application/json' \ - -H 'Coder-Session-Token: API_KEY' -``` - -`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 -returns that same total. - -Cost is derived from AI Gateway data, which is subject to its -own retention period, 60 days by default, configured -independently of chat retention. Spend for requests older than -that period is no longer reported, so a chat whose requests -have all been purged reports zero cost. - -### Parameters - -| Name | In | Type | Required | Description | -|--------|------|--------------|----------|-------------| -| `chat` | path | string(uuid) | true | Chat ID | - -### Example responses - -> 200 Response - -```json -{ - "chat_id": "efc9fe20-a1e5-4a8c-9c48-f1b30c1e4f86", - "request_count": 0, - "total_cost_micros": 0, - "unpriced_request_count": 0 -} -``` - -### Responses - -| Status | Meaning | Description | Schema | -|--------|---------------------------------------------------------|-------------|--------------------------------------------------| -| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.ChatCost](schemas.md#codersdkchatcost) | - -To perform this operation, you must be authenticated. [Learn more](authentication.md). - -## Get chat diff contents +## List chat messages ### Code samples ```sh # Example request using curl -curl -X GET http://coder-server:8080/api/v2/chats/{chat}/diff \ +curl -X GET http://coder-server:8080/api/v2/chats/{chat}/messages \ -H 'Accept: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`GET /api/v2/chats/{chat}/diff` +`GET /api/v2/chats/{chat}/messages` ### Parameters -| Name | In | Type | Required | Description | -|--------|------|--------------|----------|-------------| -| `chat` | path | string(uuid) | true | Chat ID | - -### Example responses - -> 200 Response - -```json -{ - "branch": "string", - "chat_id": "efc9fe20-a1e5-4a8c-9c48-f1b30c1e4f86", - "diff": "string", - "provider": "string", - "pull_request_url": "string", - "remote_origin": "string" -} -``` - -### Responses - -| Status | Meaning | Description | Schema | -|--------|---------------------------------------------------------|-------------|------------------------------------------------------------------| -| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.ChatDiffContents](schemas.md#codersdkchatdiffcontents) | - -To perform this operation, you must be authenticated. [Learn more](authentication.md). - -## Interrupt chat - -### Code samples - -```sh -# Example request using curl -curl -X POST http://coder-server:8080/api/v2/chats/{chat}/interrupt \ - -H 'Accept: application/json' \ - -H 'Coder-Session-Token: API_KEY' -``` - -`POST /api/v2/chats/{chat}/interrupt` - -### Parameters - -| Name | In | Type | Required | Description | -|--------|------|--------------|----------|-------------| -| `chat` | path | string(uuid) | true | Chat ID | - -### Example responses - -> 200 Response - -```json -{ - "agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e98978", - "archived": true, - "build_id": "bfb1f3fa-bf7b-43a5-9e0b-26cc050e44cb", - "children": [ - { - "agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e98978", - "archived": true, - "build_id": "bfb1f3fa-bf7b-43a5-9e0b-26cc050e44cb", - "children": [], - "client_type": "ui", - "context": { - "dirty": true, - "dirty_since": "2019-08-24T14:15:22Z", - "error": "string", - "resources": [ - { - "error": "string", - "kind": "instruction_file", - "size_bytes": 0, - "skill_description": "string", - "skill_name": "string", - "source": "string", - "status": "ok", - "tools": [ - { - "description": "string", - "name": "string" - } - ] - } - ] - }, - "created_at": "2019-08-24T14:15:22Z", - "diff_status": { - "additions": 0, - "approved": true, - "author_avatar_url": "string", - "author_login": "string", - "base_branch": "string", - "changed_files": 0, - "changes_requested": true, - "chat_id": "efc9fe20-a1e5-4a8c-9c48-f1b30c1e4f86", - "commits": 0, - "deletions": 0, - "head_branch": "string", - "pr_number": 0, - "pull_request_draft": true, - "pull_request_state": "string", - "pull_request_title": "string", - "refreshed_at": "2019-08-24T14:15:22Z", - "reviewer_count": 0, - "stale_at": "2019-08-24T14:15:22Z", - "url": "string" - }, - "files": [ - { - "created_at": "2019-08-24T14:15:22Z", - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "mime_type": "string", - "name": "string", - "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", - "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", - "size_bytes": 0 - } - ], - "has_unread": true, - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "labels": { - "property1": "string", - "property2": "string" - }, - "last_error": { - "detail": "string", - "kind": "generic", - "message": "string", - "provider": "string", - "retryable": true, - "status_code": 0 - }, - "last_model_config_id": "30ebb95f-c255-4759-9429-89aa4ec1554c", - "last_reasoning_effort": "string", - "last_turn_summary": "string", - "mcp_server_ids": [ - "497f6eca-6276-4993-bfeb-53cbbbba6f08" - ], - "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", - "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", - "owner_name": "string", - "owner_username": "string", - "parent_chat_id": "c3609ee6-3b11-4a93-b9ae-e4fabcc99359", - "pin_order": 0, - "plan_mode": "plan", - "queued_for_capacity": true, - "root_chat_id": "2898031c-fdce-4e3e-8c53-4481dd42fcd7", - "shared": true, - "status": "waiting", - "summary": "string", - "title": "string", - "updated_at": "2019-08-24T14:15:22Z", - "warnings": [ - "string" - ], - "workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9" - } - ], - "client_type": "ui", - "context": { - "dirty": true, - "dirty_since": "2019-08-24T14:15:22Z", - "error": "string", - "resources": [ - { - "error": "string", - "kind": "instruction_file", - "size_bytes": 0, - "skill_description": "string", - "skill_name": "string", - "source": "string", - "status": "ok", - "tools": [ - { - "description": "string", - "name": "string" - } - ] - } - ] - }, - "created_at": "2019-08-24T14:15:22Z", - "diff_status": { - "additions": 0, - "approved": true, - "author_avatar_url": "string", - "author_login": "string", - "base_branch": "string", - "changed_files": 0, - "changes_requested": true, - "chat_id": "efc9fe20-a1e5-4a8c-9c48-f1b30c1e4f86", - "commits": 0, - "deletions": 0, - "head_branch": "string", - "pr_number": 0, - "pull_request_draft": true, - "pull_request_state": "string", - "pull_request_title": "string", - "refreshed_at": "2019-08-24T14:15:22Z", - "reviewer_count": 0, - "stale_at": "2019-08-24T14:15:22Z", - "url": "string" - }, - "files": [ - { - "created_at": "2019-08-24T14:15:22Z", - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "mime_type": "string", - "name": "string", - "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", - "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", - "size_bytes": 0 - } - ], - "has_unread": true, - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "labels": { - "property1": "string", - "property2": "string" - }, - "last_error": { - "detail": "string", - "kind": "generic", - "message": "string", - "provider": "string", - "retryable": true, - "status_code": 0 - }, - "last_model_config_id": "30ebb95f-c255-4759-9429-89aa4ec1554c", - "last_reasoning_effort": "string", - "last_turn_summary": "string", - "mcp_server_ids": [ - "497f6eca-6276-4993-bfeb-53cbbbba6f08" - ], - "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", - "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", - "owner_name": "string", - "owner_username": "string", - "parent_chat_id": "c3609ee6-3b11-4a93-b9ae-e4fabcc99359", - "pin_order": 0, - "plan_mode": "plan", - "queued_for_capacity": true, - "root_chat_id": "2898031c-fdce-4e3e-8c53-4481dd42fcd7", - "shared": true, - "status": "waiting", - "summary": "string", - "title": "string", - "updated_at": "2019-08-24T14:15:22Z", - "warnings": [ - "string" - ], - "workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9" -} -``` - -### Responses - -| Status | Meaning | Description | Schema | -|--------|---------------------------------------------------------|-------------|------------------------------------------| -| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.Chat](schemas.md#codersdkchat) | - -To perform this operation, you must be authenticated. [Learn more](authentication.md). - -## List chat messages - -### Code samples - -```sh -# Example request using curl -curl -X GET http://coder-server:8080/api/v2/chats/{chat}/messages \ - -H 'Accept: application/json' \ - -H 'Coder-Session-Token: API_KEY' -``` - -`GET /api/v2/chats/{chat}/messages` - -### Parameters - -| Name | In | Type | Required | Description | -|-------------|-------|--------------|----------|--------------------------------------| -| `chat` | path | string(uuid) | true | Chat ID | -| `before_id` | query | integer | false | Return messages with id < before_id | -| `after_id` | query | integer | false | Return messages with id > after_id | -| `limit` | query | integer | false | Page size, 1 to 200. Defaults to 50. | +| Name | In | Type | Required | Description | +|-------------|-------|--------------|----------|--------------------------------------| +| `chat` | path | string(uuid) | true | Chat ID | +| `before_id` | query | integer | false | Return messages with id < before_id | +| `after_id` | query | integer | false | Return messages with id > after_id | +| `limit` | query | integer | false | Page size, 1 to 200. Defaults to 50. | ### Example responses @@ -4622,2138 +3982,61 @@ curl -X GET http://coder-server:8080/api/v2/chats/{chat}/stream/git \ To perform this operation, you must be authenticated. [Learn more](authentication.md). -## Stream chat parts via WebSockets +## Propose chat title ### Code samples ```sh # Example request using curl -curl -X GET http://coder-server:8080/api/v2/chats/{chat}/stream/parts \ +curl -X POST http://coder-server:8080/api/v2/chats/{chat}/title/propose \ -H 'Accept: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`GET /api/v2/chats/{chat}/stream/parts` +`POST /api/v2/chats/{chat}/title/propose` ### Parameters -| Name | In | Type | Required | Description | -|--------|------|--------------|----------|-------------| -| `chat` | path | string(uuid) | true | Chat ID | +| Name | In | Type | Required | Description | +|--------|------|--------|----------|-------------| +| `chat` | path | string | true | Chat ID | ### 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" - }, - "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 | array of [codersdk.ChatStreamEvent](schemas.md#codersdkchatstreamevent) | - -

Response Schema

- -Status Code **200** - -| 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 | | | - -#### Enumerated Values - -| 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). - -## Propose chat title - -### Code samples - -```sh -# Example request using curl -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/v2/chats/{chat}/title/propose` - -### Parameters - -| Name | In | Type | Required | Description | -|--------|------|--------|----------|-------------| -| `chat` | path | string | true | Chat ID | - -### Example responses - -> 200 Response - -```json -{ - "title": "string" -} -``` - -### Responses - -| Status | Meaning | Description | Schema | -|--------|---------------------------------------------------------|-------------|----------------------------------------------------------------------------------| -| 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). - -## Regenerate chat title - -### Code samples - -```sh -# Example request using curl -curl -X POST http://coder-server:8080/api/v2/chats/{chat}/title/regenerate \ - -H 'Accept: application/json' \ - -H 'Coder-Session-Token: API_KEY' -``` - -`POST /api/v2/chats/{chat}/title/regenerate` - -### Parameters - -| Name | In | Type | Required | Description | -|--------|------|--------------|----------|-------------| -| `chat` | path | string(uuid) | true | Chat ID | - -### Example responses - -> 200 Response - -```json -{ - "agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e98978", - "archived": true, - "build_id": "bfb1f3fa-bf7b-43a5-9e0b-26cc050e44cb", - "children": [ - { - "agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e98978", - "archived": true, - "build_id": "bfb1f3fa-bf7b-43a5-9e0b-26cc050e44cb", - "children": [], - "client_type": "ui", - "context": { - "dirty": true, - "dirty_since": "2019-08-24T14:15:22Z", - "error": "string", - "resources": [ - { - "error": "string", - "kind": "instruction_file", - "size_bytes": 0, - "skill_description": "string", - "skill_name": "string", - "source": "string", - "status": "ok", - "tools": [ - { - "description": "string", - "name": "string" - } - ] - } - ] - }, - "created_at": "2019-08-24T14:15:22Z", - "diff_status": { - "additions": 0, - "approved": true, - "author_avatar_url": "string", - "author_login": "string", - "base_branch": "string", - "changed_files": 0, - "changes_requested": true, - "chat_id": "efc9fe20-a1e5-4a8c-9c48-f1b30c1e4f86", - "commits": 0, - "deletions": 0, - "head_branch": "string", - "pr_number": 0, - "pull_request_draft": true, - "pull_request_state": "string", - "pull_request_title": "string", - "refreshed_at": "2019-08-24T14:15:22Z", - "reviewer_count": 0, - "stale_at": "2019-08-24T14:15:22Z", - "url": "string" - }, - "files": [ - { - "created_at": "2019-08-24T14:15:22Z", - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "mime_type": "string", - "name": "string", - "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", - "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", - "size_bytes": 0 - } - ], - "has_unread": true, - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "labels": { - "property1": "string", - "property2": "string" - }, - "last_error": { - "detail": "string", - "kind": "generic", - "message": "string", - "provider": "string", - "retryable": true, - "status_code": 0 - }, - "last_model_config_id": "30ebb95f-c255-4759-9429-89aa4ec1554c", - "last_reasoning_effort": "string", - "last_turn_summary": "string", - "mcp_server_ids": [ - "497f6eca-6276-4993-bfeb-53cbbbba6f08" - ], - "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", - "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", - "owner_name": "string", - "owner_username": "string", - "parent_chat_id": "c3609ee6-3b11-4a93-b9ae-e4fabcc99359", - "pin_order": 0, - "plan_mode": "plan", - "queued_for_capacity": true, - "root_chat_id": "2898031c-fdce-4e3e-8c53-4481dd42fcd7", - "shared": true, - "status": "waiting", - "summary": "string", - "title": "string", - "updated_at": "2019-08-24T14:15:22Z", - "warnings": [ - "string" - ], - "workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9" - } - ], - "client_type": "ui", - "context": { - "dirty": true, - "dirty_since": "2019-08-24T14:15:22Z", - "error": "string", - "resources": [ - { - "error": "string", - "kind": "instruction_file", - "size_bytes": 0, - "skill_description": "string", - "skill_name": "string", - "source": "string", - "status": "ok", - "tools": [ - { - "description": "string", - "name": "string" - } - ] - } - ] - }, - "created_at": "2019-08-24T14:15:22Z", - "diff_status": { - "additions": 0, - "approved": true, - "author_avatar_url": "string", - "author_login": "string", - "base_branch": "string", - "changed_files": 0, - "changes_requested": true, - "chat_id": "efc9fe20-a1e5-4a8c-9c48-f1b30c1e4f86", - "commits": 0, - "deletions": 0, - "head_branch": "string", - "pr_number": 0, - "pull_request_draft": true, - "pull_request_state": "string", - "pull_request_title": "string", - "refreshed_at": "2019-08-24T14:15:22Z", - "reviewer_count": 0, - "stale_at": "2019-08-24T14:15:22Z", - "url": "string" - }, - "files": [ - { - "created_at": "2019-08-24T14:15:22Z", - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "mime_type": "string", - "name": "string", - "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", - "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", - "size_bytes": 0 - } - ], - "has_unread": true, - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "labels": { - "property1": "string", - "property2": "string" - }, - "last_error": { - "detail": "string", - "kind": "generic", - "message": "string", - "provider": "string", - "retryable": true, - "status_code": 0 - }, - "last_model_config_id": "30ebb95f-c255-4759-9429-89aa4ec1554c", - "last_reasoning_effort": "string", - "last_turn_summary": "string", - "mcp_server_ids": [ - "497f6eca-6276-4993-bfeb-53cbbbba6f08" - ], - "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", - "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", - "owner_name": "string", - "owner_username": "string", - "parent_chat_id": "c3609ee6-3b11-4a93-b9ae-e4fabcc99359", - "pin_order": 0, - "plan_mode": "plan", - "queued_for_capacity": true, - "root_chat_id": "2898031c-fdce-4e3e-8c53-4481dd42fcd7", - "shared": true, - "status": "waiting", - "summary": "string", - "title": "string", - "updated_at": "2019-08-24T14:15:22Z", - "warnings": [ - "string" - ], - "workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9" -} -``` - -### Responses - -| Status | Meaning | Description | Schema | -|--------|---------------------------------------------------------|-------------|------------------------------------------| -| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.Chat](schemas.md#codersdkchat) | - -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 | 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 organization chat model overrides - -### Code samples - -```sh -# Example request using curl -curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/chats/model-overrides \ - -H 'Accept: application/json' \ - -H 'Coder-Session-Token: API_KEY' -``` - -`GET /api/v2/organizations/{organization}/chats/model-overrides` - -### Parameters - -| Name | In | Type | Required | Description | -|----------------|------|--------|----------|-------------------------| -| `organization` | path | string | true | Organization name or ID | - -### Example responses - -> 200 Response - -```json -{ - "overrides": [ - { - "context": "general", - "model_config_id": "string", - "reasoning_effort": "string" - } - ] -} -``` - -### Responses - -| Status | Meaning | Description | Schema | -|--------|---------------------------------------------------------|-------------|--------------------------------------------------------------------------------------| -| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.ChatModelOverridesResponse](schemas.md#codersdkchatmodeloverridesresponse) | - -To perform this operation, you must be authenticated. [Learn more](authentication.md). - -## Update organization chat model override - -### Code samples - -```sh -# Example request using curl -curl -X PUT http://coder-server:8080/api/v2/organizations/{organization}/chats/model-overrides/{context} \ - -H 'Content-Type: application/json' \ - -H 'Accept: application/json' \ - -H 'Coder-Session-Token: API_KEY' -``` - -`PUT /api/v2/organizations/{organization}/chats/model-overrides/{context}` - -> Body parameter - -```json -{ - "model_config_id": "string", - "reasoning_effort": "string" -} -``` - -### Parameters - -| Name | In | Type | Required | Description | -|----------------|------|----------------------------------------------------------------------------------------------|----------|-------------------------| -| `organization` | path | string | true | Organization name or ID | -| `context` | path | string | true | Override context | -| `body` | body | [codersdk.UpdateChatModelOverrideRequest](schemas.md#codersdkupdatechatmodeloverriderequest) | true | Model override | - -#### Enumerated Values - -| Parameter | Value(s) | -|-----------|-------------------------------------------------------------------| -| `context` | `advisor`, `compaction`, `explore`, `general`, `title_generation` | - -### Example responses - -> 200 Response - -```json -{ - "context": "general", - "model_config_id": "string", - "reasoning_effort": "string" -} -``` - -### Responses - -| Status | Meaning | Description | Schema | -|--------|---------------------------------------------------------|-------------|------------------------------------------------------------------------------------| -| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.ChatModelOverrideResponse](schemas.md#codersdkchatmodeloverrideresponse) | - -To perform this operation, you must be authenticated. [Learn more](authentication.md). - -## List AI models and provider descriptors in an organization - -### Code samples - -```sh -# Example request using curl -curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/chats/models \ - -H 'Accept: application/json' \ - -H 'Coder-Session-Token: API_KEY' -``` - -`GET /api/v2/organizations/{organization}/chats/models` - -### Parameters - -| Name | In | Type | Required | Description | -|----------------|------|--------|----------|-------------------------| -| `organization` | path | string | true | Organization name or ID | - -### Example responses - -> 200 Response - -```json -{ - "models": [ - { - "ai_provider_id": "5a3b8ff9-20e7-4c37-ba1a-5b433e355819", - "compression_threshold": 0, - "context_limit": 0, - "created_at": "2019-08-24T14:15:22Z", - "display_name": "string", - "enabled": true, - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "is_default": true, - "model": "string", - "model_config": { - "frequency_penalty": 0, - "max_output_tokens": 0, - "openai_config": { - "use_responses_api": true - }, - "presence_penalty": 0, - "provider_options": { - "anthropic": { - "allowed_domains": [ - "string" - ], - "blocked_domains": [ - "string" - ], - "context_1m_enabled": true, - "disable_parallel_tool_use": true, - "send_reasoning": true, - "thinking": { - "budget_tokens": 0 - }, - "thinking_display": "string", - "web_search_enabled": true - }, - "google": { - "cached_content": "string", - "safety_settings": [ - { - "category": "string", - "threshold": "string" - } - ], - "thinking_config": { - "include_thoughts": true, - "thinking_budget": 0, - "thinking_level": "string" - }, - "threshold": "string", - "web_search_enabled": true - }, - "openai": { - "allowed_domains": [ - "string" - ], - "include": [ - "string" - ], - "instructions": "string", - "log_probs": true, - "logit_bias": { - "property1": 0, - "property2": 0 - }, - "max_completion_tokens": 0, - "max_tool_calls": 0, - "metadata": { - "property1": null, - "property2": null - }, - "parallel_tool_calls": true, - "prediction": { - "property1": null, - "property2": null - }, - "prompt_cache_key": "string", - "reasoning_summary": "string", - "safety_identifier": "string", - "search_context_size": "string", - "service_tier": "string", - "store": true, - "strict_json_schema": true, - "structured_outputs": true, - "text_verbosity": "string", - "top_log_probs": 0, - "user": "string", - "web_search_enabled": true - }, - "openaicompat": { - "user": "string" - }, - "openrouter": { - "extra_body": { - "property1": null, - "property2": null - }, - "include_usage": true, - "log_probs": true, - "logit_bias": { - "property1": 0, - "property2": 0 - }, - "parallel_tool_calls": true, - "provider": { - "allow_fallbacks": true, - "data_collection": "string", - "ignore": [ - "string" - ], - "only": [ - "string" - ], - "order": [ - "string" - ], - "quantizations": [ - "string" - ], - "require_parameters": true, - "sort": "string" - }, - "reasoning": { - "enabled": true, - "exclude": true, - "max_tokens": 0 - }, - "user": "string" - }, - "vercel": { - "extra_body": { - "property1": null, - "property2": null - }, - "logit_bias": { - "property1": 0, - "property2": 0 - }, - "logprobs": true, - "parallel_tool_calls": true, - "providerOptions": { - "models": [ - "string" - ], - "order": [ - "string" - ] - }, - "reasoning": { - "enabled": true, - "exclude": true, - "max_tokens": 0 - }, - "top_logprobs": 0, - "user": "string" - } - }, - "reasoning_effort": { - "default": "string", - "max": "string" - }, - "temperature": 0, - "top_k": 0, - "top_p": 0 - }, - "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", - "reasoning_efforts": [ - "string" - ], - "updated_at": "2019-08-24T14:15:22Z" - } - ], - "providers": [ - { - "allow_user_api_key": true, - "available": true, - "display_name": "string", - "enabled": true, - "has_api_key": true, - "has_effective_api_key": true, - "has_user_api_key": true, - "icon": "string", - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "type": "string", - "unavailable_reason": "missing_api_key" - } - ], - "unsupported_providers": [ - { - "display_name": "string", - "provider": "string" - } - ] -} -``` - -### Responses - -| Status | Meaning | Description | Schema | -|--------|---------------------------------------------------------|-------------|----------------------------------------------------------------------------------------------| -| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.OrganizationChatModelsResponse](schemas.md#codersdkorganizationchatmodelsresponse) | - -To perform this operation, you must be authenticated. [Learn more](authentication.md). - -## Create an AI model in an organization - -### Code samples - -```sh -# Example request using curl -curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/chats/models \ - -H 'Content-Type: application/json' \ - -H 'Accept: application/json' \ - -H 'Coder-Session-Token: API_KEY' -``` - -`POST /api/v2/organizations/{organization}/chats/models` - -> Body parameter - -```json -{ - "ai_provider_id": "5a3b8ff9-20e7-4c37-ba1a-5b433e355819", - "compression_threshold": 0, - "context_limit": 0, - "display_name": "string", - "enabled": true, - "is_default": true, - "model": "string", - "model_config": { - "frequency_penalty": 0, - "max_output_tokens": 0, - "openai_config": { - "use_responses_api": true - }, - "presence_penalty": 0, - "provider_options": { - "anthropic": { - "allowed_domains": [ - "string" - ], - "blocked_domains": [ - "string" - ], - "context_1m_enabled": true, - "disable_parallel_tool_use": true, - "send_reasoning": true, - "thinking": { - "budget_tokens": 0 - }, - "thinking_display": "string", - "web_search_enabled": true - }, - "google": { - "cached_content": "string", - "safety_settings": [ - { - "category": "string", - "threshold": "string" - } - ], - "thinking_config": { - "include_thoughts": true, - "thinking_budget": 0, - "thinking_level": "string" - }, - "threshold": "string", - "web_search_enabled": true - }, - "openai": { - "allowed_domains": [ - "string" - ], - "include": [ - "string" - ], - "instructions": "string", - "log_probs": true, - "logit_bias": { - "property1": 0, - "property2": 0 - }, - "max_completion_tokens": 0, - "max_tool_calls": 0, - "metadata": { - "property1": null, - "property2": null - }, - "parallel_tool_calls": true, - "prediction": { - "property1": null, - "property2": null - }, - "prompt_cache_key": "string", - "reasoning_summary": "string", - "safety_identifier": "string", - "search_context_size": "string", - "service_tier": "string", - "store": true, - "strict_json_schema": true, - "structured_outputs": true, - "text_verbosity": "string", - "top_log_probs": 0, - "user": "string", - "web_search_enabled": true - }, - "openaicompat": { - "user": "string" - }, - "openrouter": { - "extra_body": { - "property1": null, - "property2": null - }, - "include_usage": true, - "log_probs": true, - "logit_bias": { - "property1": 0, - "property2": 0 - }, - "parallel_tool_calls": true, - "provider": { - "allow_fallbacks": true, - "data_collection": "string", - "ignore": [ - "string" - ], - "only": [ - "string" - ], - "order": [ - "string" - ], - "quantizations": [ - "string" - ], - "require_parameters": true, - "sort": "string" - }, - "reasoning": { - "enabled": true, - "exclude": true, - "max_tokens": 0 - }, - "user": "string" - }, - "vercel": { - "extra_body": { - "property1": null, - "property2": null - }, - "logit_bias": { - "property1": 0, - "property2": 0 - }, - "logprobs": true, - "parallel_tool_calls": true, - "providerOptions": { - "models": [ - "string" - ], - "order": [ - "string" - ] - }, - "reasoning": { - "enabled": true, - "exclude": true, - "max_tokens": 0 - }, - "top_logprobs": 0, - "user": "string" - } - }, - "reasoning_effort": { - "default": "string", - "max": "string" - }, - "temperature": 0, - "top_k": 0, - "top_p": 0 - } -} -``` - -### Parameters - -| Name | In | Type | Required | Description | -|----------------|------|------------------------------------------------------------------------------|----------|-------------------------| -| `organization` | path | string | true | Organization name or ID | -| `body` | body | [codersdk.CreateChatModelRequest](schemas.md#codersdkcreatechatmodelrequest) | true | Model | - -### Example responses - -> 201 Response - -```json -{ - "ai_provider_id": "5a3b8ff9-20e7-4c37-ba1a-5b433e355819", - "compression_threshold": 0, - "context_limit": 0, - "created_at": "2019-08-24T14:15:22Z", - "display_name": "string", - "enabled": true, - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "is_default": true, - "model": "string", - "model_config": { - "frequency_penalty": 0, - "max_output_tokens": 0, - "openai_config": { - "use_responses_api": true - }, - "presence_penalty": 0, - "provider_options": { - "anthropic": { - "allowed_domains": [ - "string" - ], - "blocked_domains": [ - "string" - ], - "context_1m_enabled": true, - "disable_parallel_tool_use": true, - "send_reasoning": true, - "thinking": { - "budget_tokens": 0 - }, - "thinking_display": "string", - "web_search_enabled": true - }, - "google": { - "cached_content": "string", - "safety_settings": [ - { - "category": "string", - "threshold": "string" - } - ], - "thinking_config": { - "include_thoughts": true, - "thinking_budget": 0, - "thinking_level": "string" - }, - "threshold": "string", - "web_search_enabled": true - }, - "openai": { - "allowed_domains": [ - "string" - ], - "include": [ - "string" - ], - "instructions": "string", - "log_probs": true, - "logit_bias": { - "property1": 0, - "property2": 0 - }, - "max_completion_tokens": 0, - "max_tool_calls": 0, - "metadata": { - "property1": null, - "property2": null - }, - "parallel_tool_calls": true, - "prediction": { - "property1": null, - "property2": null - }, - "prompt_cache_key": "string", - "reasoning_summary": "string", - "safety_identifier": "string", - "search_context_size": "string", - "service_tier": "string", - "store": true, - "strict_json_schema": true, - "structured_outputs": true, - "text_verbosity": "string", - "top_log_probs": 0, - "user": "string", - "web_search_enabled": true - }, - "openaicompat": { - "user": "string" - }, - "openrouter": { - "extra_body": { - "property1": null, - "property2": null - }, - "include_usage": true, - "log_probs": true, - "logit_bias": { - "property1": 0, - "property2": 0 - }, - "parallel_tool_calls": true, - "provider": { - "allow_fallbacks": true, - "data_collection": "string", - "ignore": [ - "string" - ], - "only": [ - "string" - ], - "order": [ - "string" - ], - "quantizations": [ - "string" - ], - "require_parameters": true, - "sort": "string" - }, - "reasoning": { - "enabled": true, - "exclude": true, - "max_tokens": 0 - }, - "user": "string" - }, - "vercel": { - "extra_body": { - "property1": null, - "property2": null - }, - "logit_bias": { - "property1": 0, - "property2": 0 - }, - "logprobs": true, - "parallel_tool_calls": true, - "providerOptions": { - "models": [ - "string" - ], - "order": [ - "string" - ] - }, - "reasoning": { - "enabled": true, - "exclude": true, - "max_tokens": 0 - }, - "top_logprobs": 0, - "user": "string" - } - }, - "reasoning_effort": { - "default": "string", - "max": "string" - }, - "temperature": 0, - "top_k": 0, - "top_p": 0 - }, - "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", - "reasoning_efforts": [ - "string" - ], - "updated_at": "2019-08-24T14:15:22Z" -} -``` - -### Responses - -| Status | Meaning | Description | Schema | -|--------|--------------------------------------------------------------|-------------|----------------------------------------------------| -| 201 | [Created](https://tools.ietf.org/html/rfc7231#section-6.3.2) | Created | [codersdk.ChatModel](schemas.md#codersdkchatmodel) | - -To perform this operation, you must be authenticated. [Learn more](authentication.md). - -## Get an AI model - -### Code samples - -```sh -# Example request using curl -curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/chats/models/{model} \ - -H 'Accept: application/json' \ - -H 'Coder-Session-Token: API_KEY' -``` - -`GET /api/v2/organizations/{organization}/chats/models/{model}` - -### Parameters - -| Name | In | Type | Required | Description | -|----------------|------|--------|----------|-------------------------| -| `organization` | path | string | true | Organization name or ID | -| `model` | path | string | true | Model ID | - -### Example responses - -> 200 Response - -```json -{ - "ai_provider_id": "5a3b8ff9-20e7-4c37-ba1a-5b433e355819", - "compression_threshold": 0, - "context_limit": 0, - "created_at": "2019-08-24T14:15:22Z", - "display_name": "string", - "enabled": true, - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "is_default": true, - "model": "string", - "model_config": { - "frequency_penalty": 0, - "max_output_tokens": 0, - "openai_config": { - "use_responses_api": true - }, - "presence_penalty": 0, - "provider_options": { - "anthropic": { - "allowed_domains": [ - "string" - ], - "blocked_domains": [ - "string" - ], - "context_1m_enabled": true, - "disable_parallel_tool_use": true, - "send_reasoning": true, - "thinking": { - "budget_tokens": 0 - }, - "thinking_display": "string", - "web_search_enabled": true - }, - "google": { - "cached_content": "string", - "safety_settings": [ - { - "category": "string", - "threshold": "string" - } - ], - "thinking_config": { - "include_thoughts": true, - "thinking_budget": 0, - "thinking_level": "string" - }, - "threshold": "string", - "web_search_enabled": true - }, - "openai": { - "allowed_domains": [ - "string" - ], - "include": [ - "string" - ], - "instructions": "string", - "log_probs": true, - "logit_bias": { - "property1": 0, - "property2": 0 - }, - "max_completion_tokens": 0, - "max_tool_calls": 0, - "metadata": { - "property1": null, - "property2": null - }, - "parallel_tool_calls": true, - "prediction": { - "property1": null, - "property2": null - }, - "prompt_cache_key": "string", - "reasoning_summary": "string", - "safety_identifier": "string", - "search_context_size": "string", - "service_tier": "string", - "store": true, - "strict_json_schema": true, - "structured_outputs": true, - "text_verbosity": "string", - "top_log_probs": 0, - "user": "string", - "web_search_enabled": true - }, - "openaicompat": { - "user": "string" - }, - "openrouter": { - "extra_body": { - "property1": null, - "property2": null - }, - "include_usage": true, - "log_probs": true, - "logit_bias": { - "property1": 0, - "property2": 0 - }, - "parallel_tool_calls": true, - "provider": { - "allow_fallbacks": true, - "data_collection": "string", - "ignore": [ - "string" - ], - "only": [ - "string" - ], - "order": [ - "string" - ], - "quantizations": [ - "string" - ], - "require_parameters": true, - "sort": "string" - }, - "reasoning": { - "enabled": true, - "exclude": true, - "max_tokens": 0 - }, - "user": "string" - }, - "vercel": { - "extra_body": { - "property1": null, - "property2": null - }, - "logit_bias": { - "property1": 0, - "property2": 0 - }, - "logprobs": true, - "parallel_tool_calls": true, - "providerOptions": { - "models": [ - "string" - ], - "order": [ - "string" - ] - }, - "reasoning": { - "enabled": true, - "exclude": true, - "max_tokens": 0 - }, - "top_logprobs": 0, - "user": "string" - } - }, - "reasoning_effort": { - "default": "string", - "max": "string" - }, - "temperature": 0, - "top_k": 0, - "top_p": 0 - }, - "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", - "reasoning_efforts": [ - "string" - ], - "updated_at": "2019-08-24T14:15:22Z" -} -``` - -### Responses - -| Status | Meaning | Description | Schema | -|--------|---------------------------------------------------------|-------------|----------------------------------------------------| -| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.ChatModel](schemas.md#codersdkchatmodel) | - -To perform this operation, you must be authenticated. [Learn more](authentication.md). - -## Delete an AI model - -### Code samples - -```sh -# Example request using curl -curl -X DELETE http://coder-server:8080/api/v2/organizations/{organization}/chats/models/{model} \ - -H 'Coder-Session-Token: API_KEY' -``` - -`DELETE /api/v2/organizations/{organization}/chats/models/{model}` - -### Parameters - -| Name | In | Type | Required | Description | -|----------------|------|--------|----------|-------------------------| -| `organization` | path | string | true | Organization name or ID | -| `model` | path | string | true | Model 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). - -## Update an AI model - -### Code samples - -```sh -# Example request using curl -curl -X PATCH http://coder-server:8080/api/v2/organizations/{organization}/chats/models/{model} \ - -H 'Content-Type: application/json' \ - -H 'Accept: application/json' \ - -H 'Coder-Session-Token: API_KEY' -``` - -`PATCH /api/v2/organizations/{organization}/chats/models/{model}` - -> Body parameter - -```json -{ - "ai_provider_id": "5a3b8ff9-20e7-4c37-ba1a-5b433e355819", - "compression_threshold": 0, - "context_limit": 0, - "display_name": "string", - "enabled": true, - "is_default": true, - "model": "string", - "model_config": { - "frequency_penalty": 0, - "max_output_tokens": 0, - "openai_config": { - "use_responses_api": true - }, - "presence_penalty": 0, - "provider_options": { - "anthropic": { - "allowed_domains": [ - "string" - ], - "blocked_domains": [ - "string" - ], - "context_1m_enabled": true, - "disable_parallel_tool_use": true, - "send_reasoning": true, - "thinking": { - "budget_tokens": 0 - }, - "thinking_display": "string", - "web_search_enabled": true - }, - "google": { - "cached_content": "string", - "safety_settings": [ - { - "category": "string", - "threshold": "string" - } - ], - "thinking_config": { - "include_thoughts": true, - "thinking_budget": 0, - "thinking_level": "string" - }, - "threshold": "string", - "web_search_enabled": true - }, - "openai": { - "allowed_domains": [ - "string" - ], - "include": [ - "string" - ], - "instructions": "string", - "log_probs": true, - "logit_bias": { - "property1": 0, - "property2": 0 - }, - "max_completion_tokens": 0, - "max_tool_calls": 0, - "metadata": { - "property1": null, - "property2": null - }, - "parallel_tool_calls": true, - "prediction": { - "property1": null, - "property2": null - }, - "prompt_cache_key": "string", - "reasoning_summary": "string", - "safety_identifier": "string", - "search_context_size": "string", - "service_tier": "string", - "store": true, - "strict_json_schema": true, - "structured_outputs": true, - "text_verbosity": "string", - "top_log_probs": 0, - "user": "string", - "web_search_enabled": true - }, - "openaicompat": { - "user": "string" - }, - "openrouter": { - "extra_body": { - "property1": null, - "property2": null - }, - "include_usage": true, - "log_probs": true, - "logit_bias": { - "property1": 0, - "property2": 0 - }, - "parallel_tool_calls": true, - "provider": { - "allow_fallbacks": true, - "data_collection": "string", - "ignore": [ - "string" - ], - "only": [ - "string" - ], - "order": [ - "string" - ], - "quantizations": [ - "string" - ], - "require_parameters": true, - "sort": "string" - }, - "reasoning": { - "enabled": true, - "exclude": true, - "max_tokens": 0 - }, - "user": "string" - }, - "vercel": { - "extra_body": { - "property1": null, - "property2": null - }, - "logit_bias": { - "property1": 0, - "property2": 0 - }, - "logprobs": true, - "parallel_tool_calls": true, - "providerOptions": { - "models": [ - "string" - ], - "order": [ - "string" - ] - }, - "reasoning": { - "enabled": true, - "exclude": true, - "max_tokens": 0 - }, - "top_logprobs": 0, - "user": "string" - } - }, - "reasoning_effort": { - "default": "string", - "max": "string" - }, - "temperature": 0, - "top_k": 0, - "top_p": 0 - } -} -``` - -### Parameters - -| Name | In | Type | Required | Description | -|----------------|------|------------------------------------------------------------------------------|----------|-------------------------| -| `organization` | path | string | true | Organization name or ID | -| `model` | path | string | true | Model ID | -| `body` | body | [codersdk.UpdateChatModelRequest](schemas.md#codersdkupdatechatmodelrequest) | true | Model updates | - -### Example responses - -> 200 Response - -```json -{ - "ai_provider_id": "5a3b8ff9-20e7-4c37-ba1a-5b433e355819", - "compression_threshold": 0, - "context_limit": 0, - "created_at": "2019-08-24T14:15:22Z", - "display_name": "string", - "enabled": true, - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "is_default": true, - "model": "string", - "model_config": { - "frequency_penalty": 0, - "max_output_tokens": 0, - "openai_config": { - "use_responses_api": true - }, - "presence_penalty": 0, - "provider_options": { - "anthropic": { - "allowed_domains": [ - "string" - ], - "blocked_domains": [ - "string" - ], - "context_1m_enabled": true, - "disable_parallel_tool_use": true, - "send_reasoning": true, - "thinking": { - "budget_tokens": 0 - }, - "thinking_display": "string", - "web_search_enabled": true - }, - "google": { - "cached_content": "string", - "safety_settings": [ - { - "category": "string", - "threshold": "string" - } - ], - "thinking_config": { - "include_thoughts": true, - "thinking_budget": 0, - "thinking_level": "string" - }, - "threshold": "string", - "web_search_enabled": true - }, - "openai": { - "allowed_domains": [ - "string" - ], - "include": [ - "string" - ], - "instructions": "string", - "log_probs": true, - "logit_bias": { - "property1": 0, - "property2": 0 - }, - "max_completion_tokens": 0, - "max_tool_calls": 0, - "metadata": { - "property1": null, - "property2": null - }, - "parallel_tool_calls": true, - "prediction": { - "property1": null, - "property2": null - }, - "prompt_cache_key": "string", - "reasoning_summary": "string", - "safety_identifier": "string", - "search_context_size": "string", - "service_tier": "string", - "store": true, - "strict_json_schema": true, - "structured_outputs": true, - "text_verbosity": "string", - "top_log_probs": 0, - "user": "string", - "web_search_enabled": true - }, - "openaicompat": { - "user": "string" - }, - "openrouter": { - "extra_body": { - "property1": null, - "property2": null - }, - "include_usage": true, - "log_probs": true, - "logit_bias": { - "property1": 0, - "property2": 0 - }, - "parallel_tool_calls": true, - "provider": { - "allow_fallbacks": true, - "data_collection": "string", - "ignore": [ - "string" - ], - "only": [ - "string" - ], - "order": [ - "string" - ], - "quantizations": [ - "string" - ], - "require_parameters": true, - "sort": "string" - }, - "reasoning": { - "enabled": true, - "exclude": true, - "max_tokens": 0 - }, - "user": "string" - }, - "vercel": { - "extra_body": { - "property1": null, - "property2": null - }, - "logit_bias": { - "property1": 0, - "property2": 0 - }, - "logprobs": true, - "parallel_tool_calls": true, - "providerOptions": { - "models": [ - "string" - ], - "order": [ - "string" - ] - }, - "reasoning": { - "enabled": true, - "exclude": true, - "max_tokens": 0 - }, - "top_logprobs": 0, - "user": "string" - } - }, - "reasoning_effort": { - "default": "string", - "max": "string" - }, - "temperature": 0, - "top_k": 0, - "top_p": 0 - }, - "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", - "reasoning_efforts": [ - "string" - ], - "updated_at": "2019-08-24T14:15:22Z" -} -``` - -### Responses - -| Status | Meaning | Description | Schema | -|--------|---------------------------------------------------------|-------------|----------------------------------------------------| -| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.ChatModel](schemas.md#codersdkchatmodel) | - -To perform this operation, you must be authenticated. [Learn more](authentication.md). - -## Get an AI model ACL - -### Code samples - -```sh -# Example request using curl -curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/chats/models/{model}/acl \ - -H 'Accept: application/json' \ - -H 'Coder-Session-Token: API_KEY' -``` - -`GET /api/v2/organizations/{organization}/chats/models/{model}/acl` - -### Parameters - -| Name | In | Type | Required | Description | -|----------------|------|--------------|----------|-------------------------| -| `organization` | path | string | true | Organization name or ID | -| `model` | path | string(uuid) | true | Model ID | - -### Example responses - -> 200 Response - -```json -{ - "group_roles": { - "property1": "read", - "property2": "read" - }, - "user_roles": { - "property1": "read", - "property2": "read" - } -} -``` - -### Responses - -| Status | Meaning | Description | Schema | -|--------|---------------------------------------------------------|-------------|----------------------------------------------------------| -| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.ChatModelACL](schemas.md#codersdkchatmodelacl) | - -To perform this operation, you must be authenticated. [Learn more](authentication.md). - -## Update an AI model ACL - -### Code samples - -```sh -# Example request using curl -curl -X PATCH http://coder-server:8080/api/v2/organizations/{organization}/chats/models/{model}/acl \ - -H 'Content-Type: application/json' \ - -H 'Coder-Session-Token: API_KEY' -``` - -`PATCH /api/v2/organizations/{organization}/chats/models/{model}/acl` - -> Body parameter - ```json { - "group_roles": { - "property1": "read", - "property2": "read" - }, - "user_roles": { - "property1": "read", - "property2": "read" - } + "title": "string" } ``` -### Parameters - -| Name | In | Type | Required | Description | -|----------------|------|------------------------------------------------------------------------------------|----------|-------------------------| -| `organization` | path | string | true | Organization name or ID | -| `model` | path | string(uuid) | true | Model ID | -| `body` | body | [codersdk.UpdateChatModelACLRequest](schemas.md#codersdkupdatechatmodelaclrequest) | true | Sparse model ACL update | - ### Responses -| Status | Meaning | Description | Schema | -|--------|-----------------------------------------------------------------|-------------|--------| -| 204 | [No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5) | No Content | | +| Status | Meaning | Description | Schema | +|--------|---------------------------------------------------------|-------------|----------------------------------------------------------------------------------| +| 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). -## Get organization member chat model overrides +## Regenerate chat title ### Code samples ```sh # Example request using curl -curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/members/{user}/chats/model-overrides \ +curl -X POST http://coder-server:8080/api/v2/chats/{chat}/title/regenerate \ -H 'Accept: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`GET /api/v2/organizations/{organization}/members/{user}/chats/model-overrides` +`POST /api/v2/chats/{chat}/title/regenerate` ### Parameters -| Name | In | Type | Required | Description | -|----------------|------|--------|----------|-------------------------| -| `organization` | path | string | true | Organization name or ID | -| `user` | path | string | true | User name, ID, or me | +| Name | In | Type | Required | Description | +|--------|------|--------------|----------|-------------| +| `chat` | path | string(uuid) | true | Chat ID | ### Example responses @@ -6761,88 +4044,251 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/members ```json { - "deployment_defaults": { - "explore": { - "context": "general", - "model_config_id": "string", - "reasoning_effort": "string" - }, - "general": { - "context": "general", - "model_config_id": "string", - "reasoning_effort": "string" + "agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e98978", + "archived": true, + "build_id": "bfb1f3fa-bf7b-43a5-9e0b-26cc050e44cb", + "children": [ + { + "agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e98978", + "archived": true, + "build_id": "bfb1f3fa-bf7b-43a5-9e0b-26cc050e44cb", + "children": [], + "client_type": "ui", + "context": { + "dirty": true, + "dirty_since": "2019-08-24T14:15:22Z", + "error": "string", + "resources": [ + { + "error": "string", + "kind": "instruction_file", + "size_bytes": 0, + "skill_description": "string", + "skill_name": "string", + "source": "string", + "status": "ok", + "tools": [ + { + "description": "string", + "name": "string" + } + ] + } + ] + }, + "created_at": "2019-08-24T14:15:22Z", + "diff_status": { + "additions": 0, + "approved": true, + "author_avatar_url": "string", + "author_login": "string", + "base_branch": "string", + "changed_files": 0, + "changes_requested": true, + "chat_id": "efc9fe20-a1e5-4a8c-9c48-f1b30c1e4f86", + "commits": 0, + "deletions": 0, + "head_branch": "string", + "pr_number": 0, + "pull_request_draft": true, + "pull_request_state": "string", + "pull_request_title": "string", + "refreshed_at": "2019-08-24T14:15:22Z", + "reviewer_count": 0, + "stale_at": "2019-08-24T14:15:22Z", + "url": "string" + }, + "files": [ + { + "created_at": "2019-08-24T14:15:22Z", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "mime_type": "string", + "name": "string", + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", + "size_bytes": 0 + } + ], + "has_unread": true, + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "labels": { + "property1": "string", + "property2": "string" + }, + "last_error": { + "detail": "string", + "kind": "generic", + "message": "string", + "provider": "string", + "retryable": true, + "status_code": 0 + }, + "last_model_config_id": "30ebb95f-c255-4759-9429-89aa4ec1554c", + "last_reasoning_effort": "string", + "last_turn_summary": "string", + "mcp_server_ids": [ + "497f6eca-6276-4993-bfeb-53cbbbba6f08" + ], + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", + "owner_name": "string", + "owner_username": "string", + "parent_chat_id": "c3609ee6-3b11-4a93-b9ae-e4fabcc99359", + "pin_order": 0, + "plan_mode": "plan", + "queued_for_capacity": true, + "root_chat_id": "2898031c-fdce-4e3e-8c53-4481dd42fcd7", + "shared": true, + "status": "waiting", + "summary": "string", + "title": "string", + "updated_at": "2019-08-24T14:15:22Z", + "warnings": [ + "string" + ], + "workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9" } + ], + "client_type": "ui", + "context": { + "dirty": true, + "dirty_since": "2019-08-24T14:15:22Z", + "error": "string", + "resources": [ + { + "error": "string", + "kind": "instruction_file", + "size_bytes": 0, + "skill_description": "string", + "skill_name": "string", + "source": "string", + "status": "ok", + "tools": [ + { + "description": "string", + "name": "string" + } + ] + } + ] + }, + "created_at": "2019-08-24T14:15:22Z", + "diff_status": { + "additions": 0, + "approved": true, + "author_avatar_url": "string", + "author_login": "string", + "base_branch": "string", + "changed_files": 0, + "changes_requested": true, + "chat_id": "efc9fe20-a1e5-4a8c-9c48-f1b30c1e4f86", + "commits": 0, + "deletions": 0, + "head_branch": "string", + "pr_number": 0, + "pull_request_draft": true, + "pull_request_state": "string", + "pull_request_title": "string", + "refreshed_at": "2019-08-24T14:15:22Z", + "reviewer_count": 0, + "stale_at": "2019-08-24T14:15:22Z", + "url": "string" }, - "enabled": true, - "explore": { - "context": "root", - "is_set": true, - "mode": "deployment_default", - "model_config_id": "string", - "reasoning_effort": "string" + "files": [ + { + "created_at": "2019-08-24T14:15:22Z", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "mime_type": "string", + "name": "string", + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", + "size_bytes": 0 + } + ], + "has_unread": true, + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "labels": { + "property1": "string", + "property2": "string" }, - "general": { - "context": "root", - "is_set": true, - "mode": "deployment_default", - "model_config_id": "string", - "reasoning_effort": "string" + "last_error": { + "detail": "string", + "kind": "generic", + "message": "string", + "provider": "string", + "retryable": true, + "status_code": 0 }, - "root": { - "context": "root", - "is_set": true, - "mode": "deployment_default", - "model_config_id": "string", - "reasoning_effort": "string" - } + "last_model_config_id": "30ebb95f-c255-4759-9429-89aa4ec1554c", + "last_reasoning_effort": "string", + "last_turn_summary": "string", + "mcp_server_ids": [ + "497f6eca-6276-4993-bfeb-53cbbbba6f08" + ], + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", + "owner_name": "string", + "owner_username": "string", + "parent_chat_id": "c3609ee6-3b11-4a93-b9ae-e4fabcc99359", + "pin_order": 0, + "plan_mode": "plan", + "queued_for_capacity": true, + "root_chat_id": "2898031c-fdce-4e3e-8c53-4481dd42fcd7", + "shared": true, + "status": "waiting", + "summary": "string", + "title": "string", + "updated_at": "2019-08-24T14:15:22Z", + "warnings": [ + "string" + ], + "workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9" } ``` ### Responses -| Status | Meaning | Description | Schema | -|--------|---------------------------------------------------------|-------------|--------------------------------------------------------------------------------------------------------------| -| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.UserChatPersonalModelOverridesResponse](schemas.md#codersdkuserchatpersonalmodeloverridesresponse) | +| Status | Meaning | Description | Schema | +|--------|---------------------------------------------------------|-------------|------------------------------------------| +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.Chat](schemas.md#codersdkchat) | To perform this operation, you must be authenticated. [Learn more](authentication.md). -## Update organization member chat model override +## Submit chat tool results ### Code samples ```sh # Example request using curl -curl -X PUT http://coder-server:8080/api/v2/organizations/{organization}/members/{user}/chats/model-overrides/{context} \ +curl -X POST http://coder-server:8080/api/v2/chats/{chat}/tool-results \ -H 'Content-Type: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`PUT /api/v2/organizations/{organization}/members/{user}/chats/model-overrides/{context}` +`POST /api/v2/chats/{chat}/tool-results` > Body parameter ```json { - "mode": "deployment_default", - "model_config_id": "string", - "reasoning_effort": "string" + "results": [ + { + "is_error": true, + "output": [ + 0 + ], + "tool_call_id": "string" + } + ] } ``` ### Parameters -| Name | In | Type | Required | Description | -|----------------|------|----------------------------------------------------------------------------------------------------------------------|----------|-------------------------| -| `organization` | path | string | true | Organization name or ID | -| `user` | path | string | true | User name, ID, or me | -| `context` | path | string | true | Override context | -| `body` | body | [codersdk.UpdateUserChatPersonalModelOverrideRequest](schemas.md#codersdkupdateuserchatpersonalmodeloverriderequest) | true | Personal model override | - -#### Enumerated Values - -| Parameter | Value(s) | -|-----------|------------------------------| -| `context` | `explore`, `general`, `root` | +| Name | In | Type | Required | Description | +|--------|------|----------------------------------------------------------------------------------|----------|--------------| +| `chat` | path | string | true | Chat ID | +| `body` | body | [codersdk.SubmitToolResultsRequest](schemas.md#codersdksubmittoolresultsrequest) | true | Request body | ### Responses diff --git a/docs/reference/api/mcp.md b/docs/reference/api/mcp.md index 29295af0d40..9feb9375878 100644 --- a/docs/reference/api/mcp.md +++ b/docs/reference/api/mcp.md @@ -4,636 +4,3 @@ title: MCP --- - -## Handle MCP server OAuth2 callback - -### Code samples - -```sh -# Example request using curl -curl -X GET http://coder-server:8080/api/v2/mcp/servers/{mcpServer}/oauth2/callback \ - -H 'Coder-Session-Token: API_KEY' -``` - -`GET /api/v2/mcp/servers/{mcpServer}/oauth2/callback` - -### Parameters - -| Name | In | Type | Required | Description | -|---------------------|-------|--------------|----------|--------------------------------------------------------------------------------------| -| `mcpServer` | path | string(uuid) | true | MCP server config ID | -| `code` | query | string | false | Authorization code issued by the provider. Required together with state on success. | -| `state` | query | string | false | Opaque state issued by the connect endpoint. Required together with code on success. | -| `error` | query | string | false | Provider error code. Present instead of code when authorization fails. | -| `error_description` | query | string | false | Provider error description accompanying error. | - -### Responses - -| Status | Meaning | Description | Schema | -|--------|---------------------------------------------------------|-------------|--------| -| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | | - -To perform this operation, you must be authenticated. [Learn more](authentication.md). - -## Disconnect MCP server OAuth2 token - -### Code samples - -```sh -# Example request using curl -curl -X DELETE http://coder-server:8080/api/v2/mcp/servers/{mcpServer}/oauth2/disconnect \ - -H 'Accept: application/json' \ - -H 'Coder-Session-Token: API_KEY' -``` - -`DELETE /api/v2/mcp/servers/{mcpServer}/oauth2/disconnect` - -### Parameters - -| Name | In | Type | Required | Description | -|-------------|------|--------------|----------|----------------------| -| `mcpServer` | path | string(uuid) | true | MCP server config ID | - -### Example responses - -> 200 Response - -```json -{ - "token_revocation_error": "string", - "token_revoked": true -} -``` - -### Responses - -| Status | Meaning | Description | Schema | -|--------|---------------------------------------------------------|-------------|----------------------------------------------------------------------------------------------------| -| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.MCPServerOAuth2DisconnectResponse](schemas.md#codersdkmcpserveroauth2disconnectresponse) | - -To perform this operation, you must be authenticated. [Learn more](authentication.md). - -## List MCP server configs - -### Code samples - -```sh -# Example request using curl -curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/mcp-servers \ - -H 'Accept: application/json' \ - -H 'Coder-Session-Token: API_KEY' -``` - -`GET /api/v2/organizations/{organization}/mcp-servers` - -### Parameters - -| Name | In | Type | Required | Description | -|----------------|------|--------|----------|-------------------------| -| `organization` | path | string | true | Organization name or ID | - -### Example responses - -> 200 Response - -```json -[ - { - "allow_in_plan_mode": true, - "api_key_header": "string", - "auth_connected": true, - "auth_type": "string", - "availability": "string", - "created_at": "2019-08-24T14:15:22Z", - "description": "string", - "display_name": "string", - "enabled": true, - "forward_coder_headers": true, - "has_api_key": true, - "has_custom_headers": true, - "has_oauth2_secret": true, - "icon_url": "string", - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "model_intent": true, - "oauth2_auth_url": "string", - "oauth2_client_id": "string", - "oauth2_revocation_url": "string", - "oauth2_scopes": "string", - "oauth2_token_url": "string", - "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", - "slug": "string", - "tool_allow_list": [ - "string" - ], - "tool_deny_list": [ - "string" - ], - "transport": "string", - "updated_at": "2019-08-24T14:15:22Z", - "url": "string" - } -] -``` - -### Responses - -| Status | Meaning | Description | Schema | -|--------|---------------------------------------------------------|-------------|-------------------------------------------------------------------------| -| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | array of [codersdk.MCPServerConfig](schemas.md#codersdkmcpserverconfig) | - -

Response Schema

- -Status Code **200** - -| Name | Type | Required | Restrictions | Description | -|---------------------------|-------------------|----------|--------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `[array item]` | array | false | | | -| `» allow_in_plan_mode` | boolean | false | | | -| `» api_key_header` | string | false | | Api key header key fields (only populated for admins). | -| `» auth_connected` | boolean | false | | Per-user state (populated for non-admin requests). | -| `» auth_type` | string | false | | "none", "oauth2", "api_key", "custom_headers", "user_oidc" | -| `» availability` | string | false | | Availability policy set by admin. | -| `» created_at` | string(date-time) | false | | | -| `» description` | string | false | | | -| `» display_name` | string | false | | | -| `» enabled` | boolean | false | | | -| `» forward_coder_headers` | boolean | false | | Forward coder headers forwards the same Coder identity headers we send to LLM providers (X-Coder-Owner-Id, X-Coder-Chat-Id, and the optional X-Coder-Subchat-Id and X-Coder-Workspace-Id) to this MCP server on every request. Off by default to avoid leaking chat identity to third-party servers. | -| `» has_api_key` | boolean | false | | | -| `» has_custom_headers` | boolean | false | | | -| `» has_oauth2_secret` | boolean | false | | | -| `» icon_url` | string | false | | | -| `» id` | string(uuid) | false | | | -| `» model_intent` | boolean | false | | | -| `» oauth2_auth_url` | string | false | | | -| `» oauth2_client_id` | string | false | | Oauth2 client ID fields (only populated for admins). | -| `» oauth2_revocation_url` | string | false | | | -| `» oauth2_scopes` | string | false | | | -| `» oauth2_token_url` | string | false | | | -| `» organization_id` | string(uuid) | false | | | -| `» slug` | string | false | | | -| `» tool_allow_list` | array | false | | Tool governance. | -| `» tool_deny_list` | array | false | | | -| `» transport` | string | false | | "streamable_http" or "sse" | -| `» updated_at` | string(date-time) | false | | | -| `» url` | string | false | | | - -To perform this operation, you must be authenticated. [Learn more](authentication.md). - -## Create MCP server config - -### Code samples - -```sh -# Example request using curl -curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/mcp-servers \ - -H 'Content-Type: application/json' \ - -H 'Accept: application/json' \ - -H 'Coder-Session-Token: API_KEY' -``` - -`POST /api/v2/organizations/{organization}/mcp-servers` - -> Body parameter - -```json -{ - "allow_in_plan_mode": true, - "api_key_header": "string", - "api_key_value": "string", - "auth_type": "none", - "availability": "force_on", - "custom_headers": { - "property1": "string", - "property2": "string" - }, - "description": "string", - "display_name": "string", - "enabled": true, - "forward_coder_headers": true, - "icon_url": "string", - "model_intent": true, - "oauth2_auth_url": "string", - "oauth2_client_id": "string", - "oauth2_client_secret": "string", - "oauth2_revocation_url": "string", - "oauth2_scopes": "string", - "oauth2_token_url": "string", - "slug": "string", - "tool_allow_list": [ - "string" - ], - "tool_deny_list": [ - "string" - ], - "transport": "streamable_http", - "url": "string" -} -``` - -### Parameters - -| Name | In | Type | Required | Description | -|----------------|------|------------------------------------------------------------------------------------------|----------|----------------------------------| -| `organization` | path | string | true | Organization name or ID | -| `body` | body | [codersdk.CreateMCPServerConfigRequest](schemas.md#codersdkcreatemcpserverconfigrequest) | true | Create MCP server config request | - -### Example responses - -> 201 Response - -```json -{ - "allow_in_plan_mode": true, - "api_key_header": "string", - "auth_connected": true, - "auth_type": "string", - "availability": "string", - "created_at": "2019-08-24T14:15:22Z", - "description": "string", - "display_name": "string", - "enabled": true, - "forward_coder_headers": true, - "has_api_key": true, - "has_custom_headers": true, - "has_oauth2_secret": true, - "icon_url": "string", - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "model_intent": true, - "oauth2_auth_url": "string", - "oauth2_client_id": "string", - "oauth2_revocation_url": "string", - "oauth2_scopes": "string", - "oauth2_token_url": "string", - "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", - "slug": "string", - "tool_allow_list": [ - "string" - ], - "tool_deny_list": [ - "string" - ], - "transport": "string", - "updated_at": "2019-08-24T14:15:22Z", - "url": "string" -} -``` - -### Responses - -| Status | Meaning | Description | Schema | -|--------|--------------------------------------------------------------|-------------|----------------------------------------------------------------| -| 201 | [Created](https://tools.ietf.org/html/rfc7231#section-6.3.2) | Created | [codersdk.MCPServerConfig](schemas.md#codersdkmcpserverconfig) | - -To perform this operation, you must be authenticated. [Learn more](authentication.md). - -## Get MCP server config - -### Code samples - -```sh -# Example request using curl -curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/mcp-servers/{mcpserverconfig} \ - -H 'Accept: application/json' \ - -H 'Coder-Session-Token: API_KEY' -``` - -`GET /api/v2/organizations/{organization}/mcp-servers/{mcpserverconfig}` - -### Parameters - -| Name | In | Type | Required | Description | -|-------------------|------|--------------|----------|-------------------------| -| `organization` | path | string | true | Organization name or ID | -| `mcpserverconfig` | path | string(uuid) | true | MCP server config ID | - -### Example responses - -> 200 Response - -```json -{ - "allow_in_plan_mode": true, - "api_key_header": "string", - "auth_connected": true, - "auth_type": "string", - "availability": "string", - "created_at": "2019-08-24T14:15:22Z", - "description": "string", - "display_name": "string", - "enabled": true, - "forward_coder_headers": true, - "has_api_key": true, - "has_custom_headers": true, - "has_oauth2_secret": true, - "icon_url": "string", - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "model_intent": true, - "oauth2_auth_url": "string", - "oauth2_client_id": "string", - "oauth2_revocation_url": "string", - "oauth2_scopes": "string", - "oauth2_token_url": "string", - "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", - "slug": "string", - "tool_allow_list": [ - "string" - ], - "tool_deny_list": [ - "string" - ], - "transport": "string", - "updated_at": "2019-08-24T14:15:22Z", - "url": "string" -} -``` - -### Responses - -| Status | Meaning | Description | Schema | -|--------|---------------------------------------------------------|-------------|----------------------------------------------------------------| -| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.MCPServerConfig](schemas.md#codersdkmcpserverconfig) | - -To perform this operation, you must be authenticated. [Learn more](authentication.md). - -## Delete MCP server config - -### Code samples - -```sh -# Example request using curl -curl -X DELETE http://coder-server:8080/api/v2/organizations/{organization}/mcp-servers/{mcpserverconfig} \ - -H 'Coder-Session-Token: API_KEY' -``` - -`DELETE /api/v2/organizations/{organization}/mcp-servers/{mcpserverconfig}` - -### Parameters - -| Name | In | Type | Required | Description | -|-------------------|------|--------------|----------|-------------------------| -| `organization` | path | string | true | Organization name or ID | -| `mcpserverconfig` | path | string(uuid) | true | MCP server config 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). - -## Update MCP server config - -### Code samples - -```sh -# Example request using curl -curl -X PATCH http://coder-server:8080/api/v2/organizations/{organization}/mcp-servers/{mcpserverconfig} \ - -H 'Content-Type: application/json' \ - -H 'Accept: application/json' \ - -H 'Coder-Session-Token: API_KEY' -``` - -`PATCH /api/v2/organizations/{organization}/mcp-servers/{mcpserverconfig}` - -> Body parameter - -```json -{ - "allow_in_plan_mode": true, - "api_key_header": "string", - "api_key_value": "string", - "auth_type": "none", - "availability": "force_on", - "custom_headers": { - "property1": "string", - "property2": "string" - }, - "description": "string", - "display_name": "string", - "enabled": true, - "forward_coder_headers": true, - "icon_url": "string", - "model_intent": true, - "oauth2_auth_url": "string", - "oauth2_client_id": "string", - "oauth2_client_secret": "string", - "oauth2_revocation_url": "string", - "oauth2_scopes": "string", - "oauth2_token_url": "string", - "slug": "string", - "tool_allow_list": [ - "string" - ], - "tool_deny_list": [ - "string" - ], - "transport": "streamable_http", - "url": "string" -} -``` - -### Parameters - -| Name | In | Type | Required | Description | -|-------------------|------|------------------------------------------------------------------------------------------|----------|----------------------------------| -| `organization` | path | string | true | Organization name or ID | -| `mcpserverconfig` | path | string(uuid) | true | MCP server config ID | -| `body` | body | [codersdk.UpdateMCPServerConfigRequest](schemas.md#codersdkupdatemcpserverconfigrequest) | true | Update MCP server config request | - -### Example responses - -> 200 Response - -```json -{ - "allow_in_plan_mode": true, - "api_key_header": "string", - "auth_connected": true, - "auth_type": "string", - "availability": "string", - "created_at": "2019-08-24T14:15:22Z", - "description": "string", - "display_name": "string", - "enabled": true, - "forward_coder_headers": true, - "has_api_key": true, - "has_custom_headers": true, - "has_oauth2_secret": true, - "icon_url": "string", - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "model_intent": true, - "oauth2_auth_url": "string", - "oauth2_client_id": "string", - "oauth2_revocation_url": "string", - "oauth2_scopes": "string", - "oauth2_token_url": "string", - "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", - "slug": "string", - "tool_allow_list": [ - "string" - ], - "tool_deny_list": [ - "string" - ], - "transport": "string", - "updated_at": "2019-08-24T14:15:22Z", - "url": "string" -} -``` - -### Responses - -| Status | Meaning | Description | Schema | -|--------|---------------------------------------------------------|-------------|----------------------------------------------------------------| -| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.MCPServerConfig](schemas.md#codersdkmcpserverconfig) | - -To perform this operation, you must be authenticated. [Learn more](authentication.md). - -## Get MCP server config ACL - -### Code samples - -```sh -# Example request using curl -curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/mcp-servers/{mcpserverconfig}/acl \ - -H 'Accept: application/json' \ - -H 'Coder-Session-Token: API_KEY' -``` - -`GET /api/v2/organizations/{organization}/mcp-servers/{mcpserverconfig}/acl` - -### Parameters - -| Name | In | Type | Required | Description | -|-------------------|------|--------------|----------|-------------------------| -| `organization` | path | string | true | Organization name or ID | -| `mcpserverconfig` | path | string(uuid) | true | MCP server config ID | - -### Example responses - -> 200 Response - -```json -{ - "groups": [ - { - "avatar_url": "http://example.com", - "display_name": "string", - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "members": [ - { - "avatar_url": "http://example.com", - "created_at": "2019-08-24T14:15:22Z", - "email": "user@example.com", - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "is_service_account": true, - "last_seen_at": "2019-08-24T14:15:22Z", - "login_type": "", - "name": "string", - "status": "active", - "theme_preference": "string", - "updated_at": "2019-08-24T14:15:22Z", - "username": "string" - } - ], - "name": "string", - "organization_display_name": "string", - "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", - "organization_name": "string", - "quota_allowance": 0, - "role": "read", - "source": "user", - "total_member_count": 0 - } - ], - "users": [ - { - "avatar_url": "http://example.com", - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "name": "string", - "role": "read", - "username": "string" - } - ] -} -``` - -### Responses - -| Status | Meaning | Description | Schema | -|--------|---------------------------------------------------------|-------------|----------------------------------------------------------------------| -| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.MCPServerConfigACL](schemas.md#codersdkmcpserverconfigacl) | - -To perform this operation, you must be authenticated. [Learn more](authentication.md). - -## Update MCP server config ACL - -### Code samples - -```sh -# Example request using curl -curl -X PATCH http://coder-server:8080/api/v2/organizations/{organization}/mcp-servers/{mcpserverconfig}/acl \ - -H 'Content-Type: application/json' \ - -H 'Coder-Session-Token: API_KEY' -``` - -`PATCH /api/v2/organizations/{organization}/mcp-servers/{mcpserverconfig}/acl` - -> Body parameter - -```json -{ - "group_roles": { - "property1": "read", - "property2": "read" - }, - "user_roles": { - "property1": "read", - "property2": "read" - } -} -``` - -### Parameters - -| Name | In | Type | Required | Description | -|-------------------|------|------------------------------------------------------------------------------------------------|----------|--------------------------------------| -| `organization` | path | string | true | Organization name or ID | -| `mcpserverconfig` | path | string(uuid) | true | MCP server config ID | -| `body` | body | [codersdk.UpdateMCPServerConfigACLRequest](schemas.md#codersdkupdatemcpserverconfigaclrequest) | true | Update MCP server config ACL request | - -### 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). - -## Initiate MCP server OAuth2 connect - -### Code samples - -```sh -# Example request using curl -curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/mcp-servers/{mcpserverconfig}/oauth2/connect \ - -H 'Coder-Session-Token: API_KEY' -``` - -`GET /api/v2/organizations/{organization}/mcp-servers/{mcpserverconfig}/oauth2/connect` - -### Parameters - -| Name | In | Type | Required | Description | -|-------------------|------|--------------|----------|-------------------------| -| `organization` | path | string | true | Organization name or ID | -| `mcpserverconfig` | path | string(uuid) | true | MCP server config ID | - -### Responses - -| Status | Meaning | Description | Schema | -|--------|-------------------------------------------------------------------------|--------------------|--------| -| 307 | [Temporary Redirect](https://tools.ietf.org/html/rfc7231#section-6.4.7) | Temporary Redirect | | - -To perform this operation, you must be authenticated. [Learn more](authentication.md). From 9c5f3019d35ef6d542d6738f99fee118724bb962 Mon Sep 17 00:00:00 2001 From: Michael Suchacz <203725896+ibetitsmike@users.noreply.github.com> Date: Tue, 25 Aug 2026 02:07:19 +0000 Subject: [PATCH 06/15] fix: publish usable chat endpoint documentation --- coderd/apidoc/docs.go | 26 ++++---- coderd/apidoc/swagger.json | 26 ++++---- coderd/exp_chats.go | 7 +-- docs/reference/api/chats.md | 62 ++++++++++++++----- .../apidocgen/markdown-template/code_sh.dot | 6 +- .../apidocgen/markdown-template/operation.dot | 2 +- 6 files changed, 82 insertions(+), 47 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 28bcd2a2cbf..9424b919455 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -1442,10 +1442,7 @@ const docTemplate = `{ { "CoderSessionToken": [] } - ], - "x-apidocgen": { - "skip": true - } + ] } }, "/api/v2/chats/config/auto-archive-days": { @@ -2128,7 +2125,6 @@ const docTemplate = `{ }, "/api/v2/chats/files": { "post": { - "description": "Swagger notice: Swagger 2.0 cannot model the raw binary request body this endpoint reads, so the upload is described as form data.", "consumes": [ "image/png", "image/jpeg", @@ -2159,17 +2155,20 @@ const docTemplate = `{ }, { "type": "string", - "description": "Attachment disposition carrying the file name, e.g. ` + "`" + `attachment; filename=\\", + "example": "attachment; filename=\"image.png\"", + "description": "Attachment disposition carrying the file name", "name": "Content-Disposition", "in": "header", "required": true }, { - "type": "file", - "description": "File to be uploaded, sent as the raw request body", - "name": "file", - "in": "formData", - "required": true + "description": "Raw file binary data", + "name": "request", + "in": "body", + "required": true, + "schema": { + "type": "string" + } } ], "responses": { @@ -2190,7 +2189,10 @@ const docTemplate = `{ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "rawBodyFile": "image.png" + } } }, "/api/v2/chats/files/{file}": { diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 5fc08871ec1..85d7ab4f3e7 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -1263,10 +1263,7 @@ { "CoderSessionToken": [] } - ], - "x-apidocgen": { - "skip": true - } + ] } }, "/api/v2/chats/config/auto-archive-days": { @@ -1855,7 +1852,6 @@ }, "/api/v2/chats/files": { "post": { - "description": "Swagger notice: Swagger 2.0 cannot model the raw binary request body this endpoint reads, so the upload is described as form data.", "consumes": [ "image/png", "image/jpeg", @@ -1882,17 +1878,20 @@ }, { "type": "string", - "description": "Attachment disposition carrying the file name, e.g. `attachment; filename=\\", + "example": "attachment; filename=\"image.png\"", + "description": "Attachment disposition carrying the file name", "name": "Content-Disposition", "in": "header", "required": true }, { - "type": "file", - "description": "File to be uploaded, sent as the raw request body", - "name": "file", - "in": "formData", - "required": true + "description": "Raw file binary data", + "name": "request", + "in": "body", + "required": true, + "schema": { + "type": "string" + } } ], "responses": { @@ -1913,7 +1912,10 @@ { "CoderSessionToken": [] } - ] + ], + "x-apidocgen": { + "rawBodyFile": "image.png" + } } }, "/api/v2/chats/files/{file}": { diff --git a/coderd/exp_chats.go b/coderd/exp_chats.go index f09efba7a5b..3a51ded15d7 100644 --- a/coderd/exp_chats.go +++ b/coderd/exp_chats.go @@ -326,7 +326,6 @@ type chatsByWorkspaceResponse map[uuid.UUID]uuid.UUID // @Produce json // @Success 200 {object} chatsByWorkspaceResponse // @Router /api/v2/chats/by-workspace [get] -// @x-apidocgen {"skip": true} func (api *API) chatsByWorkspace(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() @@ -6085,15 +6084,15 @@ func (api *API) deleteUserChatCompactionThreshold(rw http.ResponseWriter, r *htt } // @Summary Upload chat file -// @Description Swagger notice: Swagger 2.0 cannot model the raw binary request body this endpoint reads, so the upload is described as form data. // @ID upload-chat-file // @Security CoderSessionToken // @Tags Chats // @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, e.g. `attachment; filename=\"img.png\"`" -// @Param file formData file true "File to be uploaded, sent as the raw request body" +// @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/v2/chats/files [post] diff --git a/docs/reference/api/chats.md b/docs/reference/api/chats.md index cd360d60241..ca589825aa1 100644 --- a/docs/reference/api/chats.md +++ b/docs/reference/api/chats.md @@ -556,6 +556,44 @@ curl -X POST http://coder-server:8080/api/v2/chats \ 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 @@ -1174,28 +1212,22 @@ To perform this operation, you must be authenticated. [Learn more](authenticatio ```sh # Example request using curl 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 'Content-Disposition: string' \ - -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/v2/chats/files` -> Body parameter - -```yaml -file: string - -``` - ### Parameters -| Name | In | Type | Required | Description | -|----------------|-------|--------------|----------|-----------------| -| `organization` | query | string(uuid) | true | Organization ID | -|`Content-Disposition`|header|string|true|Attachment disposition carrying the file name, e.g. `attachment; filename=\| -|`body`|body|object|true|| -|`» file`|body|binary|true|File to be uploaded, sent as the raw request body| +| 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 diff --git a/scripts/apidocgen/markdown-template/code_sh.dot b/scripts/apidocgen/markdown-template/code_sh.dot index 7300ae4ab29..7b1fe793ab1 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 57856444b3e..1348c2d37ee 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 }} From 8d0a0fc841c562bd6b0a6d95785b46ba8f0e087d Mon Sep 17 00:00:00 2001 From: Michael Suchacz <203725896+ibetitsmike@users.noreply.github.com> Date: Tue, 25 Aug 2026 03:01:56 +0000 Subject: [PATCH 07/15] fix(coderd): keep MCP OAuth callback experimental --- coderd/apidoc/docs.go | 118 ++++++++++++++--------------- coderd/apidoc/swagger.json | 110 +++++++++++++-------------- coderd/chat_routes.go | 8 +- coderd/chat_routes_test.go | 1 + coderd/coderd.go | 2 +- coderd/coderdtest/swaggerparser.go | 1 - coderd/mcp.go | 2 +- 7 files changed, 123 insertions(+), 119 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 9424b919455..1f3c10882de 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -187,6 +187,65 @@ const docTemplate = `{ ] } }, + "/api/experimental/mcp/servers/{mcpServer}/oauth2/callback": { + "get": { + "produces": [ + "text/html" + ], + "tags": [ + "MCP" + ], + "summary": "Handle MCP server OAuth2 callback", + "operationId": "handle-mcp-server-oauth2-callback", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "MCP server config ID", + "name": "mcpServer", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Authorization code issued by the provider. Required together with state on success.", + "name": "code", + "in": "query" + }, + { + "type": "string", + "description": "Opaque state issued by the connect endpoint. Required together with code on success.", + "name": "state", + "in": "query" + }, + { + "type": "string", + "description": "Provider error code. Present instead of code when authorization fails.", + "name": "error", + "in": "query" + }, + { + "type": "string", + "description": "Provider error description accompanying error.", + "name": "error_description", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + } + }, "/api/experimental/users/{user}/skills": { "get": { "produces": [ @@ -5032,65 +5091,6 @@ const docTemplate = `{ ] } }, - "/api/v2/mcp/servers/{mcpServer}/oauth2/callback": { - "get": { - "produces": [ - "text/html" - ], - "tags": [ - "MCP" - ], - "summary": "Handle MCP server OAuth2 callback", - "operationId": "handle-mcp-server-oauth2-callback", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "MCP server config ID", - "name": "mcpServer", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Authorization code issued by the provider. Required together with state on success.", - "name": "code", - "in": "query" - }, - { - "type": "string", - "description": "Opaque state issued by the connect endpoint. Required together with code on success.", - "name": "state", - "in": "query" - }, - { - "type": "string", - "description": "Provider error code. Present instead of code when authorization fails.", - "name": "error", - "in": "query" - }, - { - "type": "string", - "description": "Provider error description accompanying error.", - "name": "error_description", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK" - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ], - "x-apidocgen": { - "skip": true - } - } - }, "/api/v2/mcp/servers/{mcpServer}/oauth2/disconnect": { "delete": { "produces": [ diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 85d7ab4f3e7..ecd45c29160 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -156,6 +156,61 @@ ] } }, + "/api/experimental/mcp/servers/{mcpServer}/oauth2/callback": { + "get": { + "produces": ["text/html"], + "tags": ["MCP"], + "summary": "Handle MCP server OAuth2 callback", + "operationId": "handle-mcp-server-oauth2-callback", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "MCP server config ID", + "name": "mcpServer", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Authorization code issued by the provider. Required together with state on success.", + "name": "code", + "in": "query" + }, + { + "type": "string", + "description": "Opaque state issued by the connect endpoint. Required together with code on success.", + "name": "state", + "in": "query" + }, + { + "type": "string", + "description": "Provider error code. Present instead of code when authorization fails.", + "name": "error", + "in": "query" + }, + { + "type": "string", + "description": "Provider error description accompanying error.", + "name": "error_description", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + } + }, "/api/experimental/users/{user}/skills": { "get": { "produces": ["application/json"], @@ -4442,61 +4497,6 @@ ] } }, - "/api/v2/mcp/servers/{mcpServer}/oauth2/callback": { - "get": { - "produces": ["text/html"], - "tags": ["MCP"], - "summary": "Handle MCP server OAuth2 callback", - "operationId": "handle-mcp-server-oauth2-callback", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "MCP server config ID", - "name": "mcpServer", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Authorization code issued by the provider. Required together with state on success.", - "name": "code", - "in": "query" - }, - { - "type": "string", - "description": "Opaque state issued by the connect endpoint. Required together with code on success.", - "name": "state", - "in": "query" - }, - { - "type": "string", - "description": "Provider error code. Present instead of code when authorization fails.", - "name": "error", - "in": "query" - }, - { - "type": "string", - "description": "Provider error description accompanying error.", - "name": "error_description", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK" - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ], - "x-apidocgen": { - "skip": true - } - } - }, "/api/v2/mcp/servers/{mcpServer}/oauth2/disconnect": { "delete": { "produces": ["application/json"], diff --git a/coderd/chat_routes.go b/coderd/chat_routes.go index 8ac65cc5bd6..f65e810c66c 100644 --- a/coderd/chat_routes.go +++ b/coderd/chat_routes.go @@ -168,9 +168,13 @@ func (api *API) registerChatStreamRoutes(r chi.Router) { r.Get("/git", api.watchChatGit) } -func (api *API) registerMCPServerOAuth2Routes(r chi.Router) { - // This callback path is frozen because it is registered with OAuth2 providers. +func (api *API) registerExperimentalMCPServerOAuth2Routes(r chi.Router) { + // TODO(CODAGT-922): migrate provider redirect URIs before promoting this callback. r.Get("/servers/{mcpServer}/oauth2/callback", api.mcpServerOAuth2Callback) + api.registerMCPServerOAuth2Routes(r) +} + +func (api *API) registerMCPServerOAuth2Routes(r chi.Router) { // 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) diff --git a/coderd/chat_routes_test.go b/coderd/chat_routes_test.go index 40ff941ef62..c6ae46c64ec 100644 --- a/coderd/chat_routes_test.go +++ b/coderd/chat_routes_test.go @@ -41,6 +41,7 @@ func TestChatRoutesCompatibility(t *testing.T) { {http.MethodGet, "/api/v2/chats/config/advisor"}, {http.MethodGet, "/api/v2/chats/00000000-0000-0000-0000-000000000000/debug/runs"}, {http.MethodGet, "/api/v2/chats/00000000-0000-0000-0000-000000000000/stream/desktop"}, + {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) diff --git a/coderd/coderd.go b/coderd/coderd.go index a9db7d56df0..920d6506da7 100644 --- a/coderd/coderd.go +++ b/coderd/coderd.go @@ -1458,7 +1458,7 @@ func New(options *Options) *API { r.Route("/mcp", func(r chi.Router) { r.Use(apiKeyMiddleware) - api.registerMCPServerOAuth2Routes(r) + api.registerExperimentalMCPServerOAuth2Routes(r) // MCP HTTP transport endpoint with mandatory authentication. r.Route("/http", func(r chi.Router) { r.Use(httpmw.RequireExperimentWithDevBypass(api.Experiments, codersdk.ExperimentOAuth2, codersdk.ExperimentMCPServerHTTP)) diff --git a/coderd/coderdtest/swaggerparser.go b/coderd/coderdtest/swaggerparser.go index 980b6d39728..97f1ac92d9d 100644 --- a/coderd/coderdtest/swaggerparser.go +++ b/coderd/coderdtest/swaggerparser.go @@ -430,7 +430,6 @@ func assertProduce(t *testing.T, comment SwaggerComment) { (comment.router == "/api/v2/workspaceagents/me/startup/logs" && comment.method == "patch") || (comment.router == "/api/v2/licenses/{id}" && comment.method == "delete") || (comment.router == "/api/v2/debug/coordinator" && comment.method == "get") || - (comment.router == "/api/v2/mcp/servers/{mcpServer}/oauth2/callback" && comment.method == "get") || (comment.router == "/api/v2/debug/tailnet" && comment.method == "get") || (comment.router == "/api/v2/workspaces/{workspace}/acl" && comment.method == "patch") || (comment.router == "/api/v2/init-script/{os}/{arch}" && comment.method == "get") || diff --git a/coderd/mcp.go b/coderd/mcp.go index ffdb9e1d06a..a1b297bfd36 100644 --- a/coderd/mcp.go +++ b/coderd/mcp.go @@ -1119,7 +1119,7 @@ func (api *API) mcpServerOAuth2Connect(rw http.ResponseWriter, r *http.Request) // @Param error_description query string false "Provider error description accompanying error." // @Produce text/html // @Success 200 -// @Router /api/v2/mcp/servers/{mcpServer}/oauth2/callback [get] +// @Router /api/experimental/mcp/servers/{mcpServer}/oauth2/callback [get] // @x-apidocgen {"skip": true} // //nolint:revive // HTTP handler writes to ResponseWriter. From 8b47de4714dd88334e3b4bb5cbcf2bdd5b9b7665 Mon Sep 17 00:00:00 2001 From: Michael Suchacz <203725896+ibetitsmike@users.noreply.github.com> Date: Tue, 25 Aug 2026 03:45:12 +0000 Subject: [PATCH 08/15] fix: align promoted chat API contracts --- coderd/apidoc/docs.go | 8 ++++++ coderd/apidoc/swagger.json | 8 ++++++ coderd/chat_routes.go | 2 +- coderd/exp_chats.go | 14 +++++----- coderd/httpmw/ratelimit.go | 34 +++++++++++++++++------- coderd/httpmw/ratelimit_test.go | 47 +++++++++++++++++++++++++++++++++ docs/reference/api/chats.md | 34 ++++++++++++------------ 7 files changed, 113 insertions(+), 34 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 1f3c10882de..0fc3afa668f 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -1946,6 +1946,7 @@ const docTemplate = `{ "parameters": [ { "type": "string", + "format": "uuid", "description": "Model config ID", "name": "modelConfig", "in": "path", @@ -1987,6 +1988,7 @@ const docTemplate = `{ "parameters": [ { "type": "string", + "format": "uuid", "description": "Model config ID", "name": "modelConfig", "in": "path", @@ -2965,6 +2967,7 @@ const docTemplate = `{ "parameters": [ { "type": "string", + "format": "uuid", "description": "Chat ID", "name": "chat", "in": "path", @@ -3003,6 +3006,7 @@ const docTemplate = `{ "parameters": [ { "type": "string", + "format": "uuid", "description": "Chat ID", "name": "chat", "in": "path", @@ -3199,6 +3203,7 @@ const docTemplate = `{ "parameters": [ { "type": "string", + "format": "uuid", "description": "Chat ID", "name": "chat", "in": "path", @@ -3268,6 +3273,7 @@ const docTemplate = `{ "parameters": [ { "type": "string", + "format": "uuid", "description": "Chat ID", "name": "chat", "in": "path", @@ -11766,6 +11772,7 @@ const docTemplate = `{ }, { "type": "string", + "format": "uuid", "description": "AI provider ID", "name": "aiProvider", "in": "path", @@ -11811,6 +11818,7 @@ const docTemplate = `{ }, { "type": "string", + "format": "uuid", "description": "AI provider ID", "name": "aiProvider", "in": "path", diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index ecd45c29160..2b30d0e0d4d 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -1697,6 +1697,7 @@ "parameters": [ { "type": "string", + "format": "uuid", "description": "Model config ID", "name": "modelConfig", "in": "path", @@ -1736,6 +1737,7 @@ "parameters": [ { "type": "string", + "format": "uuid", "description": "Model config ID", "name": "modelConfig", "in": "path", @@ -2610,6 +2612,7 @@ "parameters": [ { "type": "string", + "format": "uuid", "description": "Chat ID", "name": "chat", "in": "path", @@ -2644,6 +2647,7 @@ "parameters": [ { "type": "string", + "format": "uuid", "description": "Chat ID", "name": "chat", "in": "path", @@ -2820,6 +2824,7 @@ "parameters": [ { "type": "string", + "format": "uuid", "description": "Chat ID", "name": "chat", "in": "path", @@ -2881,6 +2886,7 @@ "parameters": [ { "type": "string", + "format": "uuid", "description": "Chat ID", "name": "chat", "in": "path", @@ -10437,6 +10443,7 @@ }, { "type": "string", + "format": "uuid", "description": "AI provider ID", "name": "aiProvider", "in": "path", @@ -10480,6 +10487,7 @@ }, { "type": "string", + "format": "uuid", "description": "AI provider ID", "name": "aiProvider", "in": "path", diff --git a/coderd/chat_routes.go b/coderd/chat_routes.go index f65e810c66c..a0786539b55 100644 --- a/coderd/chat_routes.go +++ b/coderd/chat_routes.go @@ -17,7 +17,7 @@ import ( // prefixes cannot double the budget. func (api *API) chatFilesRateLimitMW() func(http.Handler) http.Handler { api.chatFilesRateLimitOnce.Do(func() { - api.chatFilesRateLimit = httpmw.RateLimit(api.FilesRateLimit, time.Minute) + api.chatFilesRateLimit = httpmw.RateLimitByAPICompatibilityEndpoint(api.FilesRateLimit, time.Minute) }) return api.chatFilesRateLimit } diff --git a/coderd/exp_chats.go b/coderd/exp_chats.go index 3a51ded15d7..04fe7963617 100644 --- a/coderd/exp_chats.go +++ b/coderd/exp_chats.go @@ -2902,7 +2902,7 @@ func (api *API) patchChatMessage(rw http.ResponseWriter, r *http.Request) { // @ID delete-chat-queued-message // @Security CoderSessionToken // @Tags Chats -// @Param chat path string true "Chat ID" +// @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] @@ -2962,7 +2962,7 @@ func (api *API) deleteChatQueuedMessage(rw http.ResponseWriter, r *http.Request) // @ID promote-chat-queued-message // @Security CoderSessionToken // @Tags Chats -// @Param chat path string true "Chat ID" +// @Param chat path string true "Chat ID" format(uuid) // @Param queuedMessage path int true "Queued message ID" // @Produce json // @Success 202 {object} codersdk.Response @@ -5973,7 +5973,7 @@ func (api *API) getUserChatCompactionThresholds(rw http.ResponseWriter, r *http. // @ID update-user-chat-compaction-threshold // @Security CoderSessionToken // @Tags Chats -// @Param modelConfig path string true "Model config ID" +// @Param modelConfig path string true "Model config ID" format(uuid) // @Accept json // @Param request body codersdk.UpdateUserChatCompactionThresholdRequest true "Request body" // @Produce json @@ -6054,7 +6054,7 @@ func (api *API) putUserChatCompactionThreshold(rw http.ResponseWriter, r *http.R // @ID delete-user-chat-compaction-threshold // @Security CoderSessionToken // @Tags Chats -// @Param modelConfig path string true "Model config ID" +// @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} @@ -6700,7 +6700,7 @@ func (api *API) listUserAIProviderKeyConfigs(rw http.ResponseWriter, r *http.Req // @Security CoderSessionToken // @Tags Chats // @Param user path string true "User ID, username, or me" -// @Param aiProvider path string true "AI provider ID" +// @Param aiProvider path string true "AI provider ID" format(uuid) // @Accept json // @Param request body codersdk.CreateUserAIProviderKeyRequest true "Request body" // @Produce json @@ -6787,7 +6787,7 @@ func (api *API) upsertUserAIProviderKey(rw http.ResponseWriter, r *http.Request) // @Security CoderSessionToken // @Tags Chats // @Param user path string true "User ID, username, or me" -// @Param aiProvider path string true "AI provider ID" +// @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) { @@ -8064,7 +8064,7 @@ func ChatProviderAPIKeysFromDeploymentValues( // @ID submit-chat-tool-results // @Security CoderSessionToken // @Tags Chats -// @Param chat path string true "Chat ID" +// @Param chat path string true "Chat ID" format(uuid) // @Accept json // @Param request body codersdk.SubmitToolResultsRequest true "Request body" // @Success 204 diff --git a/coderd/httpmw/ratelimit.go b/coderd/httpmw/ratelimit.go index 8b7de2f71b1..a36be183daf 100644 --- a/coderd/httpmw/ratelimit.go +++ b/coderd/httpmw/ratelimit.go @@ -23,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 { @@ -87,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), @@ -108,14 +118,20 @@ func keyByNormalizedEndpoint(r *http.Request) (string, error) { if p == "" { p = "/" } - p = path.Clean(p) - // Routes mounted under both prefixes during the /api/experimental to - // /api/v2 compatibility window share one limiter instance; stripping - // the prefix keeps them in one bucket per endpoint. - for _, prefix := range []string{"/api/v2/", "/api/experimental/"} { - if strings.HasPrefix(p, prefix) { - p = p[len(prefix)-1:] - break + 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 diff --git a/coderd/httpmw/ratelimit_test.go b/coderd/httpmw/ratelimit_test.go index c6122685f87..8acb2fbd446 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/docs/reference/api/chats.md b/docs/reference/api/chats.md index ca589825aa1..4df0da0e688 100644 --- a/docs/reference/api/chats.md +++ b/docs/reference/api/chats.md @@ -3268,10 +3268,10 @@ curl -X DELETE http://coder-server:8080/api/v2/chats/{chat}/queue/{queuedMessage ### Parameters -| Name | In | Type | Required | Description | -|-----------------|------|---------|----------|-------------------| -| `chat` | path | string | true | Chat ID | -| `queuedMessage` | path | integer | true | Queued message ID | +| Name | In | Type | Required | Description | +|-----------------|------|--------------|----------|-------------------| +| `chat` | path | string(uuid) | true | Chat ID | +| `queuedMessage` | path | integer | true | Queued message ID | ### Responses @@ -3296,10 +3296,10 @@ curl -X POST http://coder-server:8080/api/v2/chats/{chat}/queue/{queuedMessage}/ ### Parameters -| Name | In | Type | Required | Description | -|-----------------|------|---------|----------|-------------------| -| `chat` | path | string | true | Chat ID | -| `queuedMessage` | path | integer | true | Queued message ID | +| Name | In | Type | Required | Description | +|-----------------|------|--------------|----------|-------------------| +| `chat` | path | string(uuid) | true | Chat ID | +| `queuedMessage` | path | integer | true | Queued message ID | ### Example responses @@ -4029,9 +4029,9 @@ curl -X POST http://coder-server:8080/api/v2/chats/{chat}/title/propose \ ### Parameters -| Name | In | Type | Required | Description | -|--------|------|--------|----------|-------------| -| `chat` | path | string | true | Chat ID | +| Name | In | Type | Required | Description | +|--------|------|--------------|----------|-------------| +| `chat` | path | string(uuid) | true | Chat ID | ### Example responses @@ -4319,7 +4319,7 @@ curl -X POST http://coder-server:8080/api/v2/chats/{chat}/tool-results \ | Name | In | Type | Required | Description | |--------|------|----------------------------------------------------------------------------------|----------|--------------| -| `chat` | path | string | true | Chat ID | +| `chat` | path | string(uuid) | true | Chat ID | | `body` | body | [codersdk.SubmitToolResultsRequest](schemas.md#codersdksubmittoolresultsrequest) | true | Request body | ### Responses @@ -4432,7 +4432,7 @@ curl -X PUT http://coder-server:8080/api/v2/users/{user}/ai-provider-keys/{aiPro | Name | In | Type | Required | Description | |--------------|------|----------------------------------------------------------------------------------------------|----------|--------------------------| | `user` | path | string | true | User ID, username, or me | -| `aiProvider` | path | string | true | AI provider ID | +| `aiProvider` | path | string(uuid) | true | AI provider ID | | `body` | body | [codersdk.CreateUserAIProviderKeyRequest](schemas.md#codersdkcreateuseraiproviderkeyrequest) | true | Request body | ### Example responses @@ -4478,10 +4478,10 @@ curl -X DELETE http://coder-server:8080/api/v2/users/{user}/ai-provider-keys/{ai ### Parameters -| Name | In | Type | Required | Description | -|--------------|------|--------|----------|--------------------------| -| `user` | path | string | true | User ID, username, or me | -| `aiProvider` | path | string | true | AI provider ID | +| Name | In | Type | Required | Description | +|--------------|------|--------------|----------|--------------------------| +| `user` | path | string | true | User ID, username, or me | +| `aiProvider` | path | string(uuid) | true | AI provider ID | ### Responses From b816c4871a13217eb6c4026cf6a8636d01340eb5 Mon Sep 17 00:00:00 2001 From: Michael Suchacz <203725896+ibetitsmike@users.noreply.github.com> Date: Tue, 25 Aug 2026 03:52:06 +0000 Subject: [PATCH 09/15] test(coderd): cover chat file rate limit aliases --- coderd/chat_routes_internal_test.go | 36 +++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 coderd/chat_routes_internal_test.go diff --git a/coderd/chat_routes_internal_test.go b/coderd/chat_routes_internal_test.go new file mode 100644 index 00000000000..2522911f9f9 --- /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) + } +} From bbe548f734ee15a0d352624a66549bdb4d9e2ba3 Mon Sep 17 00:00:00 2001 From: Michael Suchacz <203725896+ibetitsmike@users.noreply.github.com> Date: Tue, 25 Aug 2026 04:27:21 +0000 Subject: [PATCH 10/15] fix(coderd): add UUID formats to chat model routes --- coderd/apidoc/docs.go | 3 +++ coderd/apidoc/swagger.json | 3 +++ coderd/coderdtest/swagger_test.go | 32 +++++++++++++++++++++++++++++++ coderd/exp_chats.go | 6 +++--- 4 files changed, 41 insertions(+), 3 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 0fc3afa668f..9600fad5567 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -6336,6 +6336,7 @@ const docTemplate = `{ }, { "type": "string", + "format": "uuid", "description": "Model ID", "name": "model", "in": "path", @@ -6375,6 +6376,7 @@ const docTemplate = `{ }, { "type": "string", + "format": "uuid", "description": "Model ID", "name": "model", "in": "path", @@ -6417,6 +6419,7 @@ const docTemplate = `{ }, { "type": "string", + "format": "uuid", "description": "Model ID", "name": "model", "in": "path", diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 2b30d0e0d4d..e386f044360 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -5593,6 +5593,7 @@ }, { "type": "string", + "format": "uuid", "description": "Model ID", "name": "model", "in": "path", @@ -5630,6 +5631,7 @@ }, { "type": "string", + "format": "uuid", "description": "Model ID", "name": "model", "in": "path", @@ -5666,6 +5668,7 @@ }, { "type": "string", + "format": "uuid", "description": "Model ID", "name": "model", "in": "path", diff --git a/coderd/coderdtest/swagger_test.go b/coderd/coderdtest/swagger_test.go index 07ea3c74400..f805264de73 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/exp_chats.go b/coderd/exp_chats.go index 04fe7963617..6abeaf6b6b7 100644 --- a/coderd/exp_chats.go +++ b/coderd/exp_chats.go @@ -7101,7 +7101,7 @@ func chatModelConfigRBACObject(config database.ChatModelConfig) rbac.Object { // @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/v2/organizations/{organization}/chats/models/{model} [get] // @x-apidocgen {"skip": true} @@ -7422,7 +7422,7 @@ func (api *API) createChatModelConfig(rw http.ResponseWriter, r *http.Request) { // @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/v2/organizations/{organization}/chats/models/{model} [patch] @@ -7676,7 +7676,7 @@ func (api *API) updateChatModelConfig(rw http.ResponseWriter, r *http.Request) { // @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/v2/organizations/{organization}/chats/models/{model} [delete] // @x-apidocgen {"skip": true} From c1e80c3b7015dc78a9b31bc5bb2ddb9cc292ac04 Mon Sep 17 00:00:00 2001 From: Michael Suchacz <203725896+ibetitsmike@users.noreply.github.com> Date: Tue, 25 Aug 2026 04:47:59 +0000 Subject: [PATCH 11/15] test(coderd): exercise v2 route exclusions --- coderd/chat_routes_test.go | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/coderd/chat_routes_test.go b/coderd/chat_routes_test.go index c6ae46c64ec..47d2f3c05e3 100644 --- a/coderd/chat_routes_test.go +++ b/coderd/chat_routes_test.go @@ -1,12 +1,15 @@ 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" ) @@ -14,8 +17,16 @@ func TestChatRoutesCompatibility(t *testing.T) { t.Parallel() ctx := testutil.Context(t, testutil.WaitLong) - client := coderdtest.New(t, nil) - coderdtest.CreateFirstUser(t, client) + 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", @@ -39,8 +50,8 @@ func TestChatRoutesCompatibility(t *testing.T) { {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, "/api/v2/chats/00000000-0000-0000-0000-000000000000/debug/runs"}, - {http.MethodGet, "/api/v2/chats/00000000-0000-0000-0000-000000000000/stream/desktop"}, + {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"}, } { From 16d345122eb1399cffc443e71ec9370e48f16118 Mon Sep 17 00:00:00 2001 From: Michael Suchacz <203725896+ibetitsmike@users.noreply.github.com> Date: Tue, 25 Aug 2026 09:43:35 +0000 Subject: [PATCH 12/15] docs(coderd/x/chatd): update promoted chat API paths --- coderd/x/chatd/ARCHITECTURE.md | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/coderd/x/chatd/ARCHITECTURE.md b/coderd/x/chatd/ARCHITECTURE.md index 1f2b27e3479..2ce7b68f64e 100644 --- a/coderd/x/chatd/ARCHITECTURE.md +++ b/coderd/x/chatd/ARCHITECTURE.md @@ -1260,17 +1260,15 @@ WHERE id = ANY($1::uuid[]); ## Relay mechanism -TODO(CODAGT-921): update this section for the /api/v2 promotion. The stream and parts endpoints are dual-mounted under /api/experimental and /api/v2 during the compatibility window, while the internal replica-to-replica relay dial stays on /api/experimental until CODAGT-922 removes the experimental mounts. - 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: @@ -1296,13 +1294,11 @@ The forwarder must only pass parts for the currently requested episode to the st ### Parts endpoint -TODO(CODAGT-921): update the endpoint paths in this section for the /api/v2 promotion and compatibility window. - 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. From efcd25c11db7e71871cfdaf571c2d36c78ed83a7 Mon Sep 17 00:00:00 2001 From: Michael Suchacz <203725896+ibetitsmike@users.noreply.github.com> Date: Tue, 25 Aug 2026 15:41:57 +0000 Subject: [PATCH 13/15] fix: regenerate chat title API documentation --- coderd/apidoc/docs.go | 37 +----- coderd/apidoc/swagger.json | 33 +---- docs/reference/api/chats.md | 249 +----------------------------------- 3 files changed, 9 insertions(+), 310 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 9600fad5567..cf761796672 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -1376,7 +1376,7 @@ const docTemplate = `{ "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; quote multi-word values; cannot be combined with title, pr_title, or pr; a value that tokenizes to no searchable words returns an empty list). Bare terms are not supported; use ` + "`" + `title:\u003cvalue\u003e` + "`" + ` or ` + "`" + `search:\u003cvalue\u003e` + "`" + `.", + "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" }, @@ -3225,41 +3225,6 @@ const docTemplate = `{ ] } }, - "/api/v2/chats/{chat}/title/regenerate": { - "post": { - "produces": [ - "application/json" - ], - "tags": [ - "Chats" - ], - "summary": "Regenerate chat title", - "operationId": "regenerate-chat-title", - "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}/tool-results": { "post": { "consumes": [ diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index e386f044360..9a11dce2041 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -1203,7 +1203,7 @@ "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; quote multi-word values; cannot be combined with title, pr_title, or pr; a value that tokenizes to no searchable words returns an empty list). Bare terms are not supported; use `title:\u003cvalue\u003e` or `search:\u003cvalue\u003e`.", + "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" }, @@ -2846,37 +2846,6 @@ ] } }, - "/api/v2/chats/{chat}/title/regenerate": { - "post": { - "produces": ["application/json"], - "tags": ["Chats"], - "summary": "Regenerate chat title", - "operationId": "regenerate-chat-title", - "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}/tool-results": { "post": { "consumes": ["application/json"], diff --git a/docs/reference/api/chats.md b/docs/reference/api/chats.md index 4df0da0e688..f2fca3e883c 100644 --- a/docs/reference/api/chats.md +++ b/docs/reference/api/chats.md @@ -54,13 +54,13 @@ curl -X GET http://coder-server:8080/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; quote multi-word values; cannot be combined with title, pr_title, or pr; a value that tokenizes to no searchable words 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 | +| 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 @@ -4051,241 +4051,6 @@ curl -X POST http://coder-server:8080/api/v2/chats/{chat}/title/propose \ To perform this operation, you must be authenticated. [Learn more](authentication.md). -## Regenerate chat title - -### Code samples - -```sh -# Example request using curl -curl -X POST http://coder-server:8080/api/v2/chats/{chat}/title/regenerate \ - -H 'Accept: application/json' \ - -H 'Coder-Session-Token: API_KEY' -``` - -`POST /api/v2/chats/{chat}/title/regenerate` - -### Parameters - -| Name | In | Type | Required | Description | -|--------|------|--------------|----------|-------------| -| `chat` | path | string(uuid) | true | Chat ID | - -### Example responses - -> 200 Response - -```json -{ - "agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e98978", - "archived": true, - "build_id": "bfb1f3fa-bf7b-43a5-9e0b-26cc050e44cb", - "children": [ - { - "agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e98978", - "archived": true, - "build_id": "bfb1f3fa-bf7b-43a5-9e0b-26cc050e44cb", - "children": [], - "client_type": "ui", - "context": { - "dirty": true, - "dirty_since": "2019-08-24T14:15:22Z", - "error": "string", - "resources": [ - { - "error": "string", - "kind": "instruction_file", - "size_bytes": 0, - "skill_description": "string", - "skill_name": "string", - "source": "string", - "status": "ok", - "tools": [ - { - "description": "string", - "name": "string" - } - ] - } - ] - }, - "created_at": "2019-08-24T14:15:22Z", - "diff_status": { - "additions": 0, - "approved": true, - "author_avatar_url": "string", - "author_login": "string", - "base_branch": "string", - "changed_files": 0, - "changes_requested": true, - "chat_id": "efc9fe20-a1e5-4a8c-9c48-f1b30c1e4f86", - "commits": 0, - "deletions": 0, - "head_branch": "string", - "pr_number": 0, - "pull_request_draft": true, - "pull_request_state": "string", - "pull_request_title": "string", - "refreshed_at": "2019-08-24T14:15:22Z", - "reviewer_count": 0, - "stale_at": "2019-08-24T14:15:22Z", - "url": "string" - }, - "files": [ - { - "created_at": "2019-08-24T14:15:22Z", - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "mime_type": "string", - "name": "string", - "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", - "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", - "size_bytes": 0 - } - ], - "has_unread": true, - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "labels": { - "property1": "string", - "property2": "string" - }, - "last_error": { - "detail": "string", - "kind": "generic", - "message": "string", - "provider": "string", - "retryable": true, - "status_code": 0 - }, - "last_model_config_id": "30ebb95f-c255-4759-9429-89aa4ec1554c", - "last_reasoning_effort": "string", - "last_turn_summary": "string", - "mcp_server_ids": [ - "497f6eca-6276-4993-bfeb-53cbbbba6f08" - ], - "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", - "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", - "owner_name": "string", - "owner_username": "string", - "parent_chat_id": "c3609ee6-3b11-4a93-b9ae-e4fabcc99359", - "pin_order": 0, - "plan_mode": "plan", - "queued_for_capacity": true, - "root_chat_id": "2898031c-fdce-4e3e-8c53-4481dd42fcd7", - "shared": true, - "status": "waiting", - "summary": "string", - "title": "string", - "updated_at": "2019-08-24T14:15:22Z", - "warnings": [ - "string" - ], - "workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9" - } - ], - "client_type": "ui", - "context": { - "dirty": true, - "dirty_since": "2019-08-24T14:15:22Z", - "error": "string", - "resources": [ - { - "error": "string", - "kind": "instruction_file", - "size_bytes": 0, - "skill_description": "string", - "skill_name": "string", - "source": "string", - "status": "ok", - "tools": [ - { - "description": "string", - "name": "string" - } - ] - } - ] - }, - "created_at": "2019-08-24T14:15:22Z", - "diff_status": { - "additions": 0, - "approved": true, - "author_avatar_url": "string", - "author_login": "string", - "base_branch": "string", - "changed_files": 0, - "changes_requested": true, - "chat_id": "efc9fe20-a1e5-4a8c-9c48-f1b30c1e4f86", - "commits": 0, - "deletions": 0, - "head_branch": "string", - "pr_number": 0, - "pull_request_draft": true, - "pull_request_state": "string", - "pull_request_title": "string", - "refreshed_at": "2019-08-24T14:15:22Z", - "reviewer_count": 0, - "stale_at": "2019-08-24T14:15:22Z", - "url": "string" - }, - "files": [ - { - "created_at": "2019-08-24T14:15:22Z", - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "mime_type": "string", - "name": "string", - "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", - "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", - "size_bytes": 0 - } - ], - "has_unread": true, - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "labels": { - "property1": "string", - "property2": "string" - }, - "last_error": { - "detail": "string", - "kind": "generic", - "message": "string", - "provider": "string", - "retryable": true, - "status_code": 0 - }, - "last_model_config_id": "30ebb95f-c255-4759-9429-89aa4ec1554c", - "last_reasoning_effort": "string", - "last_turn_summary": "string", - "mcp_server_ids": [ - "497f6eca-6276-4993-bfeb-53cbbbba6f08" - ], - "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", - "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", - "owner_name": "string", - "owner_username": "string", - "parent_chat_id": "c3609ee6-3b11-4a93-b9ae-e4fabcc99359", - "pin_order": 0, - "plan_mode": "plan", - "queued_for_capacity": true, - "root_chat_id": "2898031c-fdce-4e3e-8c53-4481dd42fcd7", - "shared": true, - "status": "waiting", - "summary": "string", - "title": "string", - "updated_at": "2019-08-24T14:15:22Z", - "warnings": [ - "string" - ], - "workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9" -} -``` - -### Responses - -| Status | Meaning | Description | Schema | -|--------|---------------------------------------------------------|-------------|------------------------------------------| -| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.Chat](schemas.md#codersdkchat) | - -To perform this operation, you must be authenticated. [Learn more](authentication.md). - ## Submit chat tool results ### Code samples From f06af18d6435a68ec9b91476bf328374a40f96dc Mon Sep 17 00:00:00 2001 From: Michael Suchacz <203725896+ibetitsmike@users.noreply.github.com> Date: Tue, 25 Aug 2026 22:05:14 +0000 Subject: [PATCH 14/15] docs: reference promoted /api/v2 chat endpoints in agent docs --- docs/ai-coder/agents/models.md | 4 +-- .../agents/platform-controls/mcp-servers.md | 2 +- .../agents/platform-controls/organizations.md | 4 +-- .../agents/tasks-to-chats-migration.md | 32 +++++++++---------- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/docs/ai-coder/agents/models.md b/docs/ai-coder/agents/models.md index 1eaefc88030..cf07e186e2a 100644 --- a/docs/ai-coder/agents/models.md +++ b/docs/ai-coder/agents/models.md @@ -359,9 +359,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 e74985686fe..c74b169531b 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 48a2ada5a76..57d50338197 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 9f9de000244..597410837ec 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' ``` From 968b8077ff782a3b465042803a542c12a62a58e3 Mon Sep 17 00:00:00 2001 From: Michael Suchacz <203725896+ibetitsmike@users.noreply.github.com> Date: Wed, 26 Aug 2026 13:56:27 +0000 Subject: [PATCH 15/15] refactor: consolidate chat API route registration Address review feedback on the /api/v2 chat promotion: - Collapse the per-group chat register helpers into a single registerChatAPIRoutes mounted once per API prefix, inverting the unpromoted experimental routes and the /api/v2 404 reservations into chat_routes.go behind a chatAPIPrefix selector. Merge the MCP OAuth2 helper pair and fold the organization MCP server config routes into registerOrganizationChatRoutes. - Keep GET /chats/models experimental-only instead of promoting it: the organization-scoped models collection supersedes it and #28632 removes it. /api/v2/chats/models now 404s via a segment reservation. - Reword the MCP OAuth2 callback TODO: providers pin the redirect URI for existing sessions, so promotion needs a re-auth story rather than a redirect URI migration. - Fold the chat file @Produce exceptions into the swagger parser's existing no-response-model exception list. --- coderd/apidoc/docs.go | 25 --- coderd/apidoc/swagger.json | 21 --- coderd/chat_routes.go | 287 +++++++++++++++++++---------- coderd/chat_routes_test.go | 2 + coderd/coderd.go | 97 +--------- coderd/coderdtest/swaggerparser.go | 11 +- coderd/exp_chats.go | 13 +- docs/reference/api/chats.md | 222 ---------------------- 8 files changed, 201 insertions(+), 477 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index cf761796672..72317ba0ff8 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -2379,31 +2379,6 @@ const docTemplate = `{ } } }, - "/api/v2/chats/models": { - "get": { - "produces": [ - "application/json" - ], - "tags": [ - "Chats" - ], - "summary": "List AI models and provider descriptors in the default organization", - "operationId": "list-ai-models-and-provider-descriptors-in-the-default-organization", - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.OrganizationChatModelsResponse" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ] - } - }, "/api/v2/chats/watch": { "get": { "produces": [ diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 9a11dce2041..9ca41846aef 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -2090,27 +2090,6 @@ } } }, - "/api/v2/chats/models": { - "get": { - "produces": ["application/json"], - "tags": ["Chats"], - "summary": "List AI models and provider descriptors in the default organization", - "operationId": "list-ai-models-and-provider-descriptors-in-the-default-organization", - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.OrganizationChatModelsResponse" - } - } - }, - "security": [ - { - "CoderSessionToken": [] - } - ] - } - }, "/api/v2/chats/watch": { "get": { "produces": ["application/json"], diff --git a/coderd/chat_routes.go b/coderd/chat_routes.go index a0786539b55..70a942fc3a4 100644 --- a/coderd/chat_routes.go +++ b/coderd/chat_routes.go @@ -6,6 +6,7 @@ import ( "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" @@ -22,23 +23,197 @@ func (api *API) chatFilesRateLimitMW() func(http.Handler) http.Handler { return api.chatFilesRateLimit } -func (api *API) registerChatFileDownloadRoute(r chi.Router) { +// 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) + }) + } + }) + }) } -func (api *API) registerDefaultOrganizationChatModelsRoute(r chi.Router) { - r.With( - 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(api.Database), - ).Get("/models", api.listDefaultOrganizationChatModels) +// 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) { @@ -49,7 +224,10 @@ func (api *API) registerUserAIProviderKeyRoutes(r chi.Router) { }) } -func (api *API) registerOrganizationMCPServerRoutes(r chi.Router) { +// 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) @@ -68,9 +246,6 @@ func (api *API) registerOrganizationMCPServerRoutes(r chi.Router) { policy.ActionRead)).Get("/oauth2/connect", api.mcpServerOAuth2Connect) }) }) -} - -func (api *API) registerOrganizationChatRoutes(r chi.Router) { r.Route("/chats/model-overrides", func(r chi.Router) { r.Get("/", api.getOrganizationChatModelOverrides) r.Put("/{context}", api.putOrganizationChatModelOverride) @@ -97,85 +272,3 @@ func (api *API) registerOrganizationMemberChatRoutes(r chi.Router) { r.Put("/{context}", api.putUserChatPersonalModelOverride) }) } - -func (api *API) registerChatCollectionRoutes(r chi.Router) { - 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) - }) -} - -func (api *API) registerChatConfigRoutes(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) -} - -func (api *API) registerChatRoutes(r chi.Router) { - 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) - }) -} - -func (api *API) registerChatStreamRoutes(r chi.Router) { - r.Get("/", api.streamChat) - r.Get("/parts", api.streamChatParts) - r.Get("/git", api.watchChatGit) -} - -func (api *API) registerExperimentalMCPServerOAuth2Routes(r chi.Router) { - // TODO(CODAGT-922): migrate provider redirect URIs before promoting this callback. - r.Get("/servers/{mcpServer}/oauth2/callback", api.mcpServerOAuth2Callback) - api.registerMCPServerOAuth2Routes(r) -} - -func (api *API) registerMCPServerOAuth2Routes(r chi.Router) { - // 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) -} diff --git a/coderd/chat_routes_test.go b/coderd/chat_routes_test.go index 47d2f3c05e3..5b54c307884 100644 --- a/coderd/chat_routes_test.go +++ b/coderd/chat_routes_test.go @@ -31,6 +31,7 @@ func TestChatRoutesCompatibility(t *testing.T) { 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", } { @@ -44,6 +45,7 @@ func TestChatRoutesCompatibility(t *testing.T) { 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"}, diff --git a/coderd/coderd.go b/coderd/coderd.go index 920d6506da7..ce04b0cb94c 100644 --- a/coderd/coderd.go +++ b/coderd/coderd.go @@ -1379,12 +1379,10 @@ func New(options *Options) *API { ) api.registerUserAIProviderKeyRoutes(r) }) - api.registerChatFileDownloadRoute(r) r.Route("/organizations", func(r chi.Router) { r.Use(apiKeyMiddleware) r.Route("/{organization}", func(r chi.Router) { r.Use(httpmw.ExtractOrganizationParam(options.Database)) - api.registerOrganizationMCPServerRoutes(r) api.registerOrganizationChatRoutes(r) r.Route("/members/{user}", func(r chi.Router) { r.Use(httpmw.ExtractOrganizationMemberParam(options.Database)) @@ -1392,73 +1390,11 @@ func New(options *Options) *API { }) }) }) - // 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.Route("/chats", func(r chi.Router) { - r.Use(apiKeyMiddleware) - api.registerDefaultOrganizationChatModelsRoute(r) - api.registerChatCollectionRoutes(r) - r.Route("/config", func(r chi.Router) { - api.registerChatConfigRoutes(r) - 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) - }) - }) - // 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)) - api.registerChatRoutes(r) - r.Route("/stream", func(r chi.Router) { - api.registerChatStreamRoutes(r) - r.Get("/desktop", api.watchChatDesktop) - }) - r.Route("/debug", func(r chi.Router) { - r.Get("/runs", api.getChatDebugRuns) - r.Get("/runs/{debugRun}", api.getChatDebugRun) - }) - }) - }) + api.registerChatAPIRoutes(r, apiKeyMiddleware, chatAPIPrefixExperimental) r.Route("/mcp", func(r chi.Router) { r.Use(apiKeyMiddleware) - api.registerExperimentalMCPServerOAuth2Routes(r) + 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)) @@ -1510,34 +1446,10 @@ func New(options *Options) *API { r.Get("/available", handleExperimentsAvailable) r.Get("/", api.handleExperimentsGet) }) - api.registerChatFileDownloadRoute(r) - r.Route("/chats", func(r chi.Router) { - r.Use(apiKeyMiddleware) - api.registerDefaultOrganizationChatModelsRoute(r) - api.registerChatCollectionRoutes(r) - r.Route("/config", api.registerChatConfigRoutes) - // 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{"/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.Route("/{chat}", func(r chi.Router) { - r.Use(httpmw.ExtractChatParam(options.Database)) - api.registerChatRoutes(r) - r.Route("/stream", api.registerChatStreamRoutes) - }) - }) + api.registerChatAPIRoutes(r, apiKeyMiddleware, chatAPIPrefixV2) r.Route("/mcp", func(r chi.Router) { r.Use(apiKeyMiddleware) - api.registerMCPServerOAuth2Routes(r) + api.registerMCPServerOAuth2Routes(r, chatAPIPrefixV2) }) r.Get("/updatecheck", api.updateCheck) @@ -1602,7 +1514,6 @@ func New(options *Options) *API { r.Use( httpmw.ExtractOrganizationParam(options.Database), ) - api.registerOrganizationMCPServerRoutes(r) api.registerOrganizationChatRoutes(r) r.Get("/", api.organization) r.Post("/templateversions", api.postTemplateVersionsByOrganization) diff --git a/coderd/coderdtest/swaggerparser.go b/coderd/coderdtest/swaggerparser.go index 97f1ac92d9d..bff1ec553ab 100644 --- a/coderd/coderdtest/swaggerparser.go +++ b/coderd/coderdtest/swaggerparser.go @@ -408,11 +408,6 @@ func assertAccept(t *testing.T, comment SwaggerComment) { var allowedProduceTypes = []string{"json", "text/event-stream", "text/html", "text/plain"} func assertProduce(t *testing.T, comment SwaggerComment) { - if comment.method == "get" && (comment.router == "/api/v2/chats/files/{file}" || - comment.router == "/api/v2/chats/files/{file}/download") { - return - } - var hasResponseModel bool for _, r := range comment.successes { if r.model != "" { @@ -434,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 6abeaf6b6b7..9a0fadcfd51 100644 --- a/coderd/exp_chats.go +++ b/coderd/exp_chats.go @@ -6925,17 +6925,6 @@ func (api *API) listDefaultOrganizationChatModelConfigs(rw http.ResponseWriter, httpapi.Write(ctx, rw, http.StatusOK, resp) } -// @Summary List AI models and provider descriptors in the default organization -// @ID list-ai-models-and-provider-descriptors-in-the-default-organization -// @Security CoderSessionToken -// @Tags Chats -// @Produce json -// @Success 200 {object} codersdk.OrganizationChatModelsResponse -// @Router /api/v2/chats/models [get] -func (api *API) listDefaultOrganizationChatModels(rw http.ResponseWriter, r *http.Request) { - api.listChatModelConfigsByOrganization(rw, r) -} - // @Summary List AI models and provider descriptors in an organization // @ID list-ai-models-and-provider-descriptors-in-an-organization // @Security CoderSessionToken @@ -7030,7 +7019,7 @@ func chatModelConfigReadScope(scopes database.APIKeyScopes) bool { // them directly, so the fetch runs under a narrow AsChatd context scoped to // exactly these two reads and the result is projected to the fixed redacted // fields (no key material, base URLs, or headers). Disclosure matches what -// /api/v2/chats/models already shows any authenticated caller. +// /api/experimental/chats/models already shows any authenticated caller. func (api *API) chatModelProviderDescriptors( ctx context.Context, userID uuid.UUID, diff --git a/docs/reference/api/chats.md b/docs/reference/api/chats.md index f2fca3e883c..7b331c5db9c 100644 --- a/docs/reference/api/chats.md +++ b/docs/reference/api/chats.md @@ -1274,228 +1274,6 @@ curl -X GET http://coder-server:8080/api/v2/chats/files/{file} \ To perform this operation, you must be authenticated. [Learn more](authentication.md). -## List AI models and provider descriptors in the default organization - -### Code samples - -```sh -# Example request using curl -curl -X GET http://coder-server:8080/api/v2/chats/models \ - -H 'Accept: application/json' \ - -H 'Coder-Session-Token: API_KEY' -``` - -`GET /api/v2/chats/models` - -### Example responses - -> 200 Response - -```json -{ - "models": [ - { - "ai_provider_id": "5a3b8ff9-20e7-4c37-ba1a-5b433e355819", - "compression_threshold": 0, - "context_limit": 0, - "created_at": "2019-08-24T14:15:22Z", - "display_name": "string", - "enabled": true, - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "is_default": true, - "model": "string", - "model_config": { - "frequency_penalty": 0, - "max_output_tokens": 0, - "openai_config": { - "use_responses_api": true - }, - "presence_penalty": 0, - "provider_options": { - "anthropic": { - "allowed_domains": [ - "string" - ], - "blocked_domains": [ - "string" - ], - "context_1m_enabled": true, - "disable_parallel_tool_use": true, - "send_reasoning": true, - "thinking": { - "budget_tokens": 0 - }, - "thinking_display": "string", - "web_search_enabled": true - }, - "google": { - "cached_content": "string", - "safety_settings": [ - { - "category": "string", - "threshold": "string" - } - ], - "thinking_config": { - "include_thoughts": true, - "thinking_budget": 0, - "thinking_level": "string" - }, - "threshold": "string", - "web_search_enabled": true - }, - "openai": { - "allowed_domains": [ - "string" - ], - "include": [ - "string" - ], - "instructions": "string", - "log_probs": true, - "logit_bias": { - "property1": 0, - "property2": 0 - }, - "max_completion_tokens": 0, - "max_tool_calls": 0, - "metadata": { - "property1": null, - "property2": null - }, - "parallel_tool_calls": true, - "prediction": { - "property1": null, - "property2": null - }, - "prompt_cache_key": "string", - "reasoning_summary": "string", - "safety_identifier": "string", - "search_context_size": "string", - "service_tier": "string", - "store": true, - "strict_json_schema": true, - "structured_outputs": true, - "text_verbosity": "string", - "top_log_probs": 0, - "user": "string", - "web_search_enabled": true - }, - "openaicompat": { - "user": "string" - }, - "openrouter": { - "extra_body": { - "property1": null, - "property2": null - }, - "include_usage": true, - "log_probs": true, - "logit_bias": { - "property1": 0, - "property2": 0 - }, - "parallel_tool_calls": true, - "provider": { - "allow_fallbacks": true, - "data_collection": "string", - "ignore": [ - "string" - ], - "only": [ - "string" - ], - "order": [ - "string" - ], - "quantizations": [ - "string" - ], - "require_parameters": true, - "sort": "string" - }, - "reasoning": { - "enabled": true, - "exclude": true, - "max_tokens": 0 - }, - "user": "string" - }, - "vercel": { - "extra_body": { - "property1": null, - "property2": null - }, - "logit_bias": { - "property1": 0, - "property2": 0 - }, - "logprobs": true, - "parallel_tool_calls": true, - "providerOptions": { - "models": [ - "string" - ], - "order": [ - "string" - ] - }, - "reasoning": { - "enabled": true, - "exclude": true, - "max_tokens": 0 - }, - "top_logprobs": 0, - "user": "string" - } - }, - "reasoning_effort": { - "default": "string", - "max": "string" - }, - "temperature": 0, - "top_k": 0, - "top_p": 0 - }, - "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", - "reasoning_efforts": [ - "string" - ], - "updated_at": "2019-08-24T14:15:22Z" - } - ], - "providers": [ - { - "allow_user_api_key": true, - "available": true, - "display_name": "string", - "enabled": true, - "has_api_key": true, - "has_effective_api_key": true, - "has_user_api_key": true, - "icon": "string", - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "type": "string", - "unavailable_reason": "missing_api_key" - } - ], - "unsupported_providers": [ - { - "display_name": "string", - "provider": "string" - } - ] -} -``` - -### Responses - -| Status | Meaning | Description | Schema | -|--------|---------------------------------------------------------|-------------|----------------------------------------------------------------------------------------------| -| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.OrganizationChatModelsResponse](schemas.md#codersdkorganizationchatmodelsresponse) | - -To perform this operation, you must be authenticated. [Learn more](authentication.md). - ## Watch chat events for a user via WebSockets ### Code samples