/** * ResourceGovernor — Never-OOM class-based admission (T11999, Epic T11992). * * Admits resource-intensive work through priority classes whose slot budgets * are computed at acquire time from host memory + memory-pressure (PSI). A * denial returns a structured, retryable {@link ResourceDeferral} — never a * silent drop, never a crash. Existing grants are NEVER revoked. * * Three modes (verbatim writer-lease shape, `writer-lease.ts` `resolveLeaseMode`): * - `supervisor` — defer to the Rust `cleo-supervisor` `resource_admit` verb. * Demotes to `local` (log-once) until the IPC client is wired — a * dead/absent arbiter must never deadlock work. * - `local` — DEFAULT, daemon-off. Per-class slot directories under * `getCleoHome()/locks/resource-/` arbitrated by `proper-lockfile` * (crash-stale auto-release ⇒ genuinely cross-process without a daemon), * plus a point-sample of the {@link ResourceMonitor} taken INSIDE acquire. * - `off` — pure pass-through. * * `interactive-cli` is NEVER gated. The heavy classes (`test-run`, * `scoped-build`, `full-build`) are not slot classes any more: they are * admitted by the admission ledger (`admission-ledger.ts`, T13133), one byte * budget and one FIFO queue shared with evidence runs, gated by memory * pressure (T13127) and re-entrant for nested runs. The governor delegates to * it, so there is one admission point for heavy work. * * A local slot whose holder process is provably gone is reaped at once instead * of waiting out the 10 min stale timeout (T12963), as the tool semaphore does * for its slots (gh#1222). * * @task T11999 * @task T12963 * @task T13127 * @task T13133 * @epic T11992 * @adr resource-governor-never-oom-architecture §3.4 */ import type { AdmissionResult, GovernorMode, ResourceClass, ResourceGrant } from '@cleocode/contracts'; import { type AdmissionIoError } from './admission-ledger.js'; import type { ResourceSample } from './backend.js'; import { ResourceMonitor } from './monitor.js'; import { type MemoryGateReporter } from './pressure-gate.js'; /** * Resolve the governor mode from `CLEO_RESOURCES_MODE`, once per process. * Unknown / unset values resolve to `'local'` — the production-safe default * while the supervisor daemon is disabled. * * @task T11999 */ export declare function resolveGovernorMode(): GovernorMode; /** * Reset cached process-global state (mode + degrade flag). Tests only. * @internal */ export declare function _resetGovernorStateForTest(): void; /** Tunables for budget computation. All optional; sane defaults applied. */ export interface BudgetOptions { /** RAM reserved for the OS + interactive use, in MiB. Default 2048. */ readonly headroomMb?: number; /** Estimated RAM per agent session (incl. ~300 MB MCP suite), MiB. Default 4096. */ readonly agentEstRamMb?: number; /** `some avg10` (pp) above which background work is refused. Default 10. */ readonly holdSomeAvg10?: number; /** `some avg10` (pp) above which db-heavy work is refused. Default 25. */ readonly floorSomeAvg10?: number; /** Override CPU count (tests). Default {@link availableParallelism}. */ readonly cpuCount?: number; /** Override total RAM bytes (tests). Default {@link totalmem}. */ readonly totalMemBytes?: number; } /** * Compute the slot budget for a class given a point-sample. * * - `interactive-cli` → `Infinity` (never gated). * - `test-run` / `scoped-build` / `full-build` → admitted by the admission * ledger in bytes (T13133); the number here is a count view of that budget: * how many default-footprint runs it holds under the current pressure share * (`0` while the memory gate refuses, `1` when CPU-saturated, half at hold, * at least `1` otherwise). * - `agent-session` → `clamp(1, ⌊(MemAvailable − headroom)/estRamMb⌋, cpus−2)`. * - `llm-call` → `max(1, cpus−2)` (primarily gated by the llm-queue elsewhere). * - `db-heavy` → `1`, deferred (→0) under `backoff`-level MEMORY pressure. * - `background-autonomous` → `1` only when MEMORY pressure is `ok`, else `0`. * * Only `test-run`/`scoped-build` count CPU saturation (the combined * {@link pressureScore}); every other class is budgeted on memory alone * (T13170), so a busy machine never defers a store migration or the sentient * tick. * * @adr resource-governor-never-oom-architecture §3.4 (budgets) */ export declare function computeClassBudget(cls: ResourceClass, sample: ResourceSample, opts?: BudgetOptions): number; /** * Machine-wide slot directory for a class, under * `getCleoHome()/locks/resource-/`. Shared across projects, worktrees, * and PIDs — exactly like the tool semaphore. */ export declare function governorSlotDir(cls: ResourceClass): string; export { SLOT_LOCK_STALE_MS, SLOT_LOCK_UPDATE_MS } from './slot-holder.js'; /** An ungated grant (no slot held, `slot: -1`): `off` mode, `interactive-cli`, a nested run, fail-open. */ export declare function passThroughGrant(cls: ResourceClass): ResourceGrant; /** Why the governor could not keep its state: the errno code and the path, when known. */ export type GovernorIoError = AdmissionIoError; /** * The code and path of an error that means "the governor cannot keep state here" (a sandbox that * blocks writes to the CLEO home, a read-only or full disk, a broken CLEO home), or null for any * other error, which is a bug and must not be hidden. The same rule as the admission ledger's. * * @param err - a caught error. */ export declare function governorIoError(err: unknown): GovernorIoError | null; /** * Run an admission, failing OPEN when the governor cannot write its state * ({@link governorIoError}): the work proceeds ungated with a * {@link passThroughGrant}, and `ungoverned` says why. A sandboxed or * read-only CLEO home must never turn into a deferral that never clears. * Any other error is a bug and propagates. * * @example * ```ts * const { admission, ungoverned } = await admitFailOpen('agent-session', () => * governor.tryAcquire('agent-session'), * ); * ``` */ export declare function admitFailOpen(cls: ResourceClass, acquire: () => Promise): Promise<{ readonly admission: AdmissionResult; readonly ungoverned: GovernorIoError | null; }>; /** Options for {@link ResourceGovernor.acquire}. */ export interface AcquireOptions extends BudgetOptions { /** * When `false`, a single non-blocking pass — returns a {@link ResourceDeferral} * immediately if no slot is free (admission semantics; spawn/wave clamp). * When `true` (default), polls until a slot frees or `timeoutMs` elapses * (queue semantics; heavy ops). On timeout, returns a deferral. A zero * budget (pressure) defers at once. */ readonly blocking?: boolean; /** Max wall-clock to wait in blocking mode (ms). Default 3_600_000. */ readonly timeoutMs?: number; /** Poll interval in blocking mode (ms). Default 200. */ readonly pollMs?: number; /** * Inject a pre-taken sample (tests, or to avoid re-sampling). When omitted, * a fresh point-sample is taken inside acquire. */ readonly sample?: ResourceSample; /** Inject a monitor (tests). Default a fresh {@link ResourceMonitor}. */ readonly monitor?: ResourceMonitor; /** * Told when a blocking acquire waits on the memory gate, and when that wait * ends in an admission (T13127). */ readonly memoryPressure?: MemoryGateReporter; } /** * The Never-OOM admission gate. Stateless wrapper over the mode-resolved * backend (local slot dirs today; supervisor IPC when wired). Construct once * and share, or use the module-level {@link governor} singleton. */ export declare class ResourceGovernor { /** * Acquire one slot of `cls`. Returns a {@link ResourceGrant} on success or a * {@link ResourceDeferral} on denial. Never throws for admission control; * only genuinely unexpected I/O errors propagate. * * Only a held lock (`ELOCKED`) counts as a busy slot. Any other lock error * (EACCES from a sandbox that can't write the slot dir, EROFS, ENOSPC…) is * thrown once a pass found no free slot, never read as "busy": a slot that * can never be taken would otherwise defer every caller until it times out * (#1777 round 8, R8-1). Callers fail open with {@link admitFailOpen}. */ acquire(cls: ResourceClass, opts?: AcquireOptions): Promise; /** * Route an admission through the supervisor's central `resource_admit` verb. * Returns a grant (whose `release` calls `resource_release`) or a deferral, or * `null` when the supervisor is unreachable so the caller degrades to the * local slot engine. Never throws — a dead arbiter never deadlocks work. * * @task T12001 */ private acquireViaSupervisor; /** Non-blocking single-pass acquire (admission semantics). */ tryAcquire(cls: ResourceClass, opts?: AcquireOptions): Promise; /** * Currently-grantable slot count for `cls` = budget − held. Held is the count * of slot files currently locked. `Infinity` for ungated classes. */ available(cls: ResourceClass, opts?: AcquireOptions): Promise; } /** Count of slot files for a class (debug/introspection). */ export declare function slotFileCount(cls: ResourceClass): number; /** Process-wide governor singleton. */ export declare const governor: ResourceGovernor; //# sourceMappingURL=governor.d.ts.map