import { AgentMessageBus } from './message-bus.js'; import type { WrfcChain, WrfcChildRouteSelector } from './wrfc-types.js'; import { WrfcWorkmap } from './wrfc-workmap.js'; import { AgentWorktree } from './worktree.js'; import type { ConfigManager } from '../config/manager.js'; import type { AgentRecord } from '../tools/agent/index.js'; import type { ExecutionPlanManager } from '../core/execution-plan.js'; import type { FixWorkstreamRunner } from '../orchestration/fix-workstream-runner.js'; import type { RuntimeEventBus } from '../runtime/events/index.js'; import type { ProjectWorkPlanTaskCreateInput, ProjectWorkPlanTaskUpdateInput } from '../knowledge/project-planning/index.js'; import { type AgentManagerLike } from './wrfc-config.js'; export { extractScoreFromText, extractPassedFromText, extractIssuesFromText } from './wrfc-reporting.js'; /** * Schema version for the serialized WRFC chain envelope. * Increment when the WrfcChain shape changes in an incompatible way. */ export declare const CURRENT_WRFC_CHAIN_SCHEMA_VERSION = 1; type WrfcWorktreeOps = Pick & Partial>; type WrfcWorkPlanService = { createWorkPlanTask(input: ProjectWorkPlanTaskCreateInput): Promise; updateWorkPlanTask(input: ProjectWorkPlanTaskUpdateInput): Promise; }; export declare class WrfcController { private readonly chains; private chainQueue; private unsubscribers; private activeChainCount; private readonly sessionId; private readonly workmap; private readonly projectRoot; private readonly skipClaimVerification; /** Cached at construction time: whether projectRoot existed on disk when this controller was created. */ private readonly projectRootExistedAtStartup; private runtimeBus; private readonly messageBus; private planManager; private readonly agentManager; private readonly configManager; private readonly createWorktree; private readonly selectChildRoute; private workPlanService; /** The planned-fix executor over the ONE workstream engine. Wired by the composition root; the single-fixer prompt path no longer exists. */ private fixWorkstreamRunner; private readonly workPlanTaskQueues; /** Tracks last-seen timestamp per agent for watchdog timeout. */ private readonly agentLastSeen; /** Active watchdog timer handle, if any. */ private watchdogTimer; /** Pending one-shot chain-reaper timers, so dispose() can cancel them (scheduleChainCleanup). */ private readonly chainCleanupTimers; constructor(runtimeBus: RuntimeEventBus, messageBus: Pick, deps: { readonly agentManager: AgentManagerLike; readonly configManager: Pick; readonly projectRoot: string; readonly surfaceRoot?: string | undefined; readonly createWorktree?: (() => WrfcWorktreeOps) | undefined; readonly selectChildRoute?: WrfcChildRouteSelector | undefined; }); createChain(ownerRecord: AgentRecord): WrfcChain; getSessionId(): string; getWorkmap(): WrfcWorkmap; setPlanManager(planManager: Pick): void; setRuntimeBus(runtimeBus: RuntimeEventBus): void; setWorkPlanService(service: WrfcWorkPlanService | null | undefined): void; /** Wire the planned-fix runner (the one engine); absent => failing reviews fail the chain naming the missing wiring. */ setFixWorkstreamRunner(runner: FixWorkstreamRunner | null | undefined): void; getChain(chainId: string): WrfcChain | null; listChains(): WrfcChain[]; resumeChain(chainId: string): boolean; resumeAllActiveChains(): number; /** * Item 3: Serialize a chain to a JSON string for durable storage. * Returns null if the chain does not exist. */ serializeChain(chainId: string): string | null; /** * Item 3: Deserialize a chain from a JSON string. * Returns null if the JSON is invalid, the required fields are missing, or * the schema version is newer than this runtime supports. * * Schema versioning: * - Missing schemaVersion (v0/legacy): accepted for back-compat, the JSON * is the raw chain object directly. * - schemaVersion === CURRENT_WRFC_CHAIN_SCHEMA_VERSION (1): unwrap { schemaVersion, chain }. * - schemaVersion > CURRENT_WRFC_CHAIN_SCHEMA_VERSION: rejected, fail closed. */ deserializeChain(json: string): WrfcChain | null; /** * Item 3: Import a deserialized chain into this controller instance. * After importing, call resumeChain(chain.id) to continue from recorded state. * * Refuses to overwrite a non-terminal chain (state is not 'passed' or 'failed') * to prevent accidental clobber of live chains. Use force=true to override. * Always overwrites terminal chains (idempotent replay is safe for completed work). * * Returns true if the chain was imported, false if refused. */ importChain(chain: WrfcChain, force?: boolean): boolean; /** * Item d5 (see CHANGELOG 0.38.0): resurrection-safe zombie check for a chain about * to be imported at rehydrate. A non-terminal chain whose ENTIRE roster * (allAgentIds) is absent from THIS process's live AgentManager never * survived the restart, no in-process execution is coming back to finish * it, so it would otherwise show as "running" forever. If even ONE roster * agent id IS live (e.g. re-imported mid-session, not at a real process * restart), this returns false and the chain is left exactly as imported, * reaping only ever fires when NOTHING could possibly still be driving it. */ private isZombieChain; /** * Item d5: mark a reimported zombie chain terminal at import time so it * presents as failed + prunable instead of stuck non-terminal forever. * Deliberately does NOT call failChain()/cancelChain(): those assume a * live in-memory execution (cancel running children, complete the owner * agent record, re-check gates for siblings) that makes no sense for a * chain whose entire roster is already confirmed dead, this is a direct, * minimal field mutation plus the same state-changed/chain-failed events * every other terminal transition emits, so consumers see one consistent * "chain failed" signal regardless of which path produced it. */ private reapZombieChain; dispose(): void; private transition; private applyWrfcAgentMetadata; /** Wire up a freshly spawned WRFC child agent in one canonical order: * stamp metadata, push to allAgentIds tracking list, register with message bus. * Keeps the role-field assignment at each call site to preserve clarity. */ /** Prepend a formatted block of synthetic controller-injected issues to a review task body. * Returns the augmented task string; does NOT clear the issues array (caller's responsibility). */ private prependSyntheticIssues; private registerSpawnedChild; private keepOwnerAgentActive; private ownerProgress; private wrfcPhaseOrder; private setupListeners; /** * Start (or restart) the watchdog timer based on current config. * Called once on setup and whenever the timeout config may have changed. */ private resetWatchdog; /** Tick: fail any chain whose active child agent has been silent longer than timeoutMs. */ private tickWatchdog; /** * True when verifyEngineerClaims should be skipped: the explicit test-only * flag (createWrfcControllerForTest), or projectRoot not existing at * construction (cached, the workmap mkdirs it later). Both are false in * any real session, so claim verification always runs in production. */ private shouldSkipClaimVerification; /** Returns the single currently-active child agent ID for a chain, if deterministic. */ private activeChildAgentId; private onAgentComplete; private onAgentFailed; /** * Respawn the most recently spawned child agent after a transport-classified * failure, instead of failing the chain immediately. Bounded by * wrfc.transportRetryLimit (default 1) and tracked via chain.transportRetryCount, * kept separate from fixAttempts/reviewCycles so a transport blip never counts * against the ordinary fix-cycle budget. */ private retryTransportFailure; /** Point the chain's role-specific agent-id field at a freshly (re)spawned child. */ private rewireChainChildAgentId; private onAgentCancelled; private startReview; private processReview; /** * The planned-fix path: review findings parse into a dependency * graph run by the ONE engine (elastic pool, isolated worktrees, * reviewed-and-merged release). Merged => the terminal contract gate * re-reviews against the ORIGINAL request; task-level green is telemetry. * Structured failures (cycle/orphaned/tasks-failed) fail the chain honestly. */ private startPlannedFix; private runGates; private evaluateConstraintSet; private evaluateConstraints; /** * The constraints a reviewer/fixer is asked to verify: the full enumerated set * MINUS the ones a system action (a fan-out collapse) made unsatisfiable. Keeping * chain.constraints itself intact preserves fixer constraint-continuity checks; * only the rubric handed to review/gate-fix is narrowed. */ private reviewableConstraints; private evaluateSubtaskConstraints; private processGateResults; private scheduleChainCleanup; private checkAndRunGatesForAll; private autoCommit; private autoCommitCandidateAgentIds; /** * Chain-wide "own edit ledger": every path self-reported as created/modified/deleted by * any engineer/fixer/integrator completion on this chain (including subtask completions), * deduplicated. Primary source is chain.touchedPaths, an incremental accumulator appended * to on every completion (see recordTouchedPaths) so fixer/re-fix passes and resumed * chains are represented, not just the first pass. Falls back to deriving from the * last-stored report slots (chain.engineerReport / chain.integratorReport / * subtask.engineerReport) for chains serialized before touchedPaths existed. * * Self-reported, not ground truth, same accuracy ceiling as verifyEngineerClaims. Per-agent * worktree isolation (AgentWorktree.create) is not wired up in this controller today, so * there is no git-branch-diff signal to corroborate against. */ private collectChainTouchedPaths; private buildAutoCommitMessage; /** Whether every chain member (owner + children) is terminal (gone or in a terminal status). */ private allChainMembersTerminal; private failChain; private cancelRunningChildren; private hasRunningChild; private cancelChain; private dequeueNext; private findChainByAgentId; private generateWrfcId; private generateDecisionId; private appendOwnerDecision; private completeCurrentNode; private failCurrentNode; private completeSubtaskNode; private createBaseChain; private startEngineeringChain; private startCompoundEngineeringChain; /** * Appends a completion report's self-reported filesCreated/filesModified/filesDeleted * into the chain's running edit ledger (chain.touchedPaths). Called for every engineer, * fixer, and integrator completion, not just the first pass, so a chain that goes * through gate-fix or review-fix cycles still has the fixer's edits represented. This is * why it is a standalone accumulator rather than reading the last-stored report field: * chain.engineerReport / subtask.engineerReport are last-write slots that do not reliably * retain every fixer pass (see collectChainTouchedPaths for the consuming side). */ private recordTouchedPaths; private handleEngineerCompletion; private buildSubtaskEngineerTask; private buildCompoundIntegrationTask; private findSubtaskByAgentId; private onCompoundSubtaskAgentComplete; private handleCompoundEngineerCompletion; private startCompoundSubtaskReview; private processCompoundSubtaskReview; /** The compound sub-deliverable fix rides the SAME planned-fix path; merged cycles re-review against the sub-deliverable's own ask. */ private startCompoundSubtaskFix; private startIntegration; private handleIntegratorCompletion; private canonicalizeFixerReportConstraints; private spawnWrfcAgent; private withRouteReason; /** * Terminal success path, the ONE derivation point for a passing chain (its counterpart is * failChain). The chain's terminal status derives here from the full-scope review and quality * gates, never from the auto-commit result: the optional `commitNote` states the commit outcome * SEPARATELY in the completion message so a skipped/failed commit reads as a warning on a passing * chain, and can never contradict the "succeeded" verdict the transcript already showed. */ private completeChainAsPassed; /** * `message` is the chain STATUS (what the workflow did); `answer` is what the * work produced. Readers of this agent get the ANSWER, the session * transcript, and through it the chat surface the request came from. The * status stays on `progress` under the 'operator' audience, which the channel * delivery path never forwards to a person. */ private completeOwnerAgent; /** * Roll up token usage across every agent that has ever run under this * chain (the owner plus all phase/subtask children, across every review * and fix cycle, `chain.allAgentIds` already tracks the full roster for * worktree cleanup, so it doubles as the usage-aggregation source). Each * contributor's usage is added in, including the owner's own (normally * zero, but summed rather than ignored in case it is ever populated * directly). Optional fields (reasoningTokens/reasoningSummaryCount) are * only included in the result if at least one contributor reported them, * matching AgentUsage's undefined-means-no-data convention for those. */ private aggregateChainUsage; /** Roll up tool-call counts across every agent that has ever run under this chain. */ private aggregateChainToolCallCount; private upsertWrfcWorkPlanTask; private setWrfcWorkPlanTaskStatus; private enqueueWrfcWorkPlanTaskOperation; /** * Resolve the work-plan-visible role for an agent: the durable * record.wrfcRole first (preserves superseded-agent identity), else the * current-slot structural fallback; orchestrator/verifier filter out (no * work-plan representation). */ private resolveWrfcRole; private workPlanRoleForAgent; private workPlanTaskIdForAgent; private workPlanTaskTitle; private safeCheckAndRunGatesForAll; private safeDequeueNext; } //# sourceMappingURL=wrfc-controller.d.ts.map