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).
( ctx context.Context, appID uuid.UUID, latestAppStatus database.WorkspaceAppStatus, newAppStatus database.WorkspaceAppStatusState, )
| 249 | // - the new state equals the latest persisted state, |
| 250 | // - the workspace agent is not ready (still starting up). |
| 251 | func (a *AppsAPI) enqueueAITaskStateNotification( |
| 252 | ctx context.Context, |
| 253 | appID uuid.UUID, |
| 254 | latestAppStatus database.WorkspaceAppStatus, |
| 255 | newAppStatus database.WorkspaceAppStatusState, |
| 256 | ) { |
| 257 | var notificationTemplate uuid.UUID |
| 258 | switch newAppStatus { |
| 259 | case database.WorkspaceAppStatusStateWorking: |
| 260 | notificationTemplate = notifications.TemplateTaskWorking |
| 261 | case database.WorkspaceAppStatusStateIdle: |
| 262 | notificationTemplate = notifications.TemplateTaskIdle |
| 263 | case database.WorkspaceAppStatusStateComplete: |
| 264 | notificationTemplate = notifications.TemplateTaskCompleted |
| 265 | case database.WorkspaceAppStatusStateFailure: |
| 266 | notificationTemplate = notifications.TemplateTaskFailed |
| 267 | default: |
| 268 | // Not a notifiable state, do nothing |
| 269 | return |
| 270 | } |
| 271 | |
| 272 | taskID := a.Workspace.TaskID() |
| 273 | if !taskID.Valid { |
| 274 | // Workspace has no task ID, do nothing. |
| 275 | return |
| 276 | } |
| 277 | |
| 278 | // Only fetch fresh agent state for task workspaces, since we need |
| 279 | // the current lifecycle state to decide whether to send notifications. |
| 280 | agent, err := a.AgentFn(ctx) |
| 281 | if err != nil { |
| 282 | a.Log.Warn(ctx, "failed to get agent for AI task notification", slog.Error(err)) |
| 283 | return |
| 284 | } |
| 285 | |
| 286 | // Only send notifications when the agent is ready. We want to skip |
| 287 | // any state transitions that occur whilst the workspace is starting |
| 288 | // up as it doesn't make sense to receive them. |
| 289 | if agent.LifecycleState != database.WorkspaceAgentLifecycleStateReady { |
| 290 | a.Log.Debug(ctx, "skipping AI task notification because agent is not ready", |
| 291 | slog.F("agent_id", agent.ID), |
| 292 | slog.F("lifecycle_state", agent.LifecycleState), |
| 293 | slog.F("new_app_status", newAppStatus), |
| 294 | ) |
| 295 | return |
| 296 | } |
| 297 | |
| 298 | task, err := a.Database.GetTaskByID(ctx, taskID.UUID) |
| 299 | if err != nil { |
| 300 | a.Log.Warn(ctx, "failed to get task", slog.Error(err)) |
| 301 | return |
| 302 | } |
| 303 | |
| 304 | if !task.WorkspaceAppID.Valid || task.WorkspaceAppID.UUID != appID { |
| 305 | // Non-task app, do nothing. |
| 306 | return |
| 307 | } |
| 308 |
no test coverage detected