Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions cli/testdata/coder_list_--output_json.golden
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@
"most_recently_seen": null
},
"template_version_preset_id": null,
"has_ai_task": false,
"has_external_agent": false
},
"latest_app_status": null,
Expand All @@ -90,7 +89,6 @@
"allow_renames": false,
"favorite": false,
"next_start_at": "====[timestamp]=====",
"is_prebuild": false,
"task_id": null
"is_prebuild": false
}
]
5 changes: 0 additions & 5 deletions cli/testdata/server-config.yaml.golden
Original file line number Diff line number Diff line change
Expand Up @@ -809,11 +809,6 @@ workspace_prebuilds:
# limit; disabled when set to zero.
# (default: 3, type: int)
failure_hard_limit: 3
# Enable Coder Tasks. When unset, the Tasks routes are not served, the Tasks UI
# and its URLs are unavailable, the task RBAC permissions are stripped from
# built-in roles, and the CLI task commands are hidden.
# (default: false, type: bool)
enableAITasks: false
# Configure the background chat processing daemon.
chat:
# How many pending chats a worker should acquire per polling cycle.
Expand Down
111 changes: 0 additions & 111 deletions coderd/agentapi/apps.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,9 +209,6 @@ func (a *AppsAPI) UpdateAppStatus(ctx context.Context, req *agentproto.UpdateApp
}
}

// Notify on state change to Working/Idle for AI tasks.
a.enqueueAITaskStateNotification(ctx, app.ID, latestAppStatus, dbState)

if shouldBump(dbState, latestAppStatus) {
// We pass time.Time{} for nextAutostart since we don't have access to
// TemplateScheduleStore here. The activity bump logic handles this by
Expand Down Expand Up @@ -241,111 +238,3 @@ func shouldBump(dbState database.WorkspaceAppStatusState, latestAppStatus databa
}
return false
}

// enqueueAITaskStateNotification enqueues a notification when an AI task's app
// transitions to Working or Idle.
// No-op if:
// - the workspace agent app isn't configured as an AI task,
// - the new state equals the latest persisted state,
// - the workspace agent is not ready (still starting up).
func (a *AppsAPI) enqueueAITaskStateNotification(
ctx context.Context,
appID uuid.UUID,
latestAppStatus database.WorkspaceAppStatus,
newAppStatus database.WorkspaceAppStatusState,
) {
var notificationTemplate uuid.UUID
switch newAppStatus {
case database.WorkspaceAppStatusStateWorking:
notificationTemplate = notifications.TemplateTaskWorking
case database.WorkspaceAppStatusStateIdle:
notificationTemplate = notifications.TemplateTaskIdle
case database.WorkspaceAppStatusStateComplete:
notificationTemplate = notifications.TemplateTaskCompleted
case database.WorkspaceAppStatusStateFailure:
notificationTemplate = notifications.TemplateTaskFailed
default:
// Not a notifiable state, do nothing
return
}

taskID := a.Workspace.TaskID()
if !taskID.Valid {
// Workspace has no task ID, do nothing.
return
}

// Only fetch fresh agent state for task workspaces, since we need
// the current lifecycle state to decide whether to send notifications.
agent, err := a.AgentFn(ctx)
if err != nil {
a.Log.Warn(ctx, "failed to get agent for AI task notification", slog.Error(err))
return
}

// Only send notifications when the agent is ready. We want to skip
// any state transitions that occur whilst the workspace is starting
// up as it doesn't make sense to receive them.
if agent.LifecycleState != database.WorkspaceAgentLifecycleStateReady {
a.Log.Debug(ctx, "skipping AI task notification because agent is not ready",
slog.F("agent_id", agent.ID),
slog.F("lifecycle_state", agent.LifecycleState),
slog.F("new_app_status", newAppStatus),
)
return
}

task, err := a.Database.GetTaskByID(ctx, taskID.UUID)
if err != nil {
a.Log.Warn(ctx, "failed to get task", slog.Error(err))
return
}

if !task.WorkspaceAppID.Valid || task.WorkspaceAppID.UUID != appID {
// Non-task app, do nothing.
return
}

// Skip if the latest persisted state equals the new state (no new transition)
// Note: uuid.Nil check is valid here. If no previous status exists,
// GetLatestWorkspaceAppStatusByAppID returns sql.ErrNoRows and we get a zero-value struct.
if latestAppStatus.ID != uuid.Nil && latestAppStatus.State == newAppStatus {
return
}

// Skip the initial "Working" notification when the task first starts.
// This is obvious to the user since they just created the task.
// We still notify on the first "Idle" status and all subsequent transitions.
if latestAppStatus.ID == uuid.Nil && newAppStatus == database.WorkspaceAppStatusStateWorking {
return
}

ws, ok := a.Workspace.AsWorkspaceIdentity()
if !ok {
a.Log.Warn(ctx, "failed to get workspace identity for AI task notification")
return
}

if _, err := a.NotificationsEnqueuer.EnqueueWithData(
// nolint:gocritic // Need notifier actor to enqueue notifications
dbauthz.AsNotifier(ctx),
ws.OwnerID,
notificationTemplate,
map[string]string{
"task": task.Name,
"workspace": ws.Name,
},
map[string]any{
// Use a 1-minute bucketed timestamp to bypass per-day dedupe,
// allowing identical content to resend within the same day
// (but not more than once every 10s).
"dedupe_bypass_ts": a.Clock.Now().UTC().Truncate(time.Minute),
},
"api-workspace-agent-app-status",
// Associate this notification with related entities
ws.ID, ws.OwnerID, ws.OrganizationID, appID,
); err != nil {
a.Log.Warn(ctx, "failed to notify of task state", slog.Error(err))
return
}
}
15 changes: 0 additions & 15 deletions coderd/agentapi/apps_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import (
"github.com/coder/coder/v2/coderd/agentapi"
"github.com/coder/coder/v2/coderd/database"
"github.com/coder/coder/v2/coderd/database/dbmock"
"github.com/coder/coder/v2/coderd/notifications"
"github.com/coder/coder/v2/coderd/notifications/notificationstest"
"github.com/coder/coder/v2/coderd/wspubsub"
"github.com/coder/coder/v2/codersdk"
Expand Down Expand Up @@ -269,10 +268,6 @@ func TestWorkspaceAgentAppStatus(t *testing.T) {

workspace := database.Workspace{
ID: uuid.UUID{9},
TaskID: uuid.NullUUID{
Valid: true,
UUID: uuid.UUID{7},
},
}
cachedWs := &agentapi.CachedWorkspaceFields{}
cachedWs.UpdateValues(workspace)
Expand Down Expand Up @@ -301,14 +296,6 @@ func TestWorkspaceAgentAppStatus(t *testing.T) {
AgentID: agent.ID,
Slug: "vscode",
}).Times(1).Return(app, nil)
task := database.Task{
ID: uuid.UUID{7},
WorkspaceAppID: uuid.NullUUID{
Valid: true,
UUID: app.ID,
},
}
mDB.EXPECT().GetTaskByID(gomock.Any(), task.ID).Times(1).Return(task, nil)
appStatus := database.WorkspaceAppStatus{
ID: uuid.UUID{6},
}
Expand Down Expand Up @@ -336,8 +323,6 @@ func TestWorkspaceAgentAppStatus(t *testing.T) {

kind := testutil.RequireReceive(ctx, t, workspaceUpdates)
require.Equal(t, wspubsub.WorkspaceEventKindAgentAppStatusUpdate, kind)
sent := fEnq.Sent(notificationstest.WithTemplateID(notifications.TemplateTaskCompleted))
require.Len(t, sent, 1)
})

t.Run("FailUnknownApp", func(t *testing.T) {
Expand Down
10 changes: 0 additions & 10 deletions coderd/agentapi/cached_workspace.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"context"
"sync"

"github.com/google/uuid"
"golang.org/x/xerrors"

"github.com/coder/coder/v2/coderd/database"
Expand All @@ -24,14 +23,12 @@ type CachedWorkspaceFields struct {
lock sync.RWMutex

identity database.WorkspaceIdentity
taskID uuid.NullUUID
}

func (cws *CachedWorkspaceFields) Clear() {
cws.lock.Lock()
defer cws.lock.Unlock()
cws.identity = database.WorkspaceIdentity{}
cws.taskID = uuid.NullUUID{}
}

func (cws *CachedWorkspaceFields) UpdateValues(ws database.Workspace) {
Expand All @@ -45,13 +42,6 @@ func (cws *CachedWorkspaceFields) UpdateValues(ws database.Workspace) {
cws.identity.OwnerUsername = ws.OwnerUsername
cws.identity.TemplateName = ws.TemplateName
cws.identity.AutostartSchedule = ws.AutostartSchedule
cws.taskID = ws.TaskID
}

func (cws *CachedWorkspaceFields) TaskID() uuid.NullUUID {
cws.lock.RLock()
defer cws.lock.RUnlock()
return cws.taskID
}

// Returns the Workspace, true, unless the workspace has not been cached (nuked or was a prebuild).
Expand Down
5 changes: 0 additions & 5 deletions coderd/aiseats/aiseats.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,6 @@ func ReasonAIBridge(description string) Reason {
return Reason{EventType: database.AISeatUsageReasonAibridge, Description: description}
}

// ReasonTask constructs a reason for usage originating from tasks.
func ReasonTask(description string) Reason {
return Reason{EventType: database.AISeatUsageReasonTask, Description: description}
}

// SeatTracker records AI seat consumption state.
type SeatTracker interface {
// RecordUsage does not return an error to prevent blocking the user from using
Expand Down
Loading
Loading