/** * FNXC:CodeOrganization 2026-08-04-07:10: * TaskExecutor instance state fields peeled to a base class (U4) so executor.ts keeps * thin method facades only. Fields are protected (not private) so TaskExecutor methods * and runtime tests that poke (executor as any).fieldName keep working. */ import type { Agent, AgentStore, TaskStore, RunMutationContext, MergeResult, ThinkingLevel, WorkflowColumnAgent, ApprovalRequestStore, WorkspaceConfig, } from "@fusion/core"; import type { ImplementationExit } from "./implementation-exit.js"; import type { ForeachActiveContext } from "../workflows/workflow-node-handlers.js"; import { ModelRegistry, type AgentSession } from "@earendil-works/pi-coding-agent"; import { CliTaskSession } from "../cli-agent/task-session.js"; import { TokenCapDetector } from "../errors/token-cap-detector.js"; import { StepSessionExecutor } from "../execution/step-session-executor.js"; import type { PausedAbortProvenance } from "./paused-abort-provenance.js"; import type { ActiveExecutorSessionState, TaskExecutorOptions } from "./task-executor-options.js"; import type { WorkflowAgentCapacity } from "../agents/workflow-agent-capacity.js"; import { invalidateWorkspaceConfigCache } from "./workspace-config-resolver.js"; export abstract class TaskExecutorState { /** * FNXC:CodeOrganization 2026-08-04-08:05: * Constructor-injected store/rootDir/options live on the state base (U4) so pure * worktree facades and bags can share them without TaskExecutor parameter properties. */ protected store!: TaskStore; protected rootDir!: string; protected options: TaskExecutorOptions = {}; protected activeWorktrees = new Map>(); /** * FNXC:WorkflowAgentRouting 2026-08-07-03:46: * Workflow stage reservations are intentionally independent from heartbeat slots. * Constructed in wireTaskExecutorLifecycle so direct graph admission and triage share the same class. */ protected workflowAgentCapacity!: WorkflowAgentCapacity; /** * FNXC:WorkflowAgentRouting 2026-08-07-03:46: * Process-local index carrying a durable work item's narrow authority to the model tool gate. * Keyed by task for the live graph turn; removed in graph cleanup. isLive revalidates the exact record. */ protected activeWorkflowAuthorities = new Map(); /** * FNXC:WorkflowAgentRouting 2026-08-07-03:46: * Live fenced principal per task for execute/review session identity (exact graph-selected agent). * `agent` is the admission-time row so session identity does not depend on a second getAgent round-trip. */ protected activeWorkflowPrincipals = new Map(); /** * FNXC:AgentActivityStream 2026-08-09-13:59 (restored 2026-08-15-22:15 after wave-18 shell-ification dropped it): * Node-scoped routed-principal retention (`taskId\0nodeId` -> agentId) that outlives release-principal, * because the graph can emit a terminal gate result AFTER the per-attempt reservation is released while * the activity outbox must still attribute that gate to the exact routed principal (FN-8864). */ protected workflowGateActivityPrincipals = new Map(); protected executing = new Set(); protected resumingUnpaused = new Set(); protected approvalSuspended = new Set(); protected approvalResumeAfterUnwind = new Set(); protected recoveringCompleted = new Set(); protected capturedReflectionTaskIds = new Set(); protected workflowRerunPending = new Set(); protected workflowLifecycleMovesInFlight = new Set(); protected pendingTaskDisposals = new Map>(); protected unregisterTaskMoveDisposer: (() => void) | undefined; protected unregisterArchiveWorktreeDisposer: (() => void) | undefined; protected unregisterArchiveWorkspaceWorktreeDisposer: (() => void) | undefined; protected activeSessions = new Map(); protected activeStepExecutors = new Map(); protected activeStepExecutorSeenSteeringIds = new Map>(); protected effectiveColumnAgentByTask = new Map(); protected activeWorkflowStepSessions = new Map(); protected activePlanningWorkflowSessions = new Set(); protected activeWorkflowStepSessionSeenSteeringIds = new Map>(); protected activeConfiguredCommandControllers = new Map>(); protected authoritativeAssignedAgentStore: AgentStore | null = null; protected activeWorkflowGraphAbortControllers = new Map(); protected activeCliTaskSessions = new Map(); protected readonlyWorkflowStepAuditDone = false; protected activeSubagentSessions = new Map>(); protected pausedAborted = new Set(); protected pausedAbortProvenance = new Map(); protected completionFinalizedTaskIds = new Set(); protected depAborted = new Set(); protected stuckAborted = new Map(); protected userCanceledTaskIds = new Set(); protected graphExecuteSelfRequeued = new Set(); protected loopRecoveryState = new Map(); protected spawnedAgents = new Map>(); protected tokenUsageBaselines = new Map(); protected branchConflictErrorCount = new Map(); protected completedTaskWatchdogs = new Map>(); protected workflowRerunWatchdogs = new Map>(); protected pendingEphemeralDeletions = new Set(); protected workspaceConfig: WorkspaceConfig | null | undefined = undefined; /** Workspace mode is memoized per executor host; a real settings transition must refresh it live. */ public invalidateWorkspaceConfig(): void { this.workspaceConfig = undefined; invalidateWorkspaceConfigCache(this); } protected childSessions = new Map(); protected totalSpawnedCount = 0; protected tokenCapDetector = new TokenCapDetector(); protected _modelRegistry?: Promise; protected _approvalRequestStore?: ApprovalRequestStore; protected currentRunContexts = new Map(); protected outerConcurrencyClaims = new Set(); protected graphToolFailureRunCursors = new Map(); protected graphStepSessionPinned = new Set(); protected graphStepRunOnce = new Map>(); protected graphStepActiveContext = new Map(); protected graphRethinkNarrations = new Map(); protected graphColumnAgentResolver = new Map WorkflowColumnAgent | undefined>(); protected graphUnattendedRuns = new Set(); protected graphSeamGoverningNodeId = new Map(); protected graphSeamThinkingLevel = new Map(); protected graphSeamSkillName = new Map(); protected mergeRequester?: (taskId: string, options?: { signal?: AbortSignal }) => Promise; protected sessionContentionHoldAttempts = new Map(); /** * FNXC:CodeOrganization 2026-08-04-07:40: * Process-wide graph-routing set lives on the state base (U4). Instance getter keeps * host.graphRouting / host.constructor.processWideGraphRouting bag access unchanged. */ protected static processWideGraphRouting = new Set(); protected get graphRouting(): Set { return (this.constructor as typeof TaskExecutorState).processWideGraphRouting; } }