| 1079 | } |
| 1080 | |
| 1081 | export class TaskService { |
| 1082 | // Serialize stream-end processing per workspace to avoid races when |
| 1083 | // finalizing reported tasks and cleanup state transitions. |
| 1084 | private readonly workspaceEventLocks = new MutexMap<string>(); |
| 1085 | // Separate parent-scoped lock for deferred best-of fallback/finalization. This path can run |
| 1086 | // concurrently from multiple child stream-end handlers for the same parent, and it must remain |
| 1087 | // safe even when the parent stream-end already holds workspaceEventLocks for the parent itself. |
| 1088 | private readonly deferredBestOfLocks = new MutexMap<string>(); |
| 1089 | // Serialize lifecycle actions per resolved child workspace: a batch may include both the |
| 1090 | // created handle and later existing-mode handles for the same workspace. |
| 1091 | private readonly workspaceLifecycleLocks = new MutexMap<string>(); |
| 1092 | // Serialize terminal writes per workspace-turn handle so late completions/interruptions cannot |
| 1093 | // overwrite an already-settled handle. |
| 1094 | private readonly workspaceTurnSettlementLocks = new MutexMap<string>(); |
| 1095 | private readonly mutex = new AsyncMutex(); |
| 1096 | private maybeStartQueuedTasksInFlight: Promise<void> | undefined; |
| 1097 | private maybeStartQueuedTasksRerunRequested = false; |
| 1098 | // Git worktree creation touches per-repository metadata; serialize that narrow phase per project |
| 1099 | // while allowing post-fork init/send startup work for sibling tasks to overlap. |
| 1100 | private readonly reservedTaskLaunchByProjectPath = new Map<string, Promise<void>>(); |
| 1101 | // In-flight durable persistence of notify_on_terminal policy for backgrounded foreground waits. |
| 1102 | // Awaited at the start of handleStreamEnd so a just-detached wait is treated as non-blocking. |
| 1103 | private readonly pendingNotifyOnTerminalPersists = new Set<Promise<void>>(); |
| 1104 | // In-flight terminal attention drains (workspace-turn / sub-agent terminal wake-ups). Tracked so |
| 1105 | // tests and shutdown can await them; drains are idempotent and re-triggered on owner idle events. |
| 1106 | private readonly pendingTerminalAttentionDrainsByOwner = new Map<string, Promise<void>>(); |
| 1107 | private readonly pendingTerminalAttentionDrains = new Set<Promise<void>>(); |
| 1108 | private readonly pendingWaitersByTaskId = new Map<string, PendingTaskWaiter[]>(); |
| 1109 | private readonly pendingStartWaitersByTaskId = new Map<string, PendingTaskStartWaiter[]>(); |
| 1110 | // Tracks workspaces currently blocked in a foreground wait (e.g. a task tool call awaiting |
| 1111 | // agent_report). Used to avoid scheduler deadlocks when maxParallelAgentTasks is low and tasks |
| 1112 | // spawn nested tasks in the foreground. |
| 1113 | private readonly foregroundAwaitCountByWorkspaceId = new Map<string, number>(); |
| 1114 | private readonly backgroundableForegroundWaitersByWorkspaceId = new Map< |
| 1115 | string, |
| 1116 | Set<BackgroundableForegroundWaiter> |
| 1117 | >(); |
| 1118 | private readonly pendingWorkspaceTurnWaitersByHandleId = new Map<string, WorkspaceTurnWaiter[]>(); |
| 1119 | private readonly activeWorkspaceTurnHandleByWorkspaceId = new Map< |
| 1120 | string, |
| 1121 | { handleId: string; ownerWorkspaceId: string } |
| 1122 | >(); |
| 1123 | private readonly taskHandleStore: TaskHandleStore; |
| 1124 | private readonly terminalAttentionStore: TerminalAttentionStore; |
| 1125 | private readonly userBackgroundedTaskIds = new Set<string>(); |
| 1126 | |
| 1127 | // Cache completed reports so callers can retrieve them without re-reading disk. |
| 1128 | // Bounded by max entries; disk persistence is the source of truth for restart-safety. |
| 1129 | private readonly completedReportsByTaskId = new Map<string, CompletedAgentReportCacheEntry>(); |
| 1130 | private readonly gitPatchArtifactService: GitPatchArtifactService; |
| 1131 | private readonly handoffInProgress = new Set<string>(); |
| 1132 | /** |
| 1133 | * Hard-interrupted parent workspaces must not auto-resume until the next user message. |
| 1134 | * This closes races where descendants could report between parent interrupt and cascade cleanup. |
| 1135 | */ |
| 1136 | private interruptedParentWorkspaceIds = new Set<string>(); |
| 1137 | /** Tracks consecutive auto-resumes per workspace. Reset when a user message is sent. */ |
| 1138 | private consecutiveAutoResumes = new Map<string, number>(); |
nothing calls this directly
no outgoing calls
no test coverage detected