import type { CodingHarness } from "#src/harness/interface"; import type { HarnessRegistry } from "#src/harness/registry"; import { type LifecycleRegistry } from "#src/lifecycle-controller"; import { type TmuxManager } from "#src/tmux-manager"; import type { DurableTracker } from "#src/tracker"; import type { SubagentRecord } from "#src/types"; export interface StartupReconcilerOptions { tracker: DurableTracker; tmux: TmuxManager; harnessRegistry: HarnessRegistry; registry: LifecycleRegistry; /** pi config dir — used to resolve guard config from tmux-pilot.config.yaml. */ configDir: string; parentSessionId: string; } export interface ReconcileOutcome { /** Agents whose window was alive and got a live controller re-attached. */ reattached: string[]; /** Agents whose window was gone/unverifiable — reported `WindowGone` → crashed. */ crashed: string[]; /** * The subset of `reattached` NOT owned by the current parent — genuinely * orphaned agents (owning parent verifiably gone) adopted into this * session. Non-empty ⇒ the adapter surfaces the startup notification. */ adoptedForeign: string[]; /** Foreign records left alone: their parent is (or may be) still live. */ skippedForeign: string[]; } export declare class StartupReconciler { private readonly opts; constructor(opts: StartupReconcilerOptions); /** * Load persisted records and reconcile every `running` one. Terminal and * pending records are left untouched. Never throws — per-record failures * degrade to the conservative `crashed` path. */ reconcile(): ReconcileOutcome; /** * Is this record owned by the current parent session? Spawn-time * `parentSessionId` when present; legacy fallback: the `pilot-` * (or older `_pi-sub-`) session prefix of `recoveryId`. */ private isOwnRecord; /** * Whether the record carries ANY trail pointing at an owning parent: * a spawn-time `parentSessionId`, a socketPath (whose manifest can name * an owner), or a recoveryId with a recognizable subagent-session prefix. * Without one, the skip-foreign policy has nothing to protect. */ private hasOwnerEvidence; /** * Whether a foreign record's owning parent process is (or may be) still * live, via the socket-manifest `parentPid` (see `owningParentLiveness`). * Only a verifiable "gone" clears the way for adoption — "unknown" * (legacy record without a socketPath, missing manifest, pre-`parentPid` * manifest) is treated as possibly-live, because adopting a live parent's * agent means competing monitors and destructive teardown. */ private owningParentMayBeLive; /** * Attach a live `LifecycleController` to a recovered running agent — the * same monitoring path as a fresh spawn or a resume. Turn thresholds that * fired pre-restart are NOT replayed (the fresh controller's dedup set * starts empty, but thresholds only fire on turn-count increments observed * from here on). */ private attach; } /** * Deterministically reconstruct the harness's session directory from the * persisted record by asking the harness itself (`resolveSessionDir?`), * mirroring its own `setupEnvironment()` logic without re-running it (which * would recreate config/hooks unnecessarily). * * A harness that doesn't implement the method — or a record naming a harness * that is no longer registered — falls back to pi's default, * `/sessions`, rather than throwing. */ export declare function resolveSessionDir(record: SubagentRecord, harnessRegistry: HarnessRegistry): string; /** * Lifecycle-end sentinel path for a known agent. Harness override * (`resolveEndFilePath`) wins; the default keys on the record's persisted * runtime directory — `/end` for a new-layout record, the * historical `$TMPDIR/pi-tmux-end-` (pi + cmd) for a legacy one. */ export declare function resolveEndFilePath(harness: CodingHarness | undefined, agentId: string, runtimeDir?: string): string; //# sourceMappingURL=startup-reconciler.d.ts.map