From 3c199affa95c127c819aef3e6087fac1d3d5f056 Mon Sep 17 00:00:00 2001 From: Danielle Maywood Date: Tue, 25 Nov 2025 13:33:58 +0000 Subject: [PATCH 1/3] chore: promote tasks from experimental --- cli/task_create.go | 5 +- cli/task_create_test.go | 2 +- cli/task_delete.go | 5 +- cli/task_delete_test.go | 22 +- cli/task_list.go | 3 +- cli/task_logs.go | 5 +- cli/task_send.go | 5 +- cli/task_status.go | 5 +- cli/task_status_test.go | 10 +- cli/task_test.go | 6 +- coderd/aitasks.go | 78 +-- coderd/aitasks_test.go | 147 ++---- coderd/apidoc/docs.go | 555 ++++++++++---------- coderd/apidoc/swagger.json | 513 +++++++++--------- coderd/autobuild/lifecycle_executor_test.go | 3 +- coderd/coderd.go | 22 + coderd/workspaces_test.go | 3 +- codersdk/aitasks.go | 34 +- codersdk/toolsdk/toolsdk.go | 28 +- codersdk/toolsdk/toolsdk_test.go | 7 +- docs/reference/api/experimental.md | 211 +++++++- site/src/api/api.ts | 8 +- 22 files changed, 900 insertions(+), 777 deletions(-) diff --git a/cli/task_create.go b/cli/task_create.go index a0677d5ef0f97..9f300b6336d53 100644 --- a/cli/task_create.go +++ b/cli/task_create.go @@ -111,8 +111,7 @@ func (r *RootCmd) taskCreate() *serpent.Command { } var ( - ctx = inv.Context() - expClient = codersdk.NewExperimentalClient(client) + ctx = inv.Context() taskInput string templateVersionID uuid.UUID @@ -208,7 +207,7 @@ func (r *RootCmd) taskCreate() *serpent.Command { templateVersionPresetID = preset.ID } - task, err := expClient.CreateTask(ctx, ownerArg, codersdk.CreateTaskRequest{ + task, err := client.CreateTask(ctx, ownerArg, codersdk.CreateTaskRequest{ Name: taskName, TemplateVersionID: templateVersionID, TemplateVersionPresetID: templateVersionPresetID, diff --git a/cli/task_create_test.go b/cli/task_create_test.go index 81e05936d1b0c..d5b4098a47e2f 100644 --- a/cli/task_create_test.go +++ b/cli/task_create_test.go @@ -69,7 +69,7 @@ func TestTaskCreate(t *testing.T) { ActiveVersionID: templateVersionID, }, }) - case fmt.Sprintf("/api/experimental/tasks/%s", username): + case fmt.Sprintf("/api/v2/tasks/%s", username): var req codersdk.CreateTaskRequest if !httpapi.Read(ctx, w, r, &req) { return diff --git a/cli/task_delete.go b/cli/task_delete.go index 20a7c127b725f..ac41b0192f8e7 100644 --- a/cli/task_delete.go +++ b/cli/task_delete.go @@ -44,11 +44,10 @@ func (r *RootCmd) taskDelete() *serpent.Command { if err != nil { return err } - exp := codersdk.NewExperimentalClient(client) var tasks []codersdk.Task for _, identifier := range inv.Args { - task, err := exp.TaskByIdentifier(ctx, identifier) + task, err := client.TaskByIdentifier(ctx, identifier) if err != nil { return xerrors.Errorf("resolve task %q: %w", identifier, err) } @@ -71,7 +70,7 @@ func (r *RootCmd) taskDelete() *serpent.Command { for i, task := range tasks { display := displayList[i] - if err := exp.DeleteTask(ctx, task.OwnerName, task.ID); err != nil { + if err := client.DeleteTask(ctx, task.OwnerName, task.ID); err != nil { return xerrors.Errorf("delete task %q: %w", display, err) } _, _ = fmt.Fprintln( diff --git a/cli/task_delete_test.go b/cli/task_delete_test.go index fc686dc9e8f66..2d28845c73d3d 100644 --- a/cli/task_delete_test.go +++ b/cli/task_delete_test.go @@ -56,7 +56,7 @@ func TestExpTaskDelete(t *testing.T) { taskID := uuid.MustParse(id1) return func(w http.ResponseWriter, r *http.Request) { switch { - case r.Method == http.MethodGet && r.URL.Path == "/api/experimental/tasks/me/exists": + case r.Method == http.MethodGet && r.URL.Path == "/api/v2/tasks/me/exists": c.nameResolves.Add(1) httpapi.Write(r.Context(), w, http.StatusOK, codersdk.Task{ @@ -64,7 +64,7 @@ func TestExpTaskDelete(t *testing.T) { Name: "exists", OwnerName: "me", }) - case r.Method == http.MethodDelete && r.URL.Path == "/api/experimental/tasks/me/"+id1: + case r.Method == http.MethodDelete && r.URL.Path == "/api/v2/tasks/me/"+id1: c.deleteCalls.Add(1) w.WriteHeader(http.StatusAccepted) default: @@ -82,13 +82,13 @@ func TestExpTaskDelete(t *testing.T) { buildHandler: func(c *testCounters) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { switch { - case r.Method == http.MethodGet && r.URL.Path == "/api/experimental/tasks/me/"+id2: + case r.Method == http.MethodGet && r.URL.Path == "/api/v2/tasks/me/"+id2: httpapi.Write(r.Context(), w, http.StatusOK, codersdk.Task{ ID: uuid.MustParse(id2), OwnerName: "me", Name: "uuid-task", }) - case r.Method == http.MethodDelete && r.URL.Path == "/api/experimental/tasks/me/"+id2: + case r.Method == http.MethodDelete && r.URL.Path == "/api/v2/tasks/me/"+id2: c.deleteCalls.Add(1) w.WriteHeader(http.StatusAccepted) default: @@ -104,24 +104,24 @@ func TestExpTaskDelete(t *testing.T) { buildHandler: func(c *testCounters) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { switch { - case r.Method == http.MethodGet && r.URL.Path == "/api/experimental/tasks/me/first": + case r.Method == http.MethodGet && r.URL.Path == "/api/v2/tasks/me/first": c.nameResolves.Add(1) httpapi.Write(r.Context(), w, http.StatusOK, codersdk.Task{ ID: uuid.MustParse(id3), Name: "first", OwnerName: "me", }) - case r.Method == http.MethodGet && r.URL.Path == "/api/experimental/tasks/me/"+id4: + case r.Method == http.MethodGet && r.URL.Path == "/api/v2/tasks/me/"+id4: c.nameResolves.Add(1) httpapi.Write(r.Context(), w, http.StatusOK, codersdk.Task{ ID: uuid.MustParse(id4), OwnerName: "me", Name: "uuid-task-4", }) - case r.Method == http.MethodDelete && r.URL.Path == "/api/experimental/tasks/me/"+id3: + case r.Method == http.MethodDelete && r.URL.Path == "/api/v2/tasks/me/"+id3: c.deleteCalls.Add(1) w.WriteHeader(http.StatusAccepted) - case r.Method == http.MethodDelete && r.URL.Path == "/api/experimental/tasks/me/"+id4: + case r.Method == http.MethodDelete && r.URL.Path == "/api/v2/tasks/me/"+id4: c.deleteCalls.Add(1) w.WriteHeader(http.StatusAccepted) default: @@ -140,7 +140,7 @@ func TestExpTaskDelete(t *testing.T) { buildHandler: func(_ *testCounters) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { switch { - case r.Method == http.MethodGet && r.URL.Path == "/api/experimental/tasks" && r.URL.Query().Get("q") == "owner:\"me\"": + case r.Method == http.MethodGet && r.URL.Path == "/api/v2/tasks" && r.URL.Query().Get("q") == "owner:\"me\"": httpapi.Write(r.Context(), w, http.StatusOK, struct { Tasks []codersdk.Task `json:"tasks"` Count int `json:"count"` @@ -163,14 +163,14 @@ func TestExpTaskDelete(t *testing.T) { taskID := uuid.MustParse(id5) return func(w http.ResponseWriter, r *http.Request) { switch { - case r.Method == http.MethodGet && r.URL.Path == "/api/experimental/tasks/me/bad": + case r.Method == http.MethodGet && r.URL.Path == "/api/v2/tasks/me/bad": c.nameResolves.Add(1) httpapi.Write(r.Context(), w, http.StatusOK, codersdk.Task{ ID: taskID, Name: "bad", OwnerName: "me", }) - case r.Method == http.MethodDelete && r.URL.Path == "/api/experimental/tasks/me/bad": + case r.Method == http.MethodDelete && r.URL.Path == "/api/v2/tasks/me/bad": httpapi.InternalServerError(w, xerrors.New("boom")) default: httpapi.InternalServerError(w, xerrors.New("unwanted path: "+r.Method+" "+r.URL.Path)) diff --git a/cli/task_list.go b/cli/task_list.go index 8458e66252f61..1f13c85a05920 100644 --- a/cli/task_list.go +++ b/cli/task_list.go @@ -135,14 +135,13 @@ func (r *RootCmd) taskList() *serpent.Command { } ctx := inv.Context() - exp := codersdk.NewExperimentalClient(client) targetUser := strings.TrimSpace(user) if targetUser == "" && !all { targetUser = codersdk.Me } - tasks, err := exp.Tasks(ctx, &codersdk.TasksFilter{ + tasks, err := client.Tasks(ctx, &codersdk.TasksFilter{ Owner: targetUser, Status: codersdk.TaskStatus(statusFilter), }) diff --git a/cli/task_logs.go b/cli/task_logs.go index 0b2fd520af92f..87e2e8112fda1 100644 --- a/cli/task_logs.go +++ b/cli/task_logs.go @@ -41,16 +41,15 @@ func (r *RootCmd) taskLogs() *serpent.Command { var ( ctx = inv.Context() - exp = codersdk.NewExperimentalClient(client) identifier = inv.Args[0] ) - task, err := exp.TaskByIdentifier(ctx, identifier) + task, err := client.TaskByIdentifier(ctx, identifier) if err != nil { return xerrors.Errorf("resolve task %q: %w", identifier, err) } - logs, err := exp.TaskLogs(ctx, codersdk.Me, task.ID) + logs, err := client.TaskLogs(ctx, codersdk.Me, task.ID) if err != nil { return xerrors.Errorf("get task logs: %w", err) } diff --git a/cli/task_send.go b/cli/task_send.go index 7859fa1ddce25..97f1555a838a5 100644 --- a/cli/task_send.go +++ b/cli/task_send.go @@ -39,7 +39,6 @@ func (r *RootCmd) taskSend() *serpent.Command { var ( ctx = inv.Context() - exp = codersdk.NewExperimentalClient(client) identifier = inv.Args[0] taskInput string @@ -60,12 +59,12 @@ func (r *RootCmd) taskSend() *serpent.Command { taskInput = inv.Args[1] } - task, err := exp.TaskByIdentifier(ctx, identifier) + task, err := client.TaskByIdentifier(ctx, identifier) if err != nil { return xerrors.Errorf("resolve task: %w", err) } - if err = exp.TaskSend(ctx, codersdk.Me, task.ID, codersdk.TaskSendRequest{Input: taskInput}); err != nil { + if err = client.TaskSend(ctx, codersdk.Me, task.ID, codersdk.TaskSendRequest{Input: taskInput}); err != nil { return xerrors.Errorf("send input to task: %w", err) } diff --git a/cli/task_status.go b/cli/task_status.go index 9d97485c51ddd..7c91cd55e9637 100644 --- a/cli/task_status.go +++ b/cli/task_status.go @@ -83,10 +83,9 @@ func (r *RootCmd) taskStatus() *serpent.Command { } ctx := i.Context() - exp := codersdk.NewExperimentalClient(client) identifier := i.Args[0] - task, err := exp.TaskByIdentifier(ctx, identifier) + task, err := client.TaskByIdentifier(ctx, identifier) if err != nil { return err } @@ -107,7 +106,7 @@ func (r *RootCmd) taskStatus() *serpent.Command { // TODO: implement streaming updates instead of polling lastStatusRow := tsr for range t.C { - task, err := exp.TaskByID(ctx, task.ID) + task, err := client.TaskByID(ctx, task.ID) if err != nil { return err } diff --git a/cli/task_status_test.go b/cli/task_status_test.go index b19045fa2d47c..0c0d7facaf72b 100644 --- a/cli/task_status_test.go +++ b/cli/task_status_test.go @@ -36,7 +36,7 @@ func Test_TaskStatus(t *testing.T) { hf: func(ctx context.Context, _ time.Time) func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { switch r.URL.Path { - case "/api/experimental/tasks/me/doesnotexist": + case "/api/v2/tasks/me/doesnotexist": httpapi.ResourceNotFound(w) return default: @@ -52,7 +52,7 @@ func Test_TaskStatus(t *testing.T) { hf: func(ctx context.Context, now time.Time) func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { switch r.URL.Path { - case "/api/experimental/tasks/me/exists": + case "/api/v2/tasks/me/exists": httpapi.Write(ctx, w, http.StatusOK, codersdk.Task{ ID: uuid.MustParse("11111111-1111-1111-1111-111111111111"), WorkspaceStatus: codersdk.WorkspaceStatusRunning, @@ -88,7 +88,7 @@ func Test_TaskStatus(t *testing.T) { var calls atomic.Int64 return func(w http.ResponseWriter, r *http.Request) { switch r.URL.Path { - case "/api/experimental/tasks/me/exists": + case "/api/v2/tasks/me/exists": httpapi.Write(ctx, w, http.StatusOK, codersdk.Task{ ID: uuid.MustParse("11111111-1111-1111-1111-111111111111"), Name: "exists", @@ -103,7 +103,7 @@ func Test_TaskStatus(t *testing.T) { Status: codersdk.TaskStatusPending, }) return - case "/api/experimental/tasks/me/11111111-1111-1111-1111-111111111111": + case "/api/v2/tasks/me/11111111-1111-1111-1111-111111111111": defer calls.Add(1) switch calls.Load() { case 0: @@ -219,7 +219,7 @@ func Test_TaskStatus(t *testing.T) { ts := time.Date(2025, 8, 26, 12, 34, 56, 0, time.UTC) return func(w http.ResponseWriter, r *http.Request) { switch r.URL.Path { - case "/api/experimental/tasks/me/exists": + case "/api/v2/tasks/me/exists": httpapi.Write(ctx, w, http.StatusOK, codersdk.Task{ ID: uuid.MustParse("11111111-1111-1111-1111-111111111111"), Name: "exists", diff --git a/cli/task_test.go b/cli/task_test.go index 84097f01e74d3..fca04372600d8 100644 --- a/cli/task_test.go +++ b/cli/task_test.go @@ -122,8 +122,7 @@ func Test_Tasks(t *testing.T) { assertFn: func(stdout string, userClient *codersdk.Client) { // The task should eventually no longer show up in the list of tasks testutil.Eventually(ctx, t, func(ctx context.Context) bool { - expClient := codersdk.NewExperimentalClient(userClient) - tasks, err := expClient.Tasks(ctx, &codersdk.TasksFilter{}) + tasks, err := userClient.Tasks(ctx, &codersdk.TasksFilter{}) if !assert.NoError(t, err) { return false } @@ -248,8 +247,7 @@ func setupCLITaskTest(ctx context.Context, t *testing.T, agentAPIHandlers map[st template := createAITaskTemplate(t, client, owner.OrganizationID, withSidebarURL(fakeAPI.URL()), withAgentToken(authToken)) wantPrompt := "test prompt" - exp := codersdk.NewExperimentalClient(userClient) - task, err := exp.CreateTask(ctx, codersdk.Me, codersdk.CreateTaskRequest{ + task, err := userClient.CreateTask(ctx, codersdk.Me, codersdk.CreateTaskRequest{ TemplateVersionID: template.ActiveVersionID, Input: wantPrompt, Name: "test-task", diff --git a/coderd/aitasks.go b/coderd/aitasks.go index 761d4817c21a1..39b543c0f1632 100644 --- a/coderd/aitasks.go +++ b/coderd/aitasks.go @@ -31,17 +31,15 @@ import ( ) // @Summary Create a new AI task -// @Description: EXPERIMENTAL: this endpoint is experimental and not guaranteed to be stable. -// @ID create-task +// @ID create-a-new-ai-task // @Security CoderSessionToken +// @Accept json +// @Produce json // @Tags Experimental // @Param user path string true "Username, user ID, or 'me' for the authenticated user" // @Param request body codersdk.CreateTaskRequest true "Create task request" // @Success 201 {object} codersdk.Task -// @Router /api/experimental/tasks/{user} [post] -// -// EXPERIMENTAL: This endpoint is experimental and not guaranteed to be stable. -// This endpoint creates a new task for the given user. +// @Router /tasks/{user} [post] func (api *API) tasksCreate(rw http.ResponseWriter, r *http.Request) { var ( ctx = r.Context() @@ -400,16 +398,13 @@ func deriveTaskCurrentState( } // @Summary List AI tasks -// @Description: EXPERIMENTAL: this endpoint is experimental and not guaranteed to be stable. -// @ID list-tasks +// @ID list-ai-tasks // @Security CoderSessionToken +// @Produce json // @Tags Experimental // @Param q query string false "Search query for filtering tasks. Supports: owner:, organization:, status:" // @Success 200 {object} codersdk.TasksListResponse -// @Router /api/experimental/tasks [get] -// -// EXPERIMENTAL: This endpoint is experimental and not guaranteed to be stable. -// tasksList is an experimental endpoint to list tasks. +// @Router /tasks [get] func (api *API) tasksList(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() apiKey := httpmw.APIKey(r) @@ -502,20 +497,15 @@ func (api *API) convertTasks(ctx context.Context, requesterID uuid.UUID, dbTasks return result, nil } -// @Summary Get AI task by ID -// @Description: EXPERIMENTAL: this endpoint is experimental and not guaranteed to be stable. -// @ID get-task +// @Summary Get AI task by ID or name +// @ID get-ai-task-by-id-or-name // @Security CoderSessionToken +// @Produce json // @Tags Experimental // @Param user path string true "Username, user ID, or 'me' for the authenticated user" // @Param task path string true "Task ID, or task name" // @Success 200 {object} codersdk.Task -// @Router /api/experimental/tasks/{user}/{task} [get] -// -// EXPERIMENTAL: This endpoint is experimental and not guaranteed to be stable. -// taskGet is an experimental endpoint to fetch a single AI task by ID -// (workspace ID). It returns a synthesized task response including -// prompt and status. +// @Router /tasks/{user}/{task} [get] func (api *API) taskGet(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() apiKey := httpmw.APIKey(r) @@ -580,20 +570,14 @@ func (api *API) taskGet(rw http.ResponseWriter, r *http.Request) { httpapi.Write(ctx, rw, http.StatusOK, taskResp) } -// @Summary Delete AI task by ID -// @Description: EXPERIMENTAL: this endpoint is experimental and not guaranteed to be stable. -// @ID delete-task +// @Summary Delete AI task +// @ID delete-ai-task // @Security CoderSessionToken // @Tags Experimental // @Param user path string true "Username, user ID, or 'me' for the authenticated user" // @Param task path string true "Task ID, or task name" -// @Success 202 "Task deletion initiated" -// @Router /api/experimental/tasks/{user}/{task} [delete] -// -// EXPERIMENTAL: This endpoint is experimental and not guaranteed to be stable. -// taskDelete is an experimental endpoint to delete a task by ID. -// It creates a delete workspace build and returns 202 Accepted if the build was -// created. +// @Success 202 +// @Router /tasks/{user}/{task} [delete] func (api *API) taskDelete(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() apiKey := httpmw.APIKey(r) @@ -655,18 +639,15 @@ func (api *API) taskDelete(rw http.ResponseWriter, r *http.Request) { } // @Summary Update AI task input -// @Description: EXPERIMENTAL: this endpoint is experimental and not guaranteed to be stable. -// @ID update-task-input +// @ID update-ai-task-input // @Security CoderSessionToken +// @Accept json // @Tags Experimental // @Param user path string true "Username, user ID, or 'me' for the authenticated user" // @Param task path string true "Task ID, or task name" // @Param request body codersdk.UpdateTaskInputRequest true "Update task input request" // @Success 204 -// @Router /api/experimental/tasks/{user}/{task}/input [patch] -// -// EXPERIMENTAL: This endpoint is experimental and not guaranteed to be stable. -// taskUpdateInput allows modifying a task's prompt before the agent executes it. +// @Router /tasks/{user}/{task}/input [patch] func (api *API) taskUpdateInput(rw http.ResponseWriter, r *http.Request) { var ( ctx = r.Context() @@ -738,20 +719,15 @@ func (api *API) taskUpdateInput(rw http.ResponseWriter, r *http.Request) { } // @Summary Send input to AI task -// @Description: EXPERIMENTAL: this endpoint is experimental and not guaranteed to be stable. -// @ID send-task-input +// @ID send-input-to-ai-task // @Security CoderSessionToken +// @Accept json // @Tags Experimental // @Param user path string true "Username, user ID, or 'me' for the authenticated user" // @Param task path string true "Task ID, or task name" // @Param request body codersdk.TaskSendRequest true "Task input request" -// @Success 204 "Input sent successfully" -// @Router /api/experimental/tasks/{user}/{task}/send [post] -// -// EXPERIMENTAL: This endpoint is experimental and not guaranteed to be stable. -// taskSend submits task input to the task app by dialing the agent -// directly over the tailnet. We enforce ApplicationConnect RBAC on the -// workspace and validate the task app health. +// @Success 204 +// @Router /tasks/{user}/{task}/send [post] func (api *API) taskSend(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() task := httpmw.TaskParam(r) @@ -812,18 +788,14 @@ func (api *API) taskSend(rw http.ResponseWriter, r *http.Request) { } // @Summary Get AI task logs -// @Description: EXPERIMENTAL: this endpoint is experimental and not guaranteed to be stable. -// @ID get-task-logs +// @ID get-ai-task-logs // @Security CoderSessionToken +// @Produce json // @Tags Experimental // @Param user path string true "Username, user ID, or 'me' for the authenticated user" // @Param task path string true "Task ID, or task name" // @Success 200 {object} codersdk.TaskLogsResponse -// @Router /api/experimental/tasks/{user}/{task}/logs [get] -// -// EXPERIMENTAL: This endpoint is experimental and not guaranteed to be stable. -// taskLogs reads task output by dialing the agent directly over the tailnet. -// We enforce ApplicationConnect RBAC on the workspace and validate the task app health. +// @Router /tasks/{user}/{task}/logs [get] func (api *API) taskLogs(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() task := httpmw.TaskParam(r) diff --git a/coderd/aitasks_test.go b/coderd/aitasks_test.go index c3af01e8175ad..3301f8bdd5f31 100644 --- a/coderd/aitasks_test.go +++ b/coderd/aitasks_test.go @@ -124,8 +124,7 @@ func TestTasks(t *testing.T) { // Create a task with a specific prompt using the new data model. wantPrompt := "build me a web app" - exp := codersdk.NewExperimentalClient(client) - task, err := exp.CreateTask(ctx, codersdk.Me, codersdk.CreateTaskRequest{ + task, err := client.CreateTask(ctx, codersdk.Me, codersdk.CreateTaskRequest{ TemplateVersionID: template.ActiveVersionID, Input: wantPrompt, }) @@ -141,7 +140,7 @@ func TestTasks(t *testing.T) { coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, workspace.LatestBuild.ID) // List tasks via experimental API and verify the prompt and status mapping. - tasks, err := exp.Tasks(ctx, &codersdk.TasksFilter{Owner: codersdk.Me}) + tasks, err := client.Tasks(ctx, &codersdk.TasksFilter{Owner: codersdk.Me}) require.NoError(t, err) got, ok := slice.Find(tasks, func(t codersdk.Task) bool { return t.ID == task.ID }) @@ -164,10 +163,9 @@ func TestTasks(t *testing.T) { anotherUser, _ = coderdtest.CreateAnotherUser(t, client, user.OrganizationID) template = createAITemplate(t, client, user) wantPrompt = "review my code" - exp = codersdk.NewExperimentalClient(client) ) - task, err := exp.CreateTask(ctx, "me", codersdk.CreateTaskRequest{ + task, err := client.CreateTask(ctx, "me", codersdk.CreateTaskRequest{ TemplateVersionID: template.ActiveVersionID, Input: wantPrompt, }) @@ -201,7 +199,7 @@ func TestTasks(t *testing.T) { require.NoError(t, err) // Fetch the task by ID via experimental API and verify fields. - updated, err := exp.TaskByID(ctx, task.ID) + updated, err := client.TaskByID(ctx, task.ID) require.NoError(t, err) assert.Equal(t, task.ID, updated.ID, "task ID should match") @@ -215,19 +213,18 @@ func TestTasks(t *testing.T) { assert.NotEmpty(t, updated.WorkspaceStatus, "task status should not be empty") // Fetch the task by name and verify the same result - byName, err := exp.TaskByOwnerAndName(ctx, codersdk.Me, task.Name) + byName, err := client.TaskByOwnerAndName(ctx, codersdk.Me, task.Name) require.NoError(t, err) require.Equal(t, byName, updated) // Another member user should not be able to fetch the task - otherClient := codersdk.NewExperimentalClient(anotherUser) - _, err = otherClient.TaskByID(ctx, task.ID) + _, err = anotherUser.TaskByID(ctx, task.ID) require.Error(t, err, "fetching task should fail by ID for another member user") var sdkErr *codersdk.Error require.ErrorAs(t, err, &sdkErr) require.Equal(t, http.StatusNotFound, sdkErr.StatusCode()) // Also test by name - _, err = otherClient.TaskByOwnerAndName(ctx, task.OwnerName, task.Name) + _, err = anotherUser.TaskByOwnerAndName(ctx, task.OwnerName, task.Name) require.Error(t, err, "fetching task should fail by name for another member user") require.ErrorAs(t, err, &sdkErr) require.Equal(t, http.StatusNotFound, sdkErr.StatusCode()) @@ -236,7 +233,7 @@ func TestTasks(t *testing.T) { coderdtest.MustTransitionWorkspace(t, client, task.WorkspaceID.UUID, codersdk.WorkspaceTransitionStart, codersdk.WorkspaceTransitionStop) // Verify that the previous status still remains - updated, err = exp.TaskByID(ctx, task.ID) + updated, err = client.TaskByID(ctx, task.ID) require.NoError(t, err) assert.NotNil(t, updated.CurrentState, "current state should not be nil") assert.Equal(t, "all done", updated.CurrentState.Message) @@ -248,7 +245,7 @@ func TestTasks(t *testing.T) { // Verify that the status from the previous build has been cleared // and replaced by the agent initialization status. - updated, err = exp.TaskByID(ctx, task.ID) + updated, err = client.TaskByID(ctx, task.ID) require.NoError(t, err) assert.NotEqual(t, previousCurrentState, updated.CurrentState) assert.Equal(t, codersdk.TaskStateWorking, updated.CurrentState.State) @@ -267,8 +264,7 @@ func TestTasks(t *testing.T) { ctx := testutil.Context(t, testutil.WaitLong) - exp := codersdk.NewExperimentalClient(client) - task, err := exp.CreateTask(ctx, "me", codersdk.CreateTaskRequest{ + task, err := client.CreateTask(ctx, "me", codersdk.CreateTaskRequest{ TemplateVersionID: template.ActiveVersionID, Input: "delete me", }) @@ -281,7 +277,7 @@ func TestTasks(t *testing.T) { } coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, ws.LatestBuild.ID) - err = exp.DeleteTask(ctx, "me", task.ID) + err = client.DeleteTask(ctx, "me", task.ID) require.NoError(t, err, "delete task request should be accepted") // Poll until the workspace is deleted. @@ -303,8 +299,7 @@ func TestTasks(t *testing.T) { ctx := testutil.Context(t, testutil.WaitShort) - exp := codersdk.NewExperimentalClient(client) - err := exp.DeleteTask(ctx, "me", uuid.New()) + err := client.DeleteTask(ctx, "me", uuid.New()) var sdkErr *codersdk.Error require.Error(t, err, "expected an error for non-existent task") @@ -330,8 +325,7 @@ func TestTasks(t *testing.T) { } coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, ws.LatestBuild.ID) - exp := codersdk.NewExperimentalClient(client) - err := exp.DeleteTask(ctx, "me", ws.ID) + err := client.DeleteTask(ctx, "me", ws.ID) var sdkErr *codersdk.Error require.Error(t, err, "expected an error for non-task workspace delete via tasks endpoint") @@ -350,8 +344,7 @@ func TestTasks(t *testing.T) { ctx := testutil.Context(t, testutil.WaitShort) - exp := codersdk.NewExperimentalClient(client) - task, err := exp.CreateTask(ctx, "me", codersdk.CreateTaskRequest{ + task, err := client.CreateTask(ctx, "me", codersdk.CreateTaskRequest{ TemplateVersionID: template.ActiveVersionID, Input: "delete me not", }) @@ -363,10 +356,9 @@ func TestTasks(t *testing.T) { // Another regular org member without elevated permissions. otherClient, _ := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID) - expOther := codersdk.NewExperimentalClient(otherClient) // Attempt to delete the owner's task as a non-owner without permissions. - err = expOther.DeleteTask(ctx, "me", task.ID) + err = otherClient.DeleteTask(ctx, "me", task.ID) var authErr *codersdk.Error require.Error(t, err, "expected an authorization error when deleting another user's task") @@ -384,8 +376,7 @@ func TestTasks(t *testing.T) { user := coderdtest.CreateFirstUser(t, client) template := createAITemplate(t, client, user) ctx := testutil.Context(t, testutil.WaitLong) - exp := codersdk.NewExperimentalClient(client) - task, err := exp.CreateTask(ctx, "me", codersdk.CreateTaskRequest{ + task, err := client.CreateTask(ctx, "me", codersdk.CreateTaskRequest{ TemplateVersionID: template.ActiveVersionID, Input: "delete me", }) @@ -404,9 +395,9 @@ func TestTasks(t *testing.T) { // Provisionerdserver will attempt delete the related task when deleting a workspace. // This test ensures that we can still handle the case where, for some reason, the // task has not been marked as deleted, but the workspace has. - task, err = exp.TaskByID(ctx, task.ID) + task, err = client.TaskByID(ctx, task.ID) require.NoError(t, err, "fetching a task should still work if its related workspace is deleted") - err = exp.DeleteTask(ctx, task.OwnerID.String(), task.ID) + err = client.DeleteTask(ctx, task.OwnerID.String(), task.ID) require.NoError(t, err, "should be possible to delete a task with no workspace") }) @@ -419,8 +410,7 @@ func TestTasks(t *testing.T) { ctx := testutil.Context(t, testutil.WaitLong) - exp := codersdk.NewExperimentalClient(client) - task, err := exp.CreateTask(ctx, "me", codersdk.CreateTaskRequest{ + task, err := client.CreateTask(ctx, "me", codersdk.CreateTaskRequest{ TemplateVersionID: template.ActiveVersionID, Input: "delete me", }) @@ -436,7 +426,7 @@ func TestTasks(t *testing.T) { // When; the task workspace is deleted coderdtest.MustTransitionWorkspace(t, client, ws.ID, codersdk.WorkspaceTransitionStart, codersdk.WorkspaceTransitionDelete) // Then: the task associated with the workspace is also deleted - _, err = exp.TaskByID(ctx, task.ID) + _, err = client.TaskByID(ctx, task.ID) require.Error(t, err, "expected an error fetching the task") var sdkErr *codersdk.Error require.ErrorAs(t, err, &sdkErr, "expected a codersdk.Error") @@ -495,10 +485,9 @@ func TestTasks(t *testing.T) { userClient, _ = coderdtest.CreateAnotherUser(t, client, owner.OrganizationID) agentAuthToken = uuid.NewString() template = createAITemplate(t, client, owner, withAgentToken(agentAuthToken), withSidebarURL(srv.URL)) - exp = codersdk.NewExperimentalClient(userClient) ) - task, err := exp.CreateTask(ctx, "me", codersdk.CreateTaskRequest{ + task, err := userClient.CreateTask(ctx, "me", codersdk.CreateTaskRequest{ TemplateVersionID: template.ActiveVersionID, Input: "send me food", }) @@ -511,7 +500,7 @@ func TestTasks(t *testing.T) { coderdtest.AwaitWorkspaceBuildJobCompleted(t, userClient, ws.LatestBuild.ID) // Fetch the task by ID via experimental API and verify fields. - task, err = exp.TaskByID(ctx, task.ID) + task, err = client.TaskByID(ctx, task.ID) require.NoError(t, err) require.NotZero(t, task.WorkspaceBuildNumber) require.True(t, task.WorkspaceAgentID.Valid) @@ -537,7 +526,7 @@ func TestTasks(t *testing.T) { coderdtest.NewWorkspaceAgentWaiter(t, userClient, ws.ID).WaitFor(coderdtest.AgentsReady) // Fetch the task by ID via experimental API and verify fields. - task, err = exp.TaskByID(ctx, task.ID) + task, err = client.TaskByID(ctx, task.ID) require.NoError(t, err) // Make the sidebar app unhealthy initially. @@ -547,7 +536,7 @@ func TestTasks(t *testing.T) { }) require.NoError(t, err) - err = exp.TaskSend(ctx, "me", task.ID, codersdk.TaskSendRequest{ + err = client.TaskSend(ctx, "me", task.ID, codersdk.TaskSendRequest{ Input: "Hello, Agent!", }) require.Error(t, err, "wanted error due to unhealthy sidebar app") @@ -561,7 +550,7 @@ func TestTasks(t *testing.T) { statusResponse = agentapisdk.AgentStatus("bad") - err = exp.TaskSend(ctx, "me", task.ID, codersdk.TaskSendRequest{ + err = client.TaskSend(ctx, "me", task.ID, codersdk.TaskSendRequest{ Input: "Hello, Agent!", }) require.Error(t, err, "wanted error due to bad status") @@ -570,7 +559,7 @@ func TestTasks(t *testing.T) { //nolint:tparallel // Not intended to run in parallel. t.Run("SendOK", func(t *testing.T) { - err = exp.TaskSend(ctx, "me", task.ID, codersdk.TaskSendRequest{ + err = client.TaskSend(ctx, "me", task.ID, codersdk.TaskSendRequest{ Input: "Hello, Agent!", }) require.NoError(t, err, "wanted no error due to healthy sidebar app and stable status") @@ -578,7 +567,7 @@ func TestTasks(t *testing.T) { //nolint:tparallel // Not intended to run in parallel. t.Run("MissingContent", func(t *testing.T) { - err = exp.TaskSend(ctx, "me", task.ID, codersdk.TaskSendRequest{ + err = client.TaskSend(ctx, "me", task.ID, codersdk.TaskSendRequest{ Input: "", }) require.Error(t, err, "wanted error due to missing content") @@ -596,8 +585,7 @@ func TestTasks(t *testing.T) { _ = coderdtest.CreateFirstUser(t, client) ctx := testutil.Context(t, testutil.WaitShort) - exp := codersdk.NewExperimentalClient(client) - err := exp.TaskSend(ctx, "me", uuid.New(), codersdk.TaskSendRequest{ + err := client.TaskSend(ctx, "me", uuid.New(), codersdk.TaskSendRequest{ Input: "hi", }) @@ -663,10 +651,9 @@ func TestTasks(t *testing.T) { owner = coderdtest.CreateFirstUser(t, client) agentAuthToken = uuid.NewString() template = createAITemplate(t, client, owner, withAgentToken(agentAuthToken), withSidebarURL(srv.URL)) - exp = codersdk.NewExperimentalClient(client) ) - task, err := exp.CreateTask(ctx, "me", codersdk.CreateTaskRequest{ + task, err := client.CreateTask(ctx, "me", codersdk.CreateTaskRequest{ TemplateVersionID: template.ActiveVersionID, Input: "show logs", }) @@ -679,7 +666,7 @@ func TestTasks(t *testing.T) { coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, ws.LatestBuild.ID) // Fetch the task by ID via experimental API and verify fields. - task, err = exp.TaskByIdentifier(ctx, task.ID.String()) + task, err = client.TaskByIdentifier(ctx, task.ID.String()) require.NoError(t, err) require.NotZero(t, task.WorkspaceBuildNumber) require.True(t, task.WorkspaceAgentID.Valid) @@ -705,13 +692,13 @@ func TestTasks(t *testing.T) { coderdtest.NewWorkspaceAgentWaiter(t, client, ws.ID).WaitFor(coderdtest.AgentsReady) // Fetch the task by ID via experimental API and verify fields. - task, err = exp.TaskByID(ctx, task.ID) + task, err = client.TaskByID(ctx, task.ID) require.NoError(t, err) //nolint:tparallel // Not intended to run in parallel. t.Run("OK", func(t *testing.T) { // Fetch logs. - resp, err := exp.TaskLogs(ctx, "me", task.ID) + resp, err := client.TaskLogs(ctx, "me", task.ID) require.NoError(t, err) require.Len(t, resp.Logs, 3) assert.Equal(t, 0, resp.Logs[0].ID) @@ -731,7 +718,7 @@ func TestTasks(t *testing.T) { t.Run("UpstreamError", func(t *testing.T) { shouldReturnError = true t.Cleanup(func() { shouldReturnError = false }) - _, err := exp.TaskLogs(ctx, "me", task.ID) + _, err := client.TaskLogs(ctx, "me", task.ID) var sdkErr *codersdk.Error require.Error(t, err) @@ -811,8 +798,7 @@ func TestTasks(t *testing.T) { } // Given: We create a task - exp := codersdk.NewExperimentalClient(client) - task, err := exp.CreateTask(ctx, codersdk.Me, codersdk.CreateTaskRequest{ + task, err := client.CreateTask(ctx, codersdk.Me, codersdk.CreateTaskRequest{ TemplateVersionID: template.ActiveVersionID, Input: "initial prompt", }) @@ -844,17 +830,17 @@ func TestTasks(t *testing.T) { } if tt.deleteTask { - err = exp.DeleteTask(ctx, codersdk.Me, task.ID) + err = client.DeleteTask(ctx, codersdk.Me, task.ID) require.NoError(t, err) } else { // Given: Task has expected status - task, err = exp.TaskByID(ctx, task.ID) + task, err = client.TaskByID(ctx, task.ID) require.NoError(t, err) require.Equal(t, tt.wantStatus, task.Status) } // When: We attempt to update the task input - err = exp.UpdateTaskInput(ctx, task.OwnerName, task.ID, codersdk.UpdateTaskInputRequest{ + err = client.UpdateTaskInput(ctx, task.OwnerName, task.ID, codersdk.UpdateTaskInputRequest{ Input: tt.taskInput, }) if tt.wantErr != "" { @@ -868,7 +854,7 @@ func TestTasks(t *testing.T) { if !tt.deleteTask { // Then: We expect the input to **not** be updated - task, err = exp.TaskByID(ctx, task.ID) + task, err = client.TaskByID(ctx, task.ID) require.NoError(t, err) require.NotEqual(t, tt.taskInput, task.InitialPrompt) } @@ -877,7 +863,7 @@ func TestTasks(t *testing.T) { if !tt.deleteTask { // Then: We expect the input to be updated - task, err = exp.TaskByID(ctx, task.ID) + task, err = client.TaskByID(ctx, task.ID) require.NoError(t, err) require.Equal(t, tt.taskInput, task.InitialPrompt) } @@ -892,10 +878,8 @@ func TestTasks(t *testing.T) { user := coderdtest.CreateFirstUser(t, client) ctx := testutil.Context(t, testutil.WaitShort) - exp := codersdk.NewExperimentalClient(client) - // Attempt to update prompt for non-existent task - err := exp.UpdateTaskInput(ctx, user.UserID.String(), uuid.New(), codersdk.UpdateTaskInputRequest{ + err := client.UpdateTaskInput(ctx, user.UserID.String(), uuid.New(), codersdk.UpdateTaskInputRequest{ Input: "Should fail", }) require.Error(t, err) @@ -915,8 +899,7 @@ func TestTasks(t *testing.T) { template := createAITemplate(t, client, user) // Create a task as the first user - exp := codersdk.NewExperimentalClient(client) - task, err := exp.CreateTask(ctx, codersdk.Me, codersdk.CreateTaskRequest{ + task, err := client.CreateTask(ctx, codersdk.Me, codersdk.CreateTaskRequest{ TemplateVersionID: template.ActiveVersionID, Input: "initial prompt", }) @@ -933,8 +916,7 @@ func TestTasks(t *testing.T) { coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, build.ID) // Attempt to update prompt as another user should fail with 404 Not Found - otherExp := codersdk.NewExperimentalClient(anotherUser) - err = otherExp.UpdateTaskInput(ctx, task.OwnerName, task.ID, codersdk.UpdateTaskInputRequest{ + err = anotherUser.UpdateTaskInput(ctx, task.OwnerName, task.ID, codersdk.UpdateTaskInputRequest{ Input: "Should fail - unauthorized", }) require.Error(t, err) @@ -972,9 +954,7 @@ func TestTasksCreate(t *testing.T) { coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) - expClient := codersdk.NewExperimentalClient(client) - - task, err := expClient.CreateTask(ctx, "me", codersdk.CreateTaskRequest{ + task, err := client.CreateTask(ctx, "me", codersdk.CreateTaskRequest{ TemplateVersionID: template.ActiveVersionID, Input: taskPrompt, }) @@ -1019,10 +999,8 @@ func TestTasksCreate(t *testing.T) { coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) - expClient := codersdk.NewExperimentalClient(client) - // When: We attempt to create a Task. - task, err := expClient.CreateTask(ctx, "me", codersdk.CreateTaskRequest{ + task, err := client.CreateTask(ctx, "me", codersdk.CreateTaskRequest{ TemplateVersionID: template.ActiveVersionID, Input: taskPrompt, }) @@ -1107,11 +1085,10 @@ func TestTasksCreate(t *testing.T) { t.Parallel() var ( - ctx = testutil.Context(t, testutil.WaitShort) - client = coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true}) - expClient = codersdk.NewExperimentalClient(client) - user = coderdtest.CreateFirstUser(t, client) - version = coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ + ctx = testutil.Context(t, testutil.WaitShort) + client = coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true}) + user = coderdtest.CreateFirstUser(t, client) + version = coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, ProvisionApply: echo.ApplyComplete, ProvisionPlan: []*proto.Response{ @@ -1126,7 +1103,7 @@ func TestTasksCreate(t *testing.T) { coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) // When: We attempt to create a Task. - task, err := expClient.CreateTask(ctx, "me", codersdk.CreateTaskRequest{ + task, err := client.CreateTask(ctx, "me", codersdk.CreateTaskRequest{ TemplateVersionID: template.ActiveVersionID, Input: "Some prompt", Name: tt.taskName, @@ -1177,10 +1154,8 @@ func TestTasksCreate(t *testing.T) { coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) - expClient := codersdk.NewExperimentalClient(client) - // When: We attempt to create a Task. - _, err := expClient.CreateTask(ctx, "me", codersdk.CreateTaskRequest{ + _, err := client.CreateTask(ctx, "me", codersdk.CreateTaskRequest{ TemplateVersionID: template.ActiveVersionID, Input: taskPrompt, }) @@ -1209,10 +1184,8 @@ func TestTasksCreate(t *testing.T) { coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) _ = coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) - expClient := codersdk.NewExperimentalClient(client) - // When: We attempt to create a Task with an invalid template version ID. - _, err := expClient.CreateTask(ctx, "me", codersdk.CreateTaskRequest{ + _, err := client.CreateTask(ctx, "me", codersdk.CreateTaskRequest{ TemplateVersionID: uuid.New(), Input: taskPrompt, }) @@ -1248,9 +1221,7 @@ func TestTasksCreate(t *testing.T) { coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) - expClient := codersdk.NewExperimentalClient(client) - - task, err := expClient.CreateTask(ctx, "me", codersdk.CreateTaskRequest{ + task, err := client.CreateTask(ctx, "me", codersdk.CreateTaskRequest{ TemplateVersionID: template.ActiveVersionID, Input: taskPrompt, }) @@ -1307,9 +1278,7 @@ func TestTasksCreate(t *testing.T) { coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) - expClient := codersdk.NewExperimentalClient(client) - - task, err := expClient.CreateTask(ctx, "me", codersdk.CreateTaskRequest{ + task, err := client.CreateTask(ctx, "me", codersdk.CreateTaskRequest{ TemplateVersionID: template.ActiveVersionID, Input: taskPrompt, Name: taskName, @@ -1343,16 +1312,14 @@ func TestTasksCreate(t *testing.T) { coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) - expClient := codersdk.NewExperimentalClient(client) - - task1, err := expClient.CreateTask(ctx, "me", codersdk.CreateTaskRequest{ + task1, err := client.CreateTask(ctx, "me", codersdk.CreateTaskRequest{ TemplateVersionID: template.ActiveVersionID, Input: "First task", Name: "task-1", }) require.NoError(t, err) - task2, err := expClient.CreateTask(ctx, "me", codersdk.CreateTaskRequest{ + task2, err := client.CreateTask(ctx, "me", codersdk.CreateTaskRequest{ TemplateVersionID: template.ActiveVersionID, Input: "Second task", Name: "task-2", @@ -1406,11 +1373,9 @@ func TestTasksCreate(t *testing.T) { }, template.ID) coderdtest.AwaitTemplateVersionJobCompleted(t, client, version2.ID) - expClient := codersdk.NewExperimentalClient(client) - // Create a task using version 2 to verify the template_version_id is // stored correctly. - task, err := expClient.CreateTask(ctx, "me", codersdk.CreateTaskRequest{ + task, err := client.CreateTask(ctx, "me", codersdk.CreateTaskRequest{ TemplateVersionID: version2.ID, Input: "Use version 2", }) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 1b60b41717728..c92c15f159671 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -136,273 +136,6 @@ const docTemplate = `{ } } }, - "/api/experimental/tasks": { - "get": { - "security": [ - { - "CoderSessionToken": [] - } - ], - "tags": [ - "Experimental" - ], - "summary": "List AI tasks", - "operationId": "list-tasks", - "parameters": [ - { - "type": "string", - "description": "Search query for filtering tasks. Supports: owner:\u003cusername/uuid/me\u003e, organization:\u003corg-name/uuid\u003e, status:\u003cstatus\u003e", - "name": "q", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.TasksListResponse" - } - } - } - } - }, - "/api/experimental/tasks/{user}": { - "post": { - "security": [ - { - "CoderSessionToken": [] - } - ], - "tags": [ - "Experimental" - ], - "summary": "Create a new AI task", - "operationId": "create-task", - "parameters": [ - { - "type": "string", - "description": "Username, user ID, or 'me' for the authenticated user", - "name": "user", - "in": "path", - "required": true - }, - { - "description": "Create task request", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.CreateTaskRequest" - } - } - ], - "responses": { - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/codersdk.Task" - } - } - } - } - }, - "/api/experimental/tasks/{user}/{task}": { - "get": { - "security": [ - { - "CoderSessionToken": [] - } - ], - "tags": [ - "Experimental" - ], - "summary": "Get AI task by ID", - "operationId": "get-task", - "parameters": [ - { - "type": "string", - "description": "Username, user ID, or 'me' for the authenticated user", - "name": "user", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Task ID, or task name", - "name": "task", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.Task" - } - } - } - }, - "delete": { - "security": [ - { - "CoderSessionToken": [] - } - ], - "tags": [ - "Experimental" - ], - "summary": "Delete AI task by ID", - "operationId": "delete-task", - "parameters": [ - { - "type": "string", - "description": "Username, user ID, or 'me' for the authenticated user", - "name": "user", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Task ID, or task name", - "name": "task", - "in": "path", - "required": true - } - ], - "responses": { - "202": { - "description": "Task deletion initiated" - } - } - } - }, - "/api/experimental/tasks/{user}/{task}/input": { - "patch": { - "security": [ - { - "CoderSessionToken": [] - } - ], - "tags": [ - "Experimental" - ], - "summary": "Update AI task input", - "operationId": "update-task-input", - "parameters": [ - { - "type": "string", - "description": "Username, user ID, or 'me' for the authenticated user", - "name": "user", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Task ID, or task name", - "name": "task", - "in": "path", - "required": true - }, - { - "description": "Update task input request", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.UpdateTaskInputRequest" - } - } - ], - "responses": { - "204": { - "description": "No Content" - } - } - } - }, - "/api/experimental/tasks/{user}/{task}/logs": { - "get": { - "security": [ - { - "CoderSessionToken": [] - } - ], - "tags": [ - "Experimental" - ], - "summary": "Get AI task logs", - "operationId": "get-task-logs", - "parameters": [ - { - "type": "string", - "description": "Username, user ID, or 'me' for the authenticated user", - "name": "user", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Task ID, or task name", - "name": "task", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.TaskLogsResponse" - } - } - } - } - }, - "/api/experimental/tasks/{user}/{task}/send": { - "post": { - "security": [ - { - "CoderSessionToken": [] - } - ], - "tags": [ - "Experimental" - ], - "summary": "Send input to AI task", - "operationId": "send-task-input", - "parameters": [ - { - "type": "string", - "description": "Username, user ID, or 'me' for the authenticated user", - "name": "user", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Task ID, or task name", - "name": "task", - "in": "path", - "required": true - }, - { - "description": "Task input request", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.TaskSendRequest" - } - } - ], - "responses": { - "204": { - "description": "Input sent successfully" - } - } - } - }, "/appearance": { "get": { "security": [ @@ -5719,6 +5452,294 @@ const docTemplate = `{ } } }, + "/tasks": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Experimental" + ], + "summary": "List AI tasks", + "operationId": "list-ai-tasks", + "parameters": [ + { + "type": "string", + "description": "Search query for filtering tasks. Supports: owner:\u003cusername/uuid/me\u003e, organization:\u003corg-name/uuid\u003e, status:\u003cstatus\u003e", + "name": "q", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.TasksListResponse" + } + } + } + } + }, + "/tasks/{user}": { + "post": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Experimental" + ], + "summary": "Create a new AI task", + "operationId": "create-a-new-ai-task", + "parameters": [ + { + "type": "string", + "description": "Username, user ID, or 'me' for the authenticated user", + "name": "user", + "in": "path", + "required": true + }, + { + "description": "Create task request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.CreateTaskRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/codersdk.Task" + } + } + } + } + }, + "/tasks/{user}/{task}": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Experimental" + ], + "summary": "Get AI task by ID or name", + "operationId": "get-ai-task-by-id-or-name", + "parameters": [ + { + "type": "string", + "description": "Username, user ID, or 'me' for the authenticated user", + "name": "user", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Task ID, or task name", + "name": "task", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.Task" + } + } + } + }, + "delete": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "tags": [ + "Experimental" + ], + "summary": "Delete AI task", + "operationId": "delete-ai-task", + "parameters": [ + { + "type": "string", + "description": "Username, user ID, or 'me' for the authenticated user", + "name": "user", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Task ID, or task name", + "name": "task", + "in": "path", + "required": true + } + ], + "responses": { + "202": { + "description": "Accepted" + } + } + } + }, + "/tasks/{user}/{task}/input": { + "patch": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "consumes": [ + "application/json" + ], + "tags": [ + "Experimental" + ], + "summary": "Update AI task input", + "operationId": "update-ai-task-input", + "parameters": [ + { + "type": "string", + "description": "Username, user ID, or 'me' for the authenticated user", + "name": "user", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Task ID, or task name", + "name": "task", + "in": "path", + "required": true + }, + { + "description": "Update task input request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateTaskInputRequest" + } + } + ], + "responses": { + "204": { + "description": "No Content" + } + } + } + }, + "/tasks/{user}/{task}/logs": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Experimental" + ], + "summary": "Get AI task logs", + "operationId": "get-ai-task-logs", + "parameters": [ + { + "type": "string", + "description": "Username, user ID, or 'me' for the authenticated user", + "name": "user", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Task ID, or task name", + "name": "task", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.TaskLogsResponse" + } + } + } + } + }, + "/tasks/{user}/{task}/send": { + "post": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "consumes": [ + "application/json" + ], + "tags": [ + "Experimental" + ], + "summary": "Send input to AI task", + "operationId": "send-input-to-ai-task", + "parameters": [ + { + "type": "string", + "description": "Username, user ID, or 'me' for the authenticated user", + "name": "user", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Task ID, or task name", + "name": "task", + "in": "path", + "required": true + }, + { + "description": "Task input request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.TaskSendRequest" + } + } + ], + "responses": { + "204": { + "description": "No Content" + } + } + } + }, "/templates": { "get": { "security": [ diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 0639cf08a18f1..406426c34c4b5 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -112,259 +112,6 @@ } } }, - "/api/experimental/tasks": { - "get": { - "security": [ - { - "CoderSessionToken": [] - } - ], - "tags": ["Experimental"], - "summary": "List AI tasks", - "operationId": "list-tasks", - "parameters": [ - { - "type": "string", - "description": "Search query for filtering tasks. Supports: owner:\u003cusername/uuid/me\u003e, organization:\u003corg-name/uuid\u003e, status:\u003cstatus\u003e", - "name": "q", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.TasksListResponse" - } - } - } - } - }, - "/api/experimental/tasks/{user}": { - "post": { - "security": [ - { - "CoderSessionToken": [] - } - ], - "tags": ["Experimental"], - "summary": "Create a new AI task", - "operationId": "create-task", - "parameters": [ - { - "type": "string", - "description": "Username, user ID, or 'me' for the authenticated user", - "name": "user", - "in": "path", - "required": true - }, - { - "description": "Create task request", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.CreateTaskRequest" - } - } - ], - "responses": { - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/codersdk.Task" - } - } - } - } - }, - "/api/experimental/tasks/{user}/{task}": { - "get": { - "security": [ - { - "CoderSessionToken": [] - } - ], - "tags": ["Experimental"], - "summary": "Get AI task by ID", - "operationId": "get-task", - "parameters": [ - { - "type": "string", - "description": "Username, user ID, or 'me' for the authenticated user", - "name": "user", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Task ID, or task name", - "name": "task", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.Task" - } - } - } - }, - "delete": { - "security": [ - { - "CoderSessionToken": [] - } - ], - "tags": ["Experimental"], - "summary": "Delete AI task by ID", - "operationId": "delete-task", - "parameters": [ - { - "type": "string", - "description": "Username, user ID, or 'me' for the authenticated user", - "name": "user", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Task ID, or task name", - "name": "task", - "in": "path", - "required": true - } - ], - "responses": { - "202": { - "description": "Task deletion initiated" - } - } - } - }, - "/api/experimental/tasks/{user}/{task}/input": { - "patch": { - "security": [ - { - "CoderSessionToken": [] - } - ], - "tags": ["Experimental"], - "summary": "Update AI task input", - "operationId": "update-task-input", - "parameters": [ - { - "type": "string", - "description": "Username, user ID, or 'me' for the authenticated user", - "name": "user", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Task ID, or task name", - "name": "task", - "in": "path", - "required": true - }, - { - "description": "Update task input request", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.UpdateTaskInputRequest" - } - } - ], - "responses": { - "204": { - "description": "No Content" - } - } - } - }, - "/api/experimental/tasks/{user}/{task}/logs": { - "get": { - "security": [ - { - "CoderSessionToken": [] - } - ], - "tags": ["Experimental"], - "summary": "Get AI task logs", - "operationId": "get-task-logs", - "parameters": [ - { - "type": "string", - "description": "Username, user ID, or 'me' for the authenticated user", - "name": "user", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Task ID, or task name", - "name": "task", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.TaskLogsResponse" - } - } - } - } - }, - "/api/experimental/tasks/{user}/{task}/send": { - "post": { - "security": [ - { - "CoderSessionToken": [] - } - ], - "tags": ["Experimental"], - "summary": "Send input to AI task", - "operationId": "send-task-input", - "parameters": [ - { - "type": "string", - "description": "Username, user ID, or 'me' for the authenticated user", - "name": "user", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Task ID, or task name", - "name": "task", - "in": "path", - "required": true - }, - { - "description": "Task input request", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.TaskSendRequest" - } - } - ], - "responses": { - "204": { - "description": "Input sent successfully" - } - } - } - }, "/appearance": { "get": { "security": [ @@ -5064,6 +4811,266 @@ } } }, + "/tasks": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Experimental"], + "summary": "List AI tasks", + "operationId": "list-ai-tasks", + "parameters": [ + { + "type": "string", + "description": "Search query for filtering tasks. Supports: owner:\u003cusername/uuid/me\u003e, organization:\u003corg-name/uuid\u003e, status:\u003cstatus\u003e", + "name": "q", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.TasksListResponse" + } + } + } + } + }, + "/tasks/{user}": { + "post": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["Experimental"], + "summary": "Create a new AI task", + "operationId": "create-a-new-ai-task", + "parameters": [ + { + "type": "string", + "description": "Username, user ID, or 'me' for the authenticated user", + "name": "user", + "in": "path", + "required": true + }, + { + "description": "Create task request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.CreateTaskRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/codersdk.Task" + } + } + } + } + }, + "/tasks/{user}/{task}": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Experimental"], + "summary": "Get AI task by ID or name", + "operationId": "get-ai-task-by-id-or-name", + "parameters": [ + { + "type": "string", + "description": "Username, user ID, or 'me' for the authenticated user", + "name": "user", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Task ID, or task name", + "name": "task", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.Task" + } + } + } + }, + "delete": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "tags": ["Experimental"], + "summary": "Delete AI task", + "operationId": "delete-ai-task", + "parameters": [ + { + "type": "string", + "description": "Username, user ID, or 'me' for the authenticated user", + "name": "user", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Task ID, or task name", + "name": "task", + "in": "path", + "required": true + } + ], + "responses": { + "202": { + "description": "Accepted" + } + } + } + }, + "/tasks/{user}/{task}/input": { + "patch": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "consumes": ["application/json"], + "tags": ["Experimental"], + "summary": "Update AI task input", + "operationId": "update-ai-task-input", + "parameters": [ + { + "type": "string", + "description": "Username, user ID, or 'me' for the authenticated user", + "name": "user", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Task ID, or task name", + "name": "task", + "in": "path", + "required": true + }, + { + "description": "Update task input request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateTaskInputRequest" + } + } + ], + "responses": { + "204": { + "description": "No Content" + } + } + } + }, + "/tasks/{user}/{task}/logs": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Experimental"], + "summary": "Get AI task logs", + "operationId": "get-ai-task-logs", + "parameters": [ + { + "type": "string", + "description": "Username, user ID, or 'me' for the authenticated user", + "name": "user", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Task ID, or task name", + "name": "task", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.TaskLogsResponse" + } + } + } + } + }, + "/tasks/{user}/{task}/send": { + "post": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "consumes": ["application/json"], + "tags": ["Experimental"], + "summary": "Send input to AI task", + "operationId": "send-input-to-ai-task", + "parameters": [ + { + "type": "string", + "description": "Username, user ID, or 'me' for the authenticated user", + "name": "user", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Task ID, or task name", + "name": "task", + "in": "path", + "required": true + }, + { + "description": "Task input request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.TaskSendRequest" + } + } + ], + "responses": { + "204": { + "description": "No Content" + } + } + } + }, "/templates": { "get": { "security": [ diff --git a/coderd/autobuild/lifecycle_executor_test.go b/coderd/autobuild/lifecycle_executor_test.go index 466c8c40525e1..0610c781fe966 100644 --- a/coderd/autobuild/lifecycle_executor_test.go +++ b/coderd/autobuild/lifecycle_executor_test.go @@ -1830,8 +1830,7 @@ func TestExecutorTaskWorkspace(t *testing.T) { createTaskWorkspace := func(t *testing.T, client *codersdk.Client, template codersdk.Template, ctx context.Context, input string) codersdk.Workspace { t.Helper() - exp := codersdk.NewExperimentalClient(client) - task, err := exp.CreateTask(ctx, "me", codersdk.CreateTaskRequest{ + task, err := client.CreateTask(ctx, "me", codersdk.CreateTaskRequest{ TemplateVersionID: template.ActiveVersionID, Input: input, }) diff --git a/coderd/coderd.go b/coderd/coderd.go index 684781d844bb1..37b379b3a8f01 100644 --- a/coderd/coderd.go +++ b/coderd/coderd.go @@ -1023,6 +1023,9 @@ func New(options *Options) *API { httpmw.ReportCLITelemetry(api.Logger, options.Telemetry), ) + // NOTE(DanielleMaywood): + // Tasks have been promoted to stable, but we have guaranteed a single release transition period + // where these routes must remain. These should be removed no earlier than Coder v2.30.0 r.Route("/tasks", func(r chi.Router) { r.Use(apiKeyMiddleware) @@ -1650,6 +1653,25 @@ func New(options *Options) *API { r.Route("/init-script", func(r chi.Router) { r.Get("/{os}/{arch}", api.initScript) }) + r.Route("/tasks", func(r chi.Router) { + r.Use(apiKeyMiddleware) + + r.Get("/", api.tasksList) + + r.Route("/{user}", func(r chi.Router) { + r.Use(httpmw.ExtractOrganizationMembersParam(options.Database, api.HTTPAuth.Authorize)) + r.Post("/", api.tasksCreate) + + r.Route("/{task}", func(r chi.Router) { + r.Use(httpmw.ExtractTaskParam(options.Database)) + r.Get("/", api.taskGet) + r.Delete("/", api.taskDelete) + r.Patch("/input", api.taskUpdateInput) + r.Post("/send", api.taskSend) + r.Get("/logs", api.taskLogs) + }) + }) + }) }) if options.SwaggerEndpoint { diff --git a/coderd/workspaces_test.go b/coderd/workspaces_test.go index 536202bce7b96..4ab334222a438 100644 --- a/coderd/workspaces_test.go +++ b/coderd/workspaces_test.go @@ -4800,7 +4800,6 @@ func TestWorkspaceListTasks(t *testing.T) { ctx := testutil.Context(t, testutil.WaitShort) client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true}) user := coderdtest.CreateFirstUser(t, client) - expClient := codersdk.NewExperimentalClient(client) version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, @@ -4823,7 +4822,7 @@ func TestWorkspaceListTasks(t *testing.T) { coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, workspaceWithoutTask.LatestBuild.ID) // Given: a workspace associated with a task - task, err := expClient.CreateTask(ctx, codersdk.Me, codersdk.CreateTaskRequest{ + task, err := client.CreateTask(ctx, codersdk.Me, codersdk.CreateTaskRequest{ TemplateVersionID: template.ActiveVersionID, Input: "Some task prompt", }) diff --git a/codersdk/aitasks.go b/codersdk/aitasks.go index 894e2e7bed6a1..12079e76da71e 100644 --- a/codersdk/aitasks.go +++ b/codersdk/aitasks.go @@ -41,8 +41,8 @@ type CreateTaskRequest struct { // CreateTask creates a new task. // // Experimental: This method is experimental and may change in the future. -func (c *ExperimentalClient) CreateTask(ctx context.Context, user string, request CreateTaskRequest) (Task, error) { - res, err := c.Request(ctx, http.MethodPost, fmt.Sprintf("/api/experimental/tasks/%s", user), request) +func (c *Client) CreateTask(ctx context.Context, user string, request CreateTaskRequest) (Task, error) { + res, err := c.Request(ctx, http.MethodPost, fmt.Sprintf("/api/v2/tasks/%s", user), request) if err != nil { return Task{}, err } @@ -210,12 +210,12 @@ func (f TasksFilter) asRequestOption() RequestOption { // Tasks lists all tasks belonging to the user or specified owner. // // Experimental: This method is experimental and may change in the future. -func (c *ExperimentalClient) Tasks(ctx context.Context, filter *TasksFilter) ([]Task, error) { +func (c *Client) Tasks(ctx context.Context, filter *TasksFilter) ([]Task, error) { if filter == nil { filter = &TasksFilter{} } - res, err := c.Request(ctx, http.MethodGet, "/api/experimental/tasks", nil, filter.asRequestOption()) + res, err := c.Request(ctx, http.MethodGet, "/api/v2/tasks", nil, filter.asRequestOption()) if err != nil { return nil, err } @@ -236,8 +236,8 @@ func (c *ExperimentalClient) Tasks(ctx context.Context, filter *TasksFilter) ([] // Only tasks owned by codersdk.Me are supported. // // Experimental: This method is experimental and may change in the future. -func (c *ExperimentalClient) TaskByID(ctx context.Context, id uuid.UUID) (Task, error) { - res, err := c.Request(ctx, http.MethodGet, fmt.Sprintf("/api/experimental/tasks/%s/%s", "me", id.String()), nil) +func (c *Client) TaskByID(ctx context.Context, id uuid.UUID) (Task, error) { + res, err := c.Request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/tasks/%s/%s", "me", id.String()), nil) if err != nil { return Task{}, err } @@ -257,11 +257,11 @@ func (c *ExperimentalClient) TaskByID(ctx context.Context, id uuid.UUID) (Task, // TaskByOwnerAndName fetches a single experimental task by its owner and name. // // Experimental: This method is experimental and may change in the future. -func (c *ExperimentalClient) TaskByOwnerAndName(ctx context.Context, owner, ident string) (Task, error) { +func (c *Client) TaskByOwnerAndName(ctx context.Context, owner, ident string) (Task, error) { if owner == "" { owner = Me } - res, err := c.Request(ctx, http.MethodGet, fmt.Sprintf("/api/experimental/tasks/%s/%s", owner, ident), nil) + res, err := c.Request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/tasks/%s/%s", owner, ident), nil) if err != nil { return Task{}, err } @@ -300,7 +300,7 @@ func splitTaskIdentifier(identifier string) (owner string, taskName string, err // // Since there is no TaskByOwnerAndName endpoint yet, this function uses the // list endpoint with filtering when a name is provided. -func (c *ExperimentalClient) TaskByIdentifier(ctx context.Context, identifier string) (Task, error) { +func (c *Client) TaskByIdentifier(ctx context.Context, identifier string) (Task, error) { identifier = strings.TrimSpace(identifier) // Try parsing as UUID first. @@ -320,8 +320,8 @@ func (c *ExperimentalClient) TaskByIdentifier(ctx context.Context, identifier st // DeleteTask deletes a task by its ID. // // Experimental: This method is experimental and may change in the future. -func (c *ExperimentalClient) DeleteTask(ctx context.Context, user string, id uuid.UUID) error { - res, err := c.Request(ctx, http.MethodDelete, fmt.Sprintf("/api/experimental/tasks/%s/%s", user, id.String()), nil) +func (c *Client) DeleteTask(ctx context.Context, user string, id uuid.UUID) error { + res, err := c.Request(ctx, http.MethodDelete, fmt.Sprintf("/api/v2/tasks/%s/%s", user, id.String()), nil) if err != nil { return err } @@ -342,8 +342,8 @@ type TaskSendRequest struct { // TaskSend submits task input to the tasks sidebar app. // // Experimental: This method is experimental and may change in the future. -func (c *ExperimentalClient) TaskSend(ctx context.Context, user string, id uuid.UUID, req TaskSendRequest) error { - res, err := c.Request(ctx, http.MethodPost, fmt.Sprintf("/api/experimental/tasks/%s/%s/send", user, id.String()), req) +func (c *Client) TaskSend(ctx context.Context, user string, id uuid.UUID, req TaskSendRequest) error { + res, err := c.Request(ctx, http.MethodPost, fmt.Sprintf("/api/v2/tasks/%s/%s/send", user, id.String()), req) if err != nil { return err } @@ -364,8 +364,8 @@ type UpdateTaskInputRequest struct { // UpdateTaskInput updates the task's input. // // Experimental: This method is experimental and may change in the future. -func (c *ExperimentalClient) UpdateTaskInput(ctx context.Context, user string, id uuid.UUID, req UpdateTaskInputRequest) error { - res, err := c.Request(ctx, http.MethodPatch, fmt.Sprintf("/api/experimental/tasks/%s/%s/input", user, id.String()), req) +func (c *Client) UpdateTaskInput(ctx context.Context, user string, id uuid.UUID, req UpdateTaskInputRequest) error { + res, err := c.Request(ctx, http.MethodPatch, fmt.Sprintf("/api/v2/tasks/%s/%s/input", user, id.String()), req) if err != nil { return err } @@ -407,8 +407,8 @@ type TaskLogsResponse struct { // TaskLogs retrieves logs from the task's sidebar app via the experimental API. // // Experimental: This method is experimental and may change in the future. -func (c *ExperimentalClient) TaskLogs(ctx context.Context, user string, id uuid.UUID) (TaskLogsResponse, error) { - res, err := c.Request(ctx, http.MethodGet, fmt.Sprintf("/api/experimental/tasks/%s/%s/logs", user, id.String()), nil) +func (c *Client) TaskLogs(ctx context.Context, user string, id uuid.UUID) (TaskLogsResponse, error) { + res, err := c.Request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/tasks/%s/%s/logs", user, id.String()), nil) if err != nil { return TaskLogsResponse{}, err } diff --git a/codersdk/toolsdk/toolsdk.go b/codersdk/toolsdk/toolsdk.go index 1826729eed41a..454e014265134 100644 --- a/codersdk/toolsdk/toolsdk.go +++ b/codersdk/toolsdk/toolsdk.go @@ -1899,8 +1899,7 @@ var CreateTask = Tool[CreateTaskArgs, codersdk.Task]{ args.User = codersdk.Me } - expClient := codersdk.NewExperimentalClient(deps.coderClient) - task, err := expClient.CreateTask(ctx, args.User, codersdk.CreateTaskRequest{ + task, err := deps.coderClient.CreateTask(ctx, args.User, codersdk.CreateTaskRequest{ Input: args.Input, TemplateVersionID: tvID, TemplateVersionPresetID: tvPresetID, @@ -1937,14 +1936,12 @@ var DeleteTask = Tool[DeleteTaskArgs, codersdk.Response]{ return codersdk.Response{}, xerrors.New("task_id is required") } - expClient := codersdk.NewExperimentalClient(deps.coderClient) - - task, err := expClient.TaskByIdentifier(ctx, args.TaskID) + task, err := deps.coderClient.TaskByIdentifier(ctx, args.TaskID) if err != nil { return codersdk.Response{}, xerrors.Errorf("resolve task: %w", err) } - err = expClient.DeleteTask(ctx, task.OwnerName, task.ID) + err = deps.coderClient.DeleteTask(ctx, task.OwnerName, task.ID) if err != nil { return codersdk.Response{}, xerrors.Errorf("delete task: %w", err) } @@ -1988,8 +1985,7 @@ var ListTasks = Tool[ListTasksArgs, ListTasksResponse]{ args.User = codersdk.Me } - expClient := codersdk.NewExperimentalClient(deps.coderClient) - tasks, err := expClient.Tasks(ctx, &codersdk.TasksFilter{ + tasks, err := deps.coderClient.Tasks(ctx, &codersdk.TasksFilter{ Owner: args.User, Status: args.Status, }) @@ -2032,9 +2028,7 @@ var GetTaskStatus = Tool[GetTaskStatusArgs, GetTaskStatusResponse]{ return GetTaskStatusResponse{}, xerrors.New("task_id is required") } - expClient := codersdk.NewExperimentalClient(deps.coderClient) - - task, err := expClient.TaskByIdentifier(ctx, args.TaskID) + task, err := deps.coderClient.TaskByIdentifier(ctx, args.TaskID) if err != nil { return GetTaskStatusResponse{}, xerrors.Errorf("resolve task %q: %w", args.TaskID, err) } @@ -2079,14 +2073,12 @@ var SendTaskInput = Tool[SendTaskInputArgs, codersdk.Response]{ return codersdk.Response{}, xerrors.New("input is required") } - expClient := codersdk.NewExperimentalClient(deps.coderClient) - - task, err := expClient.TaskByIdentifier(ctx, args.TaskID) + task, err := deps.coderClient.TaskByIdentifier(ctx, args.TaskID) if err != nil { return codersdk.Response{}, xerrors.Errorf("resolve task %q: %w", args.TaskID, err) } - err = expClient.TaskSend(ctx, task.OwnerName, task.ID, codersdk.TaskSendRequest{ + err = deps.coderClient.TaskSend(ctx, task.OwnerName, task.ID, codersdk.TaskSendRequest{ Input: args.Input, }) if err != nil { @@ -2123,14 +2115,12 @@ var GetTaskLogs = Tool[GetTaskLogsArgs, codersdk.TaskLogsResponse]{ return codersdk.TaskLogsResponse{}, xerrors.New("task_id is required") } - expClient := codersdk.NewExperimentalClient(deps.coderClient) - - task, err := expClient.TaskByIdentifier(ctx, args.TaskID) + task, err := deps.coderClient.TaskByIdentifier(ctx, args.TaskID) if err != nil { return codersdk.TaskLogsResponse{}, err } - logs, err := expClient.TaskLogs(ctx, task.OwnerName, task.ID) + logs, err := deps.coderClient.TaskLogs(ctx, task.OwnerName, task.ID) if err != nil { return codersdk.TaskLogsResponse{}, xerrors.Errorf("get task logs %q: %w", args.TaskID, err) } diff --git a/codersdk/toolsdk/toolsdk_test.go b/codersdk/toolsdk/toolsdk_test.go index 37c4383b87402..f69bcc4d0e7fe 100644 --- a/codersdk/toolsdk/toolsdk_test.go +++ b/codersdk/toolsdk/toolsdk_test.go @@ -1025,11 +1025,8 @@ func TestTools(t *testing.T) { coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) template := coderdtest.CreateTemplate(t, client, owner.OrganizationID, version.ID) - expClient := codersdk.NewExperimentalClient(client) - taskExpClient := codersdk.NewExperimentalClient(taskClient) - // This task should not show up since listing is user-scoped. - _, err := expClient.CreateTask(ctx, member.Username, codersdk.CreateTaskRequest{ + _, err := client.CreateTask(ctx, member.Username, codersdk.CreateTaskRequest{ TemplateVersionID: template.ActiveVersionID, Input: "task for member", Name: "list-task-workspace-member", @@ -1039,7 +1036,7 @@ func TestTools(t *testing.T) { // Create tasks for taskUser. These should show up in the list. for i := range 5 { taskName := fmt.Sprintf("list-task-workspace-%d", i) - task, err := taskExpClient.CreateTask(ctx, codersdk.Me, codersdk.CreateTaskRequest{ + task, err := taskClient.CreateTask(ctx, codersdk.Me, codersdk.CreateTaskRequest{ TemplateVersionID: template.ActiveVersionID, Input: fmt.Sprintf("task %d", i), Name: taskName, diff --git a/docs/reference/api/experimental.md b/docs/reference/api/experimental.md index 5f445a253aaed..a03521e22915a 100644 --- a/docs/reference/api/experimental.md +++ b/docs/reference/api/experimental.md @@ -6,12 +6,12 @@ ```shell # Example request using curl -curl -X GET http://coder-server:8080/api/v2/api/experimental/tasks \ - -H 'Accept: */*' \ +curl -X GET http://coder-server:8080/api/v2/tasks \ + -H 'Accept: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`GET /api/experimental/tasks` +`GET /tasks` ### Parameters @@ -23,6 +23,58 @@ curl -X GET http://coder-server:8080/api/v2/api/experimental/tasks \ > 200 Response +```json +{ + "count": 0, + "tasks": [ + { + "created_at": "2019-08-24T14:15:22Z", + "current_state": { + "message": "string", + "state": "working", + "timestamp": "2019-08-24T14:15:22Z", + "uri": "string" + }, + "display_name": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initial_prompt": "string", + "name": "string", + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "owner_avatar_url": "string", + "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", + "owner_name": "string", + "status": "pending", + "template_display_name": "string", + "template_icon": "string", + "template_id": "c6d67e98-83ea-49f0-8812-e4abae2b68bc", + "template_name": "string", + "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", + "updated_at": "2019-08-24T14:15:22Z", + "workspace_agent_health": { + "healthy": false, + "reason": "agent has lost connection" + }, + "workspace_agent_id": { + "uuid": "string", + "valid": true + }, + "workspace_agent_lifecycle": "created", + "workspace_app_id": { + "uuid": "string", + "valid": true + }, + "workspace_build_number": 0, + "workspace_id": { + "uuid": "string", + "valid": true + }, + "workspace_name": "string", + "workspace_status": "pending" + } + ] +} +``` + ### Responses | Status | Meaning | Description | Schema | @@ -37,13 +89,13 @@ To perform this operation, you must be authenticated. [Learn more](authenticatio ```shell # Example request using curl -curl -X POST http://coder-server:8080/api/v2/api/experimental/tasks/{user} \ +curl -X POST http://coder-server:8080/api/v2/tasks/{user} \ -H 'Content-Type: application/json' \ - -H 'Accept: */*' \ + -H 'Accept: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`POST /api/experimental/tasks/{user}` +`POST /tasks/{user}` > Body parameter @@ -68,6 +120,53 @@ curl -X POST http://coder-server:8080/api/v2/api/experimental/tasks/{user} \ > 201 Response +```json +{ + "created_at": "2019-08-24T14:15:22Z", + "current_state": { + "message": "string", + "state": "working", + "timestamp": "2019-08-24T14:15:22Z", + "uri": "string" + }, + "display_name": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initial_prompt": "string", + "name": "string", + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "owner_avatar_url": "string", + "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", + "owner_name": "string", + "status": "pending", + "template_display_name": "string", + "template_icon": "string", + "template_id": "c6d67e98-83ea-49f0-8812-e4abae2b68bc", + "template_name": "string", + "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", + "updated_at": "2019-08-24T14:15:22Z", + "workspace_agent_health": { + "healthy": false, + "reason": "agent has lost connection" + }, + "workspace_agent_id": { + "uuid": "string", + "valid": true + }, + "workspace_agent_lifecycle": "created", + "workspace_app_id": { + "uuid": "string", + "valid": true + }, + "workspace_build_number": 0, + "workspace_id": { + "uuid": "string", + "valid": true + }, + "workspace_name": "string", + "workspace_status": "pending" +} +``` + ### Responses | Status | Meaning | Description | Schema | @@ -76,18 +175,18 @@ curl -X POST http://coder-server:8080/api/v2/api/experimental/tasks/{user} \ To perform this operation, you must be authenticated. [Learn more](authentication.md). -## Get AI task by ID +## Get AI task by ID or name ### Code samples ```shell # Example request using curl -curl -X GET http://coder-server:8080/api/v2/api/experimental/tasks/{user}/{task} \ - -H 'Accept: */*' \ +curl -X GET http://coder-server:8080/api/v2/tasks/{user}/{task} \ + -H 'Accept: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`GET /api/experimental/tasks/{user}/{task}` +`GET /tasks/{user}/{task}` ### Parameters @@ -100,6 +199,53 @@ curl -X GET http://coder-server:8080/api/v2/api/experimental/tasks/{user}/{task} > 200 Response +```json +{ + "created_at": "2019-08-24T14:15:22Z", + "current_state": { + "message": "string", + "state": "working", + "timestamp": "2019-08-24T14:15:22Z", + "uri": "string" + }, + "display_name": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initial_prompt": "string", + "name": "string", + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "owner_avatar_url": "string", + "owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05", + "owner_name": "string", + "status": "pending", + "template_display_name": "string", + "template_icon": "string", + "template_id": "c6d67e98-83ea-49f0-8812-e4abae2b68bc", + "template_name": "string", + "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", + "updated_at": "2019-08-24T14:15:22Z", + "workspace_agent_health": { + "healthy": false, + "reason": "agent has lost connection" + }, + "workspace_agent_id": { + "uuid": "string", + "valid": true + }, + "workspace_agent_lifecycle": "created", + "workspace_app_id": { + "uuid": "string", + "valid": true + }, + "workspace_build_number": 0, + "workspace_id": { + "uuid": "string", + "valid": true + }, + "workspace_name": "string", + "workspace_status": "pending" +} +``` + ### Responses | Status | Meaning | Description | Schema | @@ -108,17 +254,17 @@ curl -X GET http://coder-server:8080/api/v2/api/experimental/tasks/{user}/{task} To perform this operation, you must be authenticated. [Learn more](authentication.md). -## Delete AI task by ID +## Delete AI task ### Code samples ```shell # Example request using curl -curl -X DELETE http://coder-server:8080/api/v2/api/experimental/tasks/{user}/{task} \ +curl -X DELETE http://coder-server:8080/api/v2/tasks/{user}/{task} \ -H 'Coder-Session-Token: API_KEY' ``` -`DELETE /api/experimental/tasks/{user}/{task}` +`DELETE /tasks/{user}/{task}` ### Parameters @@ -129,9 +275,9 @@ curl -X DELETE http://coder-server:8080/api/v2/api/experimental/tasks/{user}/{ta ### Responses -| Status | Meaning | Description | Schema | -|--------|---------------------------------------------------------------|-------------------------|--------| -| 202 | [Accepted](https://tools.ietf.org/html/rfc7231#section-6.3.3) | Task deletion initiated | | +| Status | Meaning | Description | Schema | +|--------|---------------------------------------------------------------|-------------|--------| +| 202 | [Accepted](https://tools.ietf.org/html/rfc7231#section-6.3.3) | Accepted | | To perform this operation, you must be authenticated. [Learn more](authentication.md). @@ -141,12 +287,12 @@ To perform this operation, you must be authenticated. [Learn more](authenticatio ```shell # Example request using curl -curl -X PATCH http://coder-server:8080/api/v2/api/experimental/tasks/{user}/{task}/input \ +curl -X PATCH http://coder-server:8080/api/v2/tasks/{user}/{task}/input \ -H 'Content-Type: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`PATCH /api/experimental/tasks/{user}/{task}/input` +`PATCH /tasks/{user}/{task}/input` > Body parameter @@ -178,12 +324,12 @@ To perform this operation, you must be authenticated. [Learn more](authenticatio ```shell # Example request using curl -curl -X GET http://coder-server:8080/api/v2/api/experimental/tasks/{user}/{task}/logs \ - -H 'Accept: */*' \ +curl -X GET http://coder-server:8080/api/v2/tasks/{user}/{task}/logs \ + -H 'Accept: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`GET /api/experimental/tasks/{user}/{task}/logs` +`GET /tasks/{user}/{task}/logs` ### Parameters @@ -196,6 +342,19 @@ curl -X GET http://coder-server:8080/api/v2/api/experimental/tasks/{user}/{task} > 200 Response +```json +{ + "logs": [ + { + "content": "string", + "id": 0, + "time": "2019-08-24T14:15:22Z", + "type": "input" + } + ] +} +``` + ### Responses | Status | Meaning | Description | Schema | @@ -210,12 +369,12 @@ To perform this operation, you must be authenticated. [Learn more](authenticatio ```shell # Example request using curl -curl -X POST http://coder-server:8080/api/v2/api/experimental/tasks/{user}/{task}/send \ +curl -X POST http://coder-server:8080/api/v2/tasks/{user}/{task}/send \ -H 'Content-Type: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`POST /api/experimental/tasks/{user}/{task}/send` +`POST /tasks/{user}/{task}/send` > Body parameter @@ -235,8 +394,8 @@ curl -X POST http://coder-server:8080/api/v2/api/experimental/tasks/{user}/{task ### Responses -| Status | Meaning | Description | Schema | -|--------|-----------------------------------------------------------------|-------------------------|--------| -| 204 | [No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5) | Input sent successfully | | +| 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/site/src/api/api.ts b/site/src/api/api.ts index 438f059c53414..19c548d067a3d 100644 --- a/site/src/api/api.ts +++ b/site/src/api/api.ts @@ -2666,7 +2666,7 @@ class ExperimentalApiMethods { req: TypesGen.CreateTaskRequest, ): Promise => { const response = await this.axios.post( - `/api/experimental/tasks/${user}`, + `/api/v2/tasks/${user}`, req, ); @@ -2685,7 +2685,7 @@ class ExperimentalApiMethods { } const res = await this.axios.get( - "/api/experimental/tasks", + "/api/v2/tasks", { params: { q: query.join(", "), @@ -2698,14 +2698,14 @@ class ExperimentalApiMethods { getTask = async (user: string, id: string): Promise => { const response = await this.axios.get( - `/api/experimental/tasks/${user}/${id}`, + `/api/v2/tasks/${user}/${id}`, ); return response.data; }; deleteTask = async (user: string, id: string): Promise => { - await this.axios.delete(`/api/experimental/tasks/${user}/${id}`); + await this.axios.delete(`/api/v2/tasks/${user}/${id}`); }; createTaskFeedback = async ( From 4d80a83d252f86208162d034f3d82b048aed95f1 Mon Sep 17 00:00:00 2001 From: Danielle Maywood Date: Tue, 25 Nov 2025 14:41:09 +0000 Subject: [PATCH 2/3] chore: a few more experimentals --- codersdk/aitasks.go | 46 +++------------------------------------------ 1 file changed, 3 insertions(+), 43 deletions(-) diff --git a/codersdk/aitasks.go b/codersdk/aitasks.go index 12079e76da71e..e2acbfe4897c3 100644 --- a/codersdk/aitasks.go +++ b/codersdk/aitasks.go @@ -28,8 +28,6 @@ import ( const AITaskPromptParameterName = provider.TaskPromptParameterName // CreateTaskRequest represents the request to create a new task. -// -// Experimental: This type is experimental and may change in the future. type CreateTaskRequest struct { TemplateVersionID uuid.UUID `json:"template_version_id" format:"uuid"` TemplateVersionPresetID uuid.UUID `json:"template_version_preset_id,omitempty" format:"uuid"` @@ -39,8 +37,6 @@ type CreateTaskRequest struct { } // CreateTask creates a new task. -// -// Experimental: This method is experimental and may change in the future. func (c *Client) CreateTask(ctx context.Context, user string, request CreateTaskRequest) (Task, error) { res, err := c.Request(ctx, http.MethodPost, fmt.Sprintf("/api/v2/tasks/%s", user), request) if err != nil { @@ -61,8 +57,6 @@ func (c *Client) CreateTask(ctx context.Context, user string, request CreateTask } // TaskStatus represents the status of a task. -// -// Experimental: This type is experimental and may change in the future. type TaskStatus string const ( @@ -99,8 +93,6 @@ func AllTaskStatuses() []TaskStatus { } // TaskState represents the high-level lifecycle of a task. -// -// Experimental: This type is experimental and may change in the future. type TaskState string // TaskState enums. @@ -120,8 +112,6 @@ const ( ) // Task represents a task. -// -// Experimental: This type is experimental and may change in the future. type Task struct { ID uuid.UUID `json:"id" format:"uuid" table:"id"` OrganizationID uuid.UUID `json:"organization_id" format:"uuid" table:"organization id"` @@ -151,8 +141,6 @@ type Task struct { } // TaskStateEntry represents a single entry in the task's state history. -// -// Experimental: This type is experimental and may change in the future. type TaskStateEntry struct { Timestamp time.Time `json:"timestamp" format:"date-time" table:"-"` State TaskState `json:"state" enum:"working,idle,completed,failed" table:"state"` @@ -161,8 +149,6 @@ type TaskStateEntry struct { } // TasksFilter filters the list of tasks. -// -// Experimental: This type is experimental and may change in the future. type TasksFilter struct { // Owner can be a username, UUID, or "me". Owner string `json:"owner,omitempty"` @@ -175,8 +161,6 @@ type TasksFilter struct { } // TaskListResponse is the response shape for tasks list. -// -// Experimental response shape for tasks list (server returns []Task). type TasksListResponse struct { Tasks []Task `json:"tasks"` Count int `json:"count"` @@ -208,8 +192,6 @@ func (f TasksFilter) asRequestOption() RequestOption { } // Tasks lists all tasks belonging to the user or specified owner. -// -// Experimental: This method is experimental and may change in the future. func (c *Client) Tasks(ctx context.Context, filter *TasksFilter) ([]Task, error) { if filter == nil { filter = &TasksFilter{} @@ -232,10 +214,8 @@ func (c *Client) Tasks(ctx context.Context, filter *TasksFilter) ([]Task, error) return tres.Tasks, nil } -// TaskByID fetches a single experimental task by its ID. +// TaskByID fetches a single task by its ID. // Only tasks owned by codersdk.Me are supported. -// -// Experimental: This method is experimental and may change in the future. func (c *Client) TaskByID(ctx context.Context, id uuid.UUID) (Task, error) { res, err := c.Request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/tasks/%s/%s", "me", id.String()), nil) if err != nil { @@ -254,9 +234,7 @@ func (c *Client) TaskByID(ctx context.Context, id uuid.UUID) (Task, error) { return task, nil } -// TaskByOwnerAndName fetches a single experimental task by its owner and name. -// -// Experimental: This method is experimental and may change in the future. +// TaskByOwnerAndName fetches a single task by its owner and name. func (c *Client) TaskByOwnerAndName(ctx context.Context, owner, ident string) (Task, error) { if owner == "" { owner = Me @@ -318,8 +296,6 @@ func (c *Client) TaskByIdentifier(ctx context.Context, identifier string) (Task, } // DeleteTask deletes a task by its ID. -// -// Experimental: This method is experimental and may change in the future. func (c *Client) DeleteTask(ctx context.Context, user string, id uuid.UUID) error { res, err := c.Request(ctx, http.MethodDelete, fmt.Sprintf("/api/v2/tasks/%s/%s", user, id.String()), nil) if err != nil { @@ -333,15 +309,11 @@ func (c *Client) DeleteTask(ctx context.Context, user string, id uuid.UUID) erro } // TaskSendRequest is used to send task input to the tasks sidebar app. -// -// Experimental: This type is experimental and may change in the future. type TaskSendRequest struct { Input string `json:"input"` } // TaskSend submits task input to the tasks sidebar app. -// -// Experimental: This method is experimental and may change in the future. func (c *Client) TaskSend(ctx context.Context, user string, id uuid.UUID, req TaskSendRequest) error { res, err := c.Request(ctx, http.MethodPost, fmt.Sprintf("/api/v2/tasks/%s/%s/send", user, id.String()), req) if err != nil { @@ -355,15 +327,11 @@ func (c *Client) TaskSend(ctx context.Context, user string, id uuid.UUID, req Ta } // UpdateTaskInputRequest is used to update a task's input. -// -// Experimental: This type is experimental and may change in the future. type UpdateTaskInputRequest struct { Input string `json:"input"` } // UpdateTaskInput updates the task's input. -// -// Experimental: This method is experimental and may change in the future. func (c *Client) UpdateTaskInput(ctx context.Context, user string, id uuid.UUID, req UpdateTaskInputRequest) error { res, err := c.Request(ctx, http.MethodPatch, fmt.Sprintf("/api/v2/tasks/%s/%s/input", user, id.String()), req) if err != nil { @@ -377,8 +345,6 @@ func (c *Client) UpdateTaskInput(ctx context.Context, user string, id uuid.UUID, } // TaskLogType indicates the source of a task log entry. -// -// Experimental: This type is experimental and may change in the future. type TaskLogType string // TaskLogType enums. @@ -388,8 +354,6 @@ const ( ) // TaskLogEntry represents a single log entry for a task. -// -// Experimental: This type is experimental and may change in the future. type TaskLogEntry struct { ID int `json:"id" table:"id"` Content string `json:"content" table:"content"` @@ -398,15 +362,11 @@ type TaskLogEntry struct { } // TaskLogsResponse contains the logs for a task. -// -// Experimental: This type is experimental and may change in the future. type TaskLogsResponse struct { Logs []TaskLogEntry `json:"logs"` } -// TaskLogs retrieves logs from the task's sidebar app via the experimental API. -// -// Experimental: This method is experimental and may change in the future. +// TaskLogs retrieves logs from the task app. func (c *Client) TaskLogs(ctx context.Context, user string, id uuid.UUID) (TaskLogsResponse, error) { res, err := c.Request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/tasks/%s/%s/logs", user, id.String()), nil) if err != nil { From db6ce57d6daf8939de5c029a0fbae98c8df137ff Mon Sep 17 00:00:00 2001 From: Danielle Maywood Date: Tue, 25 Nov 2025 14:55:56 +0000 Subject: [PATCH 3/3] chore: oops, forgot to run make gen --- site/src/api/typesGenerated.ts | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 8a8eafa945d39..5597abae9eacc 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -1195,8 +1195,6 @@ export interface CreateProvisionerKeyResponse { // From codersdk/aitasks.go /** * CreateTaskRequest represents the request to create a new task. - * - * Experimental: This type is experimental and may change in the future. */ export interface CreateTaskRequest { readonly template_version_id: string; @@ -4723,8 +4721,6 @@ export interface TailDERPRegion { // From codersdk/aitasks.go /** * Task represents a task. - * - * Experimental: This type is experimental and may change in the future. */ export interface Task { readonly id: string; @@ -4757,8 +4753,6 @@ export interface Task { // From codersdk/aitasks.go /** * TaskLogEntry represents a single log entry for a task. - * - * Experimental: This type is experimental and may change in the future. */ export interface TaskLogEntry { readonly id: number; @@ -4775,8 +4769,6 @@ export const TaskLogTypes: TaskLogType[] = ["input", "output"]; // From codersdk/aitasks.go /** * TaskLogsResponse contains the logs for a task. - * - * Experimental: This type is experimental and may change in the future. */ export interface TaskLogsResponse { readonly logs: readonly TaskLogEntry[]; @@ -4785,8 +4777,6 @@ export interface TaskLogsResponse { // From codersdk/aitasks.go /** * TaskSendRequest is used to send task input to the tasks sidebar app. - * - * Experimental: This type is experimental and may change in the future. */ export interface TaskSendRequest { readonly input: string; @@ -4798,8 +4788,6 @@ export type TaskState = "complete" | "failed" | "idle" | "working"; // From codersdk/aitasks.go /** * TaskStateEntry represents a single entry in the task's state history. - * - * Experimental: This type is experimental and may change in the future. */ export interface TaskStateEntry { readonly timestamp: string; @@ -4836,8 +4824,6 @@ export const TaskStatuses: TaskStatus[] = [ // From codersdk/aitasks.go /** * TasksFilter filters the list of tasks. - * - * Experimental: This type is experimental and may change in the future. */ export interface TasksFilter { /** @@ -4861,8 +4847,6 @@ export interface TasksFilter { // From codersdk/aitasks.go /** * TaskListResponse is the response shape for tasks list. - * - * Experimental response shape for tasks list (server returns []Task). */ export interface TasksListResponse { readonly tasks: readonly Task[]; @@ -5376,8 +5360,6 @@ export interface UpdateRoles { // From codersdk/aitasks.go /** * UpdateTaskInputRequest is used to update a task's input. - * - * Experimental: This type is experimental and may change in the future. */ export interface UpdateTaskInputRequest { readonly input: string;