From 0cf364d188d695ac8095ed531f6c408f25d264b0 Mon Sep 17 00:00:00 2001 From: Mathias Fredriksson Date: Mon, 1 Dec 2025 14:08:49 +0000 Subject: [PATCH 1/2] chore: remove deprecated AITaskPromptParameterName constant This removes the deprecated AITaskPromptParameterName constant and all backward compatibility code that was added in v2.28. Changes: - Remove AITaskPromptParameterName constant from codersdk/aitasks.go - Remove backward compatibility code in coderd/aitasks.go that automatically populated the 'AI Prompt' parameter for templates that defined it - Remove the backward compatibility test (OK AIPromptBackCompat) - Update dbfake to no longer set the AI Prompt parameter - Update toolsdk tests to not require AI Prompt parameter - Remove AITaskPromptParameterName from frontend TypeScript types - Remove preset prompt read-only feature from TaskPrompt component that relied on the AI Prompt parameter - Update test mocks (MockAIPromptPresets) to not include AI Prompt Task prompts are now exclusively stored in the tasks.prompt database column, as introduced in the migration that added the tasks table. Related: coder/internal#1150 --- coderd/aitasks.go | 22 ------- coderd/aitasks_test.go | 58 ++----------------- coderd/database/dbfake/dbfake.go | 6 +- codersdk/aitasks.go | 15 ----- codersdk/toolsdk/toolsdk_test.go | 1 - docs/ai-coder/ai-bridge/client-config.md | 7 ++- docs/ai-coder/tasks-migration.md | 5 +- provisioner/terraform/provision_test.go | 21 ++----- site/src/api/typesGenerated.ts | 16 ----- .../tasks/TaskPrompt/TaskPrompt.stories.tsx | 8 +-- .../modules/tasks/TaskPrompt/TaskPrompt.tsx | 26 +-------- site/src/testHelpers/entities.ts | 3 +- 12 files changed, 22 insertions(+), 166 deletions(-) diff --git a/coderd/aitasks.go b/coderd/aitasks.go index e919b70d3754d..a99653945fd6a 100644 --- a/coderd/aitasks.go +++ b/coderd/aitasks.go @@ -28,7 +28,6 @@ import ( "github.com/coder/coder/v2/coderd/rbac/policy" "github.com/coder/coder/v2/coderd/searchquery" "github.com/coder/coder/v2/coderd/util/ptr" - "github.com/coder/coder/v2/coderd/util/slice" "github.com/coder/coder/v2/codersdk" ) @@ -131,31 +130,10 @@ func (api *API) tasksCreate(rw http.ResponseWriter, r *http.Request) { } } - // Check if the template defines the AI Prompt parameter. - templateParams, err := api.Database.GetTemplateVersionParameters(ctx, req.TemplateVersionID) - if err != nil { - httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ - Message: "Internal error fetching template parameters.", - Detail: err.Error(), - }) - return - } - - var richParams []codersdk.WorkspaceBuildParameter - if _, hasAIPromptParam := slice.Find(templateParams, func(param database.TemplateVersionParameter) bool { - return param.Name == codersdk.AITaskPromptParameterName - }); hasAIPromptParam { - // Only add the AI Prompt parameter if the template defines it. - richParams = []codersdk.WorkspaceBuildParameter{ - {Name: codersdk.AITaskPromptParameterName, Value: req.Input}, - } - } - createReq := codersdk.CreateWorkspaceRequest{ Name: taskName, TemplateVersionID: req.TemplateVersionID, TemplateVersionPresetID: req.TemplateVersionPresetID, - RichParameterValues: richParams, } var owner workspaceOwner diff --git a/coderd/aitasks_test.go b/coderd/aitasks_test.go index 0ab974f17d00d..75c416054fa5d 100644 --- a/coderd/aitasks_test.go +++ b/coderd/aitasks_test.go @@ -57,7 +57,7 @@ func TestTasks(t *testing.T) { o(&opt) } - // Create a template version that supports AI tasks with the AI Prompt parameter. + // Create a template version that supports AI tasks. taskAppID := uuid.New() version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, @@ -137,7 +137,7 @@ func TestTasks(t *testing.T) { got, ok := slice.Find(tasks, func(t codersdk.Task) bool { return t.ID == task.ID }) require.True(t, ok, "task should be found in the list") - assert.Equal(t, wantPrompt, got.InitialPrompt, "task prompt should match the AI Prompt parameter") + assert.Equal(t, wantPrompt, got.InitialPrompt, "task prompt should match the input") assert.Equal(t, task.WorkspaceID.UUID, got.WorkspaceID.UUID, "workspace id should match") assert.Equal(t, task.WorkspaceName, got.WorkspaceName, "workspace name should match") // Status should be populated via the tasks_with_status view. @@ -196,7 +196,7 @@ func TestTasks(t *testing.T) { assert.Equal(t, task.ID, updated.ID, "task ID should match") assert.Equal(t, task.Name, updated.Name, "task name should match") - assert.Equal(t, wantPrompt, updated.InitialPrompt, "task prompt should match the AI Prompt parameter") + assert.Equal(t, wantPrompt, updated.InitialPrompt, "task prompt should match the input") assert.Equal(t, task.WorkspaceID.UUID, updated.WorkspaceID.UUID, "workspace id should match") assert.Equal(t, task.WorkspaceName, updated.WorkspaceName, "workspace name should match") assert.Equal(t, ws.LatestBuild.BuildNumber, updated.WorkspaceBuildNumber, "workspace build number should match") @@ -971,56 +971,6 @@ func TestTasksCreate(t *testing.T) { require.Len(t, parameters, 0) }) - t.Run("OK AIPromptBackCompat", func(t *testing.T) { - t.Parallel() - - var ( - ctx = testutil.Context(t, testutil.WaitShort) - - taskPrompt = "Some task prompt" - ) - - client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true}) - user := coderdtest.CreateFirstUser(t, client) - - // Given: A template with an "AI Prompt" parameter - version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ - Parse: echo.ParseComplete, - ProvisionApply: echo.ApplyComplete, - ProvisionGraph: []*proto.Response{ - {Type: &proto.Response_Graph{Graph: &proto.GraphComplete{ - Parameters: []*proto.RichParameter{{Name: codersdk.AITaskPromptParameterName, Type: "string"}}, - HasAiTasks: true, - }}}, - }, - }) - coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) - template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) - - // When: We attempt to create a Task. - task, err := client.CreateTask(ctx, "me", codersdk.CreateTaskRequest{ - TemplateVersionID: template.ActiveVersionID, - Input: taskPrompt, - }) - require.NoError(t, err) - require.True(t, task.WorkspaceID.Valid) - - ws, err := client.Workspace(ctx, task.WorkspaceID.UUID) - require.NoError(t, err) - coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, ws.LatestBuild.ID) - - // Then: We expect a workspace to have been created. - assert.NotEmpty(t, task.Name) - assert.Equal(t, template.ID, task.TemplateID) - - // And: We expect it to have the "AI Prompt" parameter correctly set. - parameters, err := client.WorkspaceBuildParameters(ctx, ws.LatestBuild.ID) - require.NoError(t, err) - require.Len(t, parameters, 1) - assert.Equal(t, codersdk.AITaskPromptParameterName, parameters[0].Name) - assert.Equal(t, taskPrompt, parameters[0].Value) - }) - t.Run("CustomNames", func(t *testing.T) { t.Parallel() @@ -1147,7 +1097,7 @@ func TestTasksCreate(t *testing.T) { client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true}) user := coderdtest.CreateFirstUser(t, client) - // Given: A template without an "AI Prompt" parameter + // Given: A template without AI task support (no coder_ai_task resource) version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil) coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) diff --git a/coderd/database/dbfake/dbfake.go b/coderd/database/dbfake/dbfake.go index 97558b4b8b928..6161299e00df8 100644 --- a/coderd/database/dbfake/dbfake.go +++ b/coderd/database/dbfake/dbfake.go @@ -24,7 +24,6 @@ import ( "github.com/coder/coder/v2/coderd/rbac" "github.com/coder/coder/v2/coderd/telemetry" "github.com/coder/coder/v2/coderd/wspubsub" - "github.com/coder/coder/v2/codersdk" "github.com/coder/coder/v2/provisionersdk" sdkproto "github.com/coder/coder/v2/provisionersdk/proto" ) @@ -130,10 +129,7 @@ func (b WorkspaceBuildBuilder) WithTask(taskSeed database.TaskTable, appSeed *sd b.taskAppID, err = uuid.Parse(takeFirst(appSeed.Id, uuid.NewString())) require.NoError(b.t, err) - return b.Params(database.WorkspaceBuildParameter{ - Name: codersdk.AITaskPromptParameterName, - Value: b.taskSeed.Prompt, - }).WithAgent(func(a []*sdkproto.Agent) []*sdkproto.Agent { + return b.WithAgent(func(a []*sdkproto.Agent) []*sdkproto.Agent { a[0].Apps = []*sdkproto.App{ { Id: b.taskAppID.String(), diff --git a/codersdk/aitasks.go b/codersdk/aitasks.go index e2acbfe4897c3..c4a0cb61419dd 100644 --- a/codersdk/aitasks.go +++ b/codersdk/aitasks.go @@ -10,23 +10,8 @@ import ( "github.com/google/uuid" "golang.org/x/xerrors" - - "github.com/coder/terraform-provider-coder/v2/provider" ) -// AITaskPromptParameterName is the name of the parameter used to pass prompts -// to AI tasks. -// -// Deprecated: This constant is deprecated and maintained only for backwards -// compatibility with older templates. Task prompts are now stored directly -// in the tasks.prompt database column. New code should access prompts via -// the Task.InitialPrompt field returned from task endpoints. -// -// This constant will be removed in a future major version. Templates should -// not rely on this parameter name, as the backend will continue to create it -// automatically for compatibility but reads from tasks.prompt. -const AITaskPromptParameterName = provider.TaskPromptParameterName - // CreateTaskRequest represents the request to create a new task. type CreateTaskRequest struct { TemplateVersionID uuid.UUID `json:"template_version_id" format:"uuid"` diff --git a/codersdk/toolsdk/toolsdk_test.go b/codersdk/toolsdk/toolsdk_test.go index 0a8a94c8dc9c7..b075cbd73a701 100644 --- a/codersdk/toolsdk/toolsdk_test.go +++ b/codersdk/toolsdk/toolsdk_test.go @@ -1017,7 +1017,6 @@ func TestTools(t *testing.T) { ProvisionApply: echo.ApplyComplete, ProvisionGraph: []*proto.Response{ {Type: &proto.Response_Graph{Graph: &proto.GraphComplete{ - Parameters: []*proto.RichParameter{{Name: "AI Prompt", Type: "string"}}, HasAiTasks: true, }}}, }, diff --git a/docs/ai-coder/ai-bridge/client-config.md b/docs/ai-coder/ai-bridge/client-config.md index 7f63ad22973f4..2f3d57fbb44b8 100644 --- a/docs/ai-coder/ai-bridge/client-config.md +++ b/docs/ai-coder/ai-bridge/client-config.md @@ -58,6 +58,8 @@ data "coder_workspace_owner" "me" {} data "coder_workspace" "me" {} +data "coder_task" "me" {} + resource "coder_agent" "dev" { arch = "amd64" os = "linux" @@ -71,13 +73,12 @@ resource "coder_agent" "dev" { # See https://registry.coder.com/modules/coder/claude-code for more information module "claude-code" { - count = local.has_ai_prompt ? data.coder_workspace.me.start_count : 0 source = "dev.registry.coder.com/coder/claude-code/coder" - version = ">= 3.4.0" + version = ">= 4.0.0" agent_id = coder_agent.dev.id workdir = "/home/coder/project" claude_api_key = data.coder_workspace_owner.me.session_token # Use the Coder session token to authenticate with AI Bridge - ai_prompt = data.coder_parameter.ai_prompt.value + ai_prompt = data.coder_task.me.prompt ... # other claude-code configuration } ``` diff --git a/docs/ai-coder/tasks-migration.md b/docs/ai-coder/tasks-migration.md index 6cd02ba2e7ba2..1bc41ff530115 100644 --- a/docs/ai-coder/tasks-migration.md +++ b/docs/ai-coder/tasks-migration.md @@ -8,7 +8,8 @@ Prior to Coder version 2.28.0, the definition of a Coder task was different to t Note that 2 and 3 were generally handled by the `coder/agentapi` Terraform module. -The pre-2.28.0 definition will be supported until the release of 2.29.0. You will need to update your Tasks-enabled templates to continue using Tasks after this release. +> [!IMPORTANT] +> The pre-2.28.0 definition is no longer supported as of Coder 2.30.0. You must update your Tasks-enabled templates to use the new format described below. You can view an [example migration here](https://github.com/coder/coder/pull/20420). Alternatively, follow the steps below: @@ -114,7 +115,7 @@ resource "coder_ai_task" "task" { In v2.28 and above, the following changes were made: -- The explicitly named "AI Prompt" parameter is deprecated. The task prompt is now available in the `coder_ai_task` resource (provider version 2.12 and above) and `coder_task` data source (provider version 2.13 and above). +- The explicitly named "AI Prompt" parameter is no longer supported. The task prompt is now available in the `coder_ai_task` resource (provider version 2.12 and above) and `coder_task` data source (provider version 2.13 and above). - Modules no longer define the `coder_ai_task` resource. These must be defined explicitly in the template. - The `sidebar_app` field of the `coder_ai_task` resource is now deprecated. In its place, use `app_id`. diff --git a/provisioner/terraform/provision_test.go b/provisioner/terraform/provision_test.go index ee71838d989d3..ccd8986d0fc9a 100644 --- a/provisioner/terraform/provision_test.go +++ b/provisioner/terraform/provision_test.go @@ -20,8 +20,6 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/coder/terraform-provider-coder/v2/provider" - "cdr.dev/slog" "cdr.dev/slog/sloggers/slogtest" @@ -941,18 +939,15 @@ func TestProvision(t *testing.T) { { Name: "ai-task-multiple-allowed-in-plan", Files: map[string]string{ - "main.tf": fmt.Sprintf(`terraform { + "main.tf": `terraform { required_providers { coder = { source = "coder/coder" - version = ">= 2.7.0" + version = ">= 2.13.0" } } } - data "coder_parameter" "prompt" { - name = "%s" - type = "string" - } + data "coder_task" "me" {} resource "coder_ai_task" "a" { sidebar_app { id = "7128be08-8722-44cb-bbe1-b5a391c4d94b" # fake ID, irrelevant here anyway but needed for validation @@ -963,7 +958,7 @@ func TestProvision(t *testing.T) { id = "7128be08-8722-44cb-bbe1-b5a391c4d94b" # fake ID, irrelevant here anyway but needed for validation } } - `, provider.TaskPromptParameterName), + `, }, Request: &proto.PlanRequest{}, Response: &proto.GraphComplete{ @@ -977,14 +972,6 @@ func TestProvision(t *testing.T) { Type: "coder_ai_task", }, }, - Parameters: []*proto.RichParameter{ - { - Name: provider.TaskPromptParameterName, - Type: "string", - Required: true, - FormType: proto.ParameterFormType_INPUT, - }, - }, AiTasks: []*proto.AITask{ { Id: "a", diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 47da5bb36dc41..93dee1e18f4b9 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -108,22 +108,6 @@ export interface AIConfig { readonly bridge?: AIBridgeConfig; } -// From codersdk/aitasks.go -/** - * AITaskPromptParameterName is the name of the parameter used to pass prompts - * to AI tasks. - * - * Deprecated: This constant is deprecated and maintained only for backwards - * compatibility with older templates. Task prompts are now stored directly - * in the tasks.prompt database column. New code should access prompts via - * the Task.InitialPrompt field returned from task endpoints. - * - * This constant will be removed in a future major version. Templates should - * not rely on this parameter name, as the backend will continue to create it - * automatically for compatibility but reads from tasks.prompt. - */ -export const AITaskPromptParameterName = "AI Prompt"; - // From codersdk/allowlist.go /** * APIAllowListTarget represents a single allow-list entry using the canonical diff --git a/site/src/modules/tasks/TaskPrompt/TaskPrompt.stories.tsx b/site/src/modules/tasks/TaskPrompt/TaskPrompt.stories.tsx index bd4a765b9c0be..d7f90857482c8 100644 --- a/site/src/modules/tasks/TaskPrompt/TaskPrompt.stories.tsx +++ b/site/src/modules/tasks/TaskPrompt/TaskPrompt.stories.tsx @@ -1,7 +1,7 @@ import { - MockAIPromptPresets, MockPresets, MockTask, + MockTaskPresets, MockTasks, MockTemplate, MockTemplateVersion, @@ -72,11 +72,9 @@ export const WithPresets: Story = { }, }; -export const ReadOnlyPresetPrompt: Story = { +export const WithAIPresets: Story = { beforeEach: () => { - spyOn(API, "getTemplateVersionPresets").mockResolvedValue( - MockAIPromptPresets, - ); + spyOn(API, "getTemplateVersionPresets").mockResolvedValue(MockTaskPresets); }, }; diff --git a/site/src/modules/tasks/TaskPrompt/TaskPrompt.tsx b/site/src/modules/tasks/TaskPrompt/TaskPrompt.tsx index 97eda8895529a..7580963eeff3f 100644 --- a/site/src/modules/tasks/TaskPrompt/TaskPrompt.tsx +++ b/site/src/modules/tasks/TaskPrompt/TaskPrompt.tsx @@ -7,7 +7,6 @@ import type { Template, TemplateVersionExternalAuth, } from "api/typesGenerated"; -import { AITaskPromptParameterName } from "api/typesGenerated"; import { ErrorAlert } from "components/Alert/ErrorAlert"; import { Button } from "components/Button/Button"; import { ExternalImage } from "components/ExternalImage/ExternalImage"; @@ -162,19 +161,6 @@ const CreateTaskForm: FC = ({ templates, onSuccess }) => { const defaultPreset = presets?.find((p) => p.Default); setSelectedPresetId(defaultPreset?.ID ?? presets?.[0]?.ID); }, [presets]); - const selectedPreset = presets?.find((p) => p.ID === selectedPresetId); - - // Read-only prompt if defined in preset - const presetPrompt = selectedPreset?.Parameters?.find( - (param) => param.Name === AITaskPromptParameterName, - )?.Value; - const isPromptReadOnly = !!presetPrompt; - useEffect(() => { - if (presetPrompt) { - setPrompt(presetPrompt); - } - }, [presetPrompt]); - // External Auth const { externalAuth, @@ -250,21 +236,13 @@ const CreateTaskForm: FC = ({ templates, onSuccess }) => { className="border border-border border-solid rounded-3xl p-3 bg-surface-secondary" disabled={createTaskMutation.isPending} > -