Skip to content

Agent Sessions Not Cleaned Up When Tasks Are Deleted #737

@DanielVisca

Description

@DanielVisca

Issue: Agent Sessions Not Cleaned Up When Tasks Are Deleted

Problem

Resource Leak: When a user deletes a task, the associated agent session remains active, causing:

  • Cloud sessions: Polling continues forever fetching from deleted task's S3 URLs
  • Local sessions: tRPC subscriptions stay active, agent processes keep running
  • Memory leaks: Session objects accumulate in session store
  • Performance degradation over time

Root Cause

Missing cleanup in task deletion flow:

// Current flow in useDeleteTask() - BROKEN
return client.deleteTask(taskId);  // ✅ Deletes task
// ❌ MISSING: Clean up associated sessions

Tasks and sessions have 1-to-many relationship but only 1-way cleanup.

Fix Location

File: apps/twig/src/renderer/features/tasks/hooks/useTasks.ts
Function: useDeleteTask()mutation.onMutate

Exact Fix

Add session cleanup in the onMutate callback:

export function useDeleteTask() {
  const queryClient = useQueryClient();
  const { view, navigateToTaskInput } = useNavigationStore();
  const unpinTask = usePinnedTasksStore((state) => state.unpin);
  const sessions = useSessions();                    // ✅ Hook at top level
  const sessionActions = useSessionActions();        // ✅ Hook at top level

  const mutation = useAuthenticatedMutation(
    async (client, taskId: string) => {
      // ... existing workspace cleanup ...
      return client.deleteTask(taskId);
    },
    {
      onMutate: async (taskId) => {
        // ✅ NEW: Clean up session resources when task is deleted
        const activeSession = Object.values(sessions).find((s) => s.taskId === taskId);
        if (activeSession) {
          log.info("Cleaning up session during task deletion", { 
            taskRunId: activeSession.taskRunId, 
            taskId 
          });
          
          // ✅ This handles both cloud and local sessions
          await sessionActions.disconnectFromTask(taskId);
        }
        
        // ... existing optimistic update logic ...
      },
    },
  );
}

Why This Fix Works

  1. disconnectFromTask() properly handles:

    • Cloud sessions: Stops polling intervals via stopCloudPolling()
    • Local sessions: Cancels agent via agent.cancel.mutate() + unsubscribes
  2. Timing: Runs in onMutate before API call, ensuring cleanup even if API fails

  3. Scope: Uses taskId to find all sessions for that task (handles 1-to-many)

Validation Steps

Before Fix (Reproduce Issue):

  1. Create task + start agent session
  2. Add debug log to startCloudPolling(): console.log('🔄 POLLING:', taskRunId)
  3. Delete task
  4. Observe: Polling continues forever

After Fix (Verify Fix):

  1. Repeat same steps
  2. Delete task
  3. Observe: Polling stops immediately, no more console messages

Impact

  • Lines changed: ~10 lines
  • Risk: Low (only adds cleanup, no breaking changes)
  • Benefit: High (fixes resource leaks, improves performance)

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions