refactor: remove deprecated AITaskPromptParameterName constant (#21023)

This removes the deprecated AITaskPromptParameterName constant and all
backward compatibility code that was added for v2.28.

- Remove AITaskPromptParameterName constant from codersdk/aitasks.go
- Remove backward compatibility code in coderd/aitasks.go that 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
- Remove AITaskPromptParameterName from frontend TypeScript types
- Remove preset prompt read-only feature from TaskPrompt component
- Update docs to reflect that pre-2.28 definition is no longer supported

Task prompts are now exclusively stored in the tasks.prompt database
column, as introduced in the migration that added the tasks table.
This commit is contained in:
Mathias Fredriksson
2025-12-16 15:14:59 +00:00
committed by GitHub
parent 7a5c5581e9
commit dac822b7f4
13 changed files with 37 additions and 174 deletions
-22
View File
@@ -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
+4 -54
View File
@@ -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)
+1 -5
View File
@@ -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(),
-15
View File
@@ -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"`
-1
View File
@@ -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,
}}},
},
+11 -3
View File
@@ -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,15 +73,21 @@ 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
count = data.coder_task.me.enabled ? 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
}
# The coder_ai_task resource associates the task to the app.
resource "coder_ai_task" "task" {
count = data.coder_task.me.enabled ? data.coder_workspace.me.start_count : 0
app_id = module.claude-code[0].task_app_id
}
```
## External and Desktop Clients
+3 -2
View File
@@ -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`.
+8 -8
View File
@@ -268,16 +268,16 @@ Coder maintains the [Tasks on Docker](https://registry.coder.com/templates/coder
coder template push tasks-docker -d . --variable anthropic_api_key="your-api-key"
```
1. **Create the new Workspace**
1. In your Coder Deployment, click **Workspaces** in the upper left hand corner
1. Click **New workspace** and choose **tasks-docker**
1. Fill in the Workspace name. Add in an AI Prompt for Claude Code like "Make the background yellow". Click **Create workspace**
1. **Create a Task**
1. In your Coder deployment, click **Tasks** in the navigation
1. In the "Prompt your AI agent to start a task" box, enter a prompt like "Make the background yellow"
1. Select the **tasks-docker** template from the dropdown and click the submit button
1. **See Tasks in action**
1. Once your workspace is running, click **View tasks** with your workspace. This will bring you to the Tasks view where you can see Claude Code (left panel), preview the sample application, and interact with the code in code-server. You might need to wait for Claude Code to finish changing the background color of the application.
1. Navigate to the **Tasks** tab in the upper left hand corner
1. Your task will appear in the table below. Click on it to open the task view where you can follow the initialization
1. Once active, you'll see Claude Code on the left panel and can preview the sample application or interact with the code in code-server on the right. You might need to wait for Claude Code to finish changing the background color of the application.
1. Try typing in a new request to Claude Code: "make the background red"
1. Let's exit out of this specific Task view, so we can see all the running tasks
1. You can start a new task by prompting in the "Prompt your AI agent to start a task" box. You can select which template to run this from, so tasks-docker here, and that will spin up a new Workspace
1. Click the back arrow to return to the task overview (you can also see all your tasks in the sidebar)
1. You can start a new task from the prompt box at the top of the page
![Tasks changing background color of demo application](../images/screenshots/quickstart-tasks-background-change.png)
+4 -17
View File
@@ -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",
-16
View File
@@ -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
@@ -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);
},
};
@@ -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<CreateTaskFormProps> = ({ 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<CreateTaskFormProps> = ({ templates, onSuccess }) => {
className="border border-border border-solid rounded-3xl p-3 bg-surface-secondary"
disabled={createTaskMutation.isPending}
>
<label
htmlFor="prompt"
className={
isPromptReadOnly
? "text-xs font-medium text-content-primary block px-3 pt-2"
: "sr-only"
}
>
{isPromptReadOnly ? "Prompt defined by preset" : "Prompt"}
<label htmlFor="prompt" className="sr-only">
Prompt
</label>
<PromptTextarea
required
value={prompt}
onChange={(e) => setPrompt(e.target.value)}
readOnly={isPromptReadOnly}
isSubmitting={createTaskMutation.isPending}
onKeyDown={handleKeyDown}
/>
+1 -2
View File
@@ -4977,14 +4977,13 @@ export const MockPresets: TypesGen.Preset[] = [
},
];
export const MockAIPromptPresets: TypesGen.Preset[] = [
export const MockTaskPresets: TypesGen.Preset[] = [
{
ID: "ai-preset-1",
Name: "Code Review",
Description: "",
Icon: "",
Parameters: [
{ Name: "AI Prompt", Value: "Review the code for best practices" },
{ Name: "cpu", Value: "4" },
{ Name: "memory", Value: "8GB" },
],