import type { RunStore } from "./types.ts"; /** The lock file's contents while held (spec §7.2). */ export interface LockInfo { readonly runId: string; readonly pid: number; readonly host: string; readonly startedAt: string; } /** * The two non-deterministic facts a lock decision needs (spec §7.3): who "we" are (pid + hostname), * and whether a given pid on THIS host is still alive. Injected so contention and reclaim are testable * deterministically and offline — no real process spawning in the unit suite. */ export interface ProcessEnv { readonly pid: number; readonly hostname: string; /** Is `pid` (on this host) still alive? Never called for a different host — liveness is meaningless across hosts (spec §7.3). */ isAlive(pid: number): boolean; } /** The real seam: this process's pid/hostname, liveness via a zero-signal `kill` probe (no side effect on a live process). */ export declare function createProcessEnv(): ProcessEnv; export type AcquireResult = { readonly ok: true; readonly reclaimed?: LockInfo; } | { readonly ok: false; readonly reason: "held"; readonly holder: LockInfo; } | { readonly ok: false; readonly reason: "foreign-host"; readonly holder: LockInfo; } | { readonly ok: false; readonly reason: "contended"; }; /** This process's currently held run + its abort signal, if any — the one thing that can meaningfully own a live `AbortController` (mirrors the old in-process guard's shape). */ export interface ActiveRun { readonly runId: string; readonly controller: AbortController; } export type BeginResult = { readonly ok: true; readonly controller: AbortController; readonly reclaimed?: LockInfo; } | { readonly ok: false; readonly reason: "held"; readonly holder: LockInfo; } | { readonly ok: false; readonly reason: "foreign-host"; readonly holder: LockInfo; } | { readonly ok: false; readonly reason: "contended"; }; /** * The single execution slot per project (spec §7): backed by the file lock, not an in-process flag, so * it holds across concurrent PI sessions on the same project (spec §7.2). `active` still tracks only * THIS process's own current run, since only the process actually executing can hold a live * `AbortController` for `/workflow cancel` to abort. * * `projectRoot`/`store` are passed per call (like `RunStore` itself), not bound at construction, so one * `RunLock` instance can serve the extension's whole lifetime while each command supplies its own * per-invocation store. */ export interface RunLock { readonly active: ActiveRun | undefined; /** * Acquire the project lock for `runId` (spec §7.2/§7.3). On a successful reclaim of a stale lock, * appends a `run-crashed` event to the abandoned run's log via `store` — UNDER the newly acquired * lock, before this resolves — leaving it resumable (spec §7.3). */ begin(runId: string, projectRoot: string, store: Pick): Promise; /** Release the lock (spec §7.1: on completion, crash, cancel, or block). No-op if `runId` is not this process's current holder. */ end(runId: string, projectRoot: string): Promise; } export declare function createRunLock(env?: ProcessEnv, now?: () => Date): RunLock; //# sourceMappingURL=run-lock.d.ts.map