import { open } from "node:fs/promises"; /** * Defines state root env var shared by the lifecycle pipeline. */ export declare const STATE_ROOT_ENV_VAR = "AGENT_HARNESS_STATE_ROOT"; /** * Lock file (per state root) that serializes first-run seeding and refresh * of managed state assets across concurrently starting CLI processes. */ export declare const STATE_ROOT_SEED_LOCK_FILE = "state-root.seed.lock"; /** * Describes state root resolution options data exchanged by the lifecycle pipeline. */ export interface StateRootResolutionOptions { packageRoot: string; workingDirectory: string; explicitStateRoot?: string; } /** * Describes prepared state root data exchanged by the lifecycle pipeline. */ export interface PreparedStateRoot { packageRoot: string; stateRoot: string; usesPackageRoot: boolean; } /** * Describes seed-lock timing policy data exchanged by the lifecycle pipeline. */ export interface StateRootSeedLockPolicy { /** Lock files older than this are considered abandoned crash litter. */ staleAfterMs: number; /** Total wall-clock budget for waiting on a live lock holder. */ waitBudgetMs: number; /** Poll interval while waiting for the lock to be released. */ pollIntervalMs: number; } /** * Test-only failure-injection seam (#427/#428 pattern, finding R1): * replaces the lock-open step so tests can force transient EPERM/EACCES * contention deterministically instead of racing the real delete-pending * window. `undefined` (the default) restores the real `open(lockPath, "wx")`. */ type SeedLockOpenOverride = (lockPath: string) => Promise>>; /** * Resolves state root from the provided inputs. */ export declare function resolveStateRoot(options: StateRootResolutionOptions): PreparedStateRoot; /** * Runs a state-root management section under the exclusive per-root seed * lock. * * Concurrent CLI processes starting against the same state root (the real * multi-agent scenario) must not race the destructive managed-asset * reconciliation: without the lock, two first runs both reached the * template copy and one crashed with EEXIST — or worse, one process's * `ensureCleanDirectory` deleted a tree the other had just populated. * The lock is acquired with O_EXCL so exactly one process holds it; other * processes wait (bounded) and then run their own section once the holder * releases, preserving the existing per-run refresh semantics. * * Crash litter is handled: a lock file older than `staleAfterMs` is broken * and the acquisition retried; if a live holder exceeds `waitBudgetMs`, a * clear error names the lock path for manual cleanup. */ export declare function runWithStateRootSeedLock(stateRoot: string, seedSection: () => Promise): Promise; /** * Provides prepare state root for the lifecycle pipeline. */ export declare function prepareStateRoot(preparedRoot: PreparedStateRoot): Promise; /** * Exposes test-only hooks for the state-root seed lock (#428 cross-process * hardening): deterministic policy overrides and direct section execution. */ export declare const stateRootInternals: { /** * Runs a section under the exclusive state-root seed lock. */ runSeedSectionForTests: typeof runWithStateRootSeedLock; /** * Returns whether a lock file is old enough to be treated as abandoned * crash litter, given the current seed-lock policy. */ isStaleLockFileForTests: typeof isStaleLockFile; /** * Returns whether a filesystem error means the lock file already exists. */ isPathExistsErrorForTests: typeof isPathExistsError; /** * Replaces the seed-lock timing policy (stale age / wait budget / poll * interval) so tests can exercise the stale-break and timeout branches * deterministically instead of waiting on real timers. */ setSeedLockPolicyForTests(policy: StateRootSeedLockPolicy): void; /** * Restores the default seed-lock timing policy. */ resetSeedLockPolicyForTests(): void; /** * Test-only failure injection: replaces the lock-open step (finding R1). * Pass `undefined` to restore the real open. */ setSeedLockOpenOverrideForTests(override: SeedLockOpenOverride | undefined): void; }; /** Returns whether a filesystem error means the lock file already exists. */ declare function isPathExistsError(error: unknown): boolean; /** Returns whether a lock file is old enough to be abandoned crash litter. */ declare function isStaleLockFile(lockPath: string): Promise; export {};