From 43765864e570fb8089fa0cf606da15b389933e86 Mon Sep 17 00:00:00 2001 From: Danielle Maywood Date: Thu, 28 Aug 2025 17:28:47 +0100 Subject: [PATCH] chore: add fields to codersdk.Task (#19619) Closes https://github.com/coder/internal/issues/949 Adds the following fields to `codersdk.Task` - OwnerName - TemplateName - TemplateDisplayName - TemplateIcon - WorkspaceAgentID - WorkspaceAgentLifecycle - WorkspaceAgentHealth The implementation is unfortunately not compatible with multiple agents as we have no reliable way to tell which agent has the AI task running in it. For now we just pick the first agent found, but in the future this will need to be changed. --- cli/exp_task_status_test.go | 7 ++++++ coderd/aitasks.go | 46 ++++++++++++++++++++++++++-------- codersdk/aitasks.go | 29 +++++++++++++-------- site/src/api/typesGenerated.ts | 7 ++++++ 4 files changed, 67 insertions(+), 22 deletions(-) diff --git a/cli/exp_task_status_test.go b/cli/exp_task_status_test.go index 6aa52ff388..6631980ac1 100644 --- a/cli/exp_task_status_test.go +++ b/cli/exp_task_status_test.go @@ -188,9 +188,16 @@ STATE CHANGED STATUS STATE MESSAGE "id": "11111111-1111-1111-1111-111111111111", "organization_id": "00000000-0000-0000-0000-000000000000", "owner_id": "00000000-0000-0000-0000-000000000000", + "owner_name": "", "name": "", "template_id": "00000000-0000-0000-0000-000000000000", + "template_name": "", + "template_display_name": "", + "template_icon": "", "workspace_id": null, + "workspace_agent_id": null, + "workspace_agent_lifecycle": null, + "workspace_agent_health": null, "initial_prompt": "", "status": "running", "current_state": { diff --git a/coderd/aitasks.go b/coderd/aitasks.go index 5fb9ceec9a..79b2f74f73 100644 --- a/coderd/aitasks.go +++ b/coderd/aitasks.go @@ -213,6 +213,22 @@ func (api *API) tasksFromWorkspaces(ctx context.Context, apiWorkspaces []codersd tasks := make([]codersdk.Task, 0, len(apiWorkspaces)) for _, ws := range apiWorkspaces { + // TODO(DanielleMaywood): + // This just picks up the first agent it discovers. + // This approach _might_ break when a task has multiple agents, + // depending on which agent was found first. + var taskAgentID uuid.NullUUID + var taskAgentLifecycle *codersdk.WorkspaceAgentLifecycle + var taskAgentHealth *codersdk.WorkspaceAgentHealth + for _, resource := range ws.LatestBuild.Resources { + for _, agent := range resource.Agents { + taskAgentID = uuid.NullUUID{Valid: true, UUID: agent.ID} + taskAgentLifecycle = &agent.LifecycleState + taskAgentHealth = &agent.Health + break + } + } + var currentState *codersdk.TaskStateEntry if ws.LatestAppStatus != nil { currentState = &codersdk.TaskStateEntry{ @@ -222,18 +238,26 @@ func (api *API) tasksFromWorkspaces(ctx context.Context, apiWorkspaces []codersd URI: ws.LatestAppStatus.URI, } } + tasks = append(tasks, codersdk.Task{ - ID: ws.ID, - OrganizationID: ws.OrganizationID, - OwnerID: ws.OwnerID, - Name: ws.Name, - TemplateID: ws.TemplateID, - WorkspaceID: uuid.NullUUID{Valid: true, UUID: ws.ID}, - CreatedAt: ws.CreatedAt, - UpdatedAt: ws.UpdatedAt, - InitialPrompt: promptsByBuildID[ws.LatestBuild.ID], - Status: ws.LatestBuild.Status, - CurrentState: currentState, + ID: ws.ID, + OrganizationID: ws.OrganizationID, + OwnerID: ws.OwnerID, + OwnerName: ws.OwnerName, + Name: ws.Name, + TemplateID: ws.TemplateID, + TemplateName: ws.TemplateName, + TemplateDisplayName: ws.TemplateDisplayName, + TemplateIcon: ws.TemplateIcon, + WorkspaceID: uuid.NullUUID{Valid: true, UUID: ws.ID}, + WorkspaceAgentID: taskAgentID, + WorkspaceAgentLifecycle: taskAgentLifecycle, + WorkspaceAgentHealth: taskAgentHealth, + CreatedAt: ws.CreatedAt, + UpdatedAt: ws.UpdatedAt, + InitialPrompt: promptsByBuildID[ws.LatestBuild.ID], + Status: ws.LatestBuild.Status, + CurrentState: currentState, }) } diff --git a/codersdk/aitasks.go b/codersdk/aitasks.go index d666f63df0..753471e34b 100644 --- a/codersdk/aitasks.go +++ b/codersdk/aitasks.go @@ -88,17 +88,24 @@ const ( // // 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"` - OwnerID uuid.UUID `json:"owner_id" format:"uuid" table:"owner id"` - Name string `json:"name" table:"name,default_sort"` - TemplateID uuid.UUID `json:"template_id" format:"uuid" table:"template id"` - WorkspaceID uuid.NullUUID `json:"workspace_id" format:"uuid" table:"workspace id"` - InitialPrompt string `json:"initial_prompt" table:"initial prompt"` - Status WorkspaceStatus `json:"status" enums:"pending,starting,running,stopping,stopped,failed,canceling,canceled,deleting,deleted" table:"status"` - CurrentState *TaskStateEntry `json:"current_state" table:"cs,recursive_inline"` - CreatedAt time.Time `json:"created_at" format:"date-time" table:"created at"` - UpdatedAt time.Time `json:"updated_at" format:"date-time" table:"updated at"` + ID uuid.UUID `json:"id" format:"uuid" table:"id"` + OrganizationID uuid.UUID `json:"organization_id" format:"uuid" table:"organization id"` + OwnerID uuid.UUID `json:"owner_id" format:"uuid" table:"owner id"` + OwnerName string `json:"owner_name" table:"owner name"` + Name string `json:"name" table:"name,default_sort"` + TemplateID uuid.UUID `json:"template_id" format:"uuid" table:"template id"` + TemplateName string `json:"template_name" table:"template name"` + TemplateDisplayName string `json:"template_display_name" table:"template display name"` + TemplateIcon string `json:"template_icon" table:"template icon"` + WorkspaceID uuid.NullUUID `json:"workspace_id" format:"uuid" table:"workspace id"` + WorkspaceAgentID uuid.NullUUID `json:"workspace_agent_id" format:"uuid" table:"workspace agent id"` + WorkspaceAgentLifecycle *WorkspaceAgentLifecycle `json:"workspace_agent_lifecycle" table:"workspace agent lifecycle"` + WorkspaceAgentHealth *WorkspaceAgentHealth `json:"workspace_agent_health" table:"workspace agent health"` + InitialPrompt string `json:"initial_prompt" table:"initial prompt"` + Status WorkspaceStatus `json:"status" enums:"pending,starting,running,stopping,stopped,failed,canceling,canceled,deleting,deleted" table:"status"` + CurrentState *TaskStateEntry `json:"current_state" table:"cs,recursive_inline"` + CreatedAt time.Time `json:"created_at" format:"date-time" table:"created at"` + UpdatedAt time.Time `json:"updated_at" format:"date-time" table:"updated at"` } // TaskStateEntry represents a single entry in the task's state history. diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index f35dfdb123..54984cd115 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -2812,9 +2812,16 @@ export interface Task { readonly id: string; readonly organization_id: string; readonly owner_id: string; + readonly owner_name: string; readonly name: string; readonly template_id: string; + readonly template_name: string; + readonly template_display_name: string; + readonly template_icon: string; readonly workspace_id: string | null; + readonly workspace_agent_id: string | null; + readonly workspace_agent_lifecycle: WorkspaceAgentLifecycle | null; + readonly workspace_agent_health: WorkspaceAgentHealth | null; readonly initial_prompt: string; readonly status: WorkspaceStatus; readonly current_state: TaskStateEntry | null;