/** * macOS ResourceMonitor backend. * * macOS has no PSI. This backend maps the kernel's own signals onto the * PSI-shaped {@link ResourceSample}, so `evaluateState`, the slot scaling and * the governor's budgets work unchanged: * * - `kern.memorystatus_vm_pressure_level` — the kernel's memory pressure * verdict (1 normal, 2 warning, 4 critical), the signal jetsam acts on * - `kern.memorystatus_level` — the share of RAM that is neither wired nor * held by the compressor (active + inactive + free + speculative pages). * Unlike `os.freemem()`, which reads near zero on a healthy Mac, it falls * only as memory is wired or compressed * - `vm.compressor_bytes_used` — RAM the compressor occupies (the * "Compressed" figure of Activity Monitor and vm_stat, read here without a * `vm_stat` spawn) * - `vm.swapusage` — swap in use * - `vm.page_{free,speculative,pageable_external,purgeable}_count` × * `hw.pagesize` — memory the kernel can hand out without compressing or * swapping (free pages, file cache, purgeable caches): darwin's * `MemAvailable` (T13132) * - `vm.loadavg` + `hw.perflevel{0,1}.logicalcpu` (or `hw.ncpu`) — * run-queue length per effective core (CPU saturation) * * ## Sampling discipline (darwin amendment) * * Node has no sysctl binding, so `sample()` runs ONE `/usr/sbin/sysctl` exec * (≈4 ms) for all names, and caches its output for `cacheTtlMs` (default 2 s) * across every backend instance in the process. A poll loop or a burst of * admissions therefore spawns at most one child per TTL. No other process or * file read happens on the sample path; `sweepChildRss` (low frequency) uses * `ps`. * * ## Memory mapping (T13127) * * The kernel's own verdict decides whether memory is short NOW; the other * signals only grade how short. Two scores, the higher wins: * * - **Kernel warning or critical**: 15 or 40, plus severity. Above half of RAM * wired or compressed (`q`, from `1 − memorystatus_level`, or the * compressor's share when larger or the level is unreadable), each point of * `q` adds one; the swap the squeeze pushed out adds `50 × swap/RAM`, faded * in as `q` rises from {@link DARWIN_SWAP_RAMP} `[0.30, 0.50]`. Swap is never * counted while the kernel says normal: swapped pages linger for hours after * pressure has gone (idle apps keep them), so at a normal kernel level it is * history, not pressure. * - **Headroom, at any level**: reclaimable memory in bytes (free + * speculative + file-backed + purgeable pages; memory neither wired nor * compressed when the page counts are unreadable), against * {@link darwinHeadroomFloorBytes} (a quarter of RAM, at least 6 GiB, at most * half of RAM). Below that floor the score rises to 30 as headroom runs out, * but while the kernel says normal it stops at * {@link DARWIN_NORMAL_HEADROOM_CAP} (15, the memory gate's resume * threshold): it can hold, never back off or keep a latched gate shut. * Anonymous memory apps hold is not headroom, which the neither-wired-nor- * compressed share counted as if it were (T13132). A box with large wired * local-model weights but plenty left is not short. * * With the monitor's thresholds (hold 10, backoff 20): a kernel warning alone * holds; critical, or a warning with a hot squeeze and swap, backs off. * Calibration: the 2026-10-03 incident shape (48 GiB, kernel warning, 41% * neither wired nor compressed, 13.6 GiB swap) scores 38 (backoff); before * T13127 it scored 15, so heavy work kept being admitted. A 16 GiB laptop at * 44% wired or compressed with 6 GiB of old swap, an 8 GiB Air at 44% with * 3 GiB, and a 64 GiB Mac with 40 GiB of wired model weights and 21 GiB left * all score 0 at a normal kernel level. * * `full avg10` is 15 when the kernel reports critical. Memory is instantaneous * in all three windows. * * ## CPU mapping * * Effective cores are performance cores plus half the efficiency cores (a * 6P+12E machine counts as 12; `hw.ncpu` when the split is unknown). With * `r = load / cores`, `some = 100 × (r − 1) / r` once `r > 1`: the share of * runnable work that is waiting for a core. A box running twice its cores * scores 50. avg10/avg60/avg300 come from the 1/5/15 minute load averages. * * @module resources/darwin-backend * @task T12981 * @task T13127 * @epic T12978 */ import type { ChildRssSweep, PsiData, ResourceBackend, ResourceSample } from './backend.js'; import type { StatFileFn } from './linux-backend.js'; /** The sysctl names one sample reads, in one exec. */ export declare const DARWIN_SYSCTL_NAMES: readonly ["kern.memorystatus_vm_pressure_level", "kern.memorystatus_level", "vm.compressor_bytes_used", "vm.swapusage", "vm.page_free_count", "vm.page_speculative_count", "vm.page_pageable_external_count", "vm.page_purgeable_count", "hw.pagesize", "vm.loadavg", "hw.ncpu", "hw.perflevel0.logicalcpu", "hw.perflevel1.logicalcpu"]; /** * Injectable sysctl reader: raw `name: value` output for the given names. * Defaults to one `/usr/sbin/sysctl` exec. A name the kernel does not know is * simply absent from the output. */ export type SysctlFn = (names: readonly string[]) => Promise; /** Injectable per-pid RSS reader for {@link DarwinResourceBackend.sweepChildRss}. */ export type PsRssFn = (pids: readonly number[]) => Promise; /** Parsed darwin signals; every field is `null` when its sysctl was absent. */ export interface DarwinSignals { /** 1 normal, 2 warning, 4 critical. */ readonly pressureLevel: number | null; /** * `kern.memorystatus_level`: share of RAM (0–100) that is neither wired nor * compressed. (Named for the kernel's "free percentage"; it counts active * and inactive pages as well as free ones.) */ readonly freePercent: number | null; /** RAM the compressor occupies, in bytes (`vm.compressor_bytes_used`). */ readonly compressorBytes: number | null; readonly swapTotalBytes: number | null; readonly swapUsedBytes: number | null; /** * Memory the kernel can hand out without compressing or swapping, in bytes: * (free + speculative + file-backed + purgeable pages) × `hw.pagesize`. * `null` when the page size or the free count is unreadable. */ readonly reclaimableBytes: number | null; /** 1, 5 and 15 minute load averages. */ readonly loadAvg: readonly [number, number, number] | null; readonly ncpu: number | null; /** Performance-core logical CPUs (`hw.perflevel0`), when reported. */ readonly perfCores: number | null; /** Efficiency-core logical CPUs (`hw.perflevel1`), when reported. */ readonly efficiencyCores: number | null; } /** * Parse `sysctl ` output (`name: value` per line). * * @example * ```ts * parseDarwinSysctl('kern.memorystatus_vm_pressure_level: 2\nhw.ncpu: 18\n'); * // → { pressureLevel: 2, ncpu: 18, freePercent: null, ... } * ``` */ export declare function parseDarwinSysctl(output: string): DarwinSignals; /** Kernel pressure-level scores on the PSI `some avg10` scale (warning, critical). */ export declare const DARWIN_LEVEL_SCORES: Readonly<{ warning: 15; critical: 40; }>; /** * Share of RAM wired or compressed above which a kernel warning grows more * severe (one point per percentage point of RAM). */ export declare const DARWIN_WARN_SQUEEZE_KNEE = 0.5; /** * Squeeze range over which swap starts to count at a kernel warning, from not * at all to in full: a cold compressor means the swap is not active. */ export declare const DARWIN_SWAP_RAMP: readonly [number, number]; /** * The most headroom alone can score while the kernel says normal: the memory * gate's resume threshold ({@link MEMORY_GATE_RESUME_AT_OR_BELOW}, 15), below * backoff and the gate's refuse threshold (25). Low reclaimable memory can * narrow admission (hold, half the budget) but never refuse heavy work or * defer db-heavy (the sentient tick, exodus-on-open) on its own, and a gate * latched by a passing kernel warning releases once the kernel is back to * normal. Only a kernel warning or critical level goes past it (T13132, #1865 * review). */ export declare const DARWIN_NORMAL_HEADROOM_CAP = 15; /** * The headroom below which memory scores at any kernel level: a quarter of * RAM, at least one heavy worker's footprint ({@link GIB_PER_WORKER} GiB), and * never more than half of RAM (T13132, after the #1806 review: a fixed 6 GiB * was a sliver of a large machine). * * @param totalBytes - physical RAM in bytes. * * @example * ```ts * darwinHeadroomFloorBytes(48 * 1024 ** 3); // 12 GiB * darwinHeadroomFloorBytes(16 * 1024 ** 3); // 6 GiB * darwinHeadroomFloorBytes(8 * 1024 ** 3); // 4 GiB * ``` */ export declare function darwinHeadroomFloorBytes(totalBytes: number): number; /** * Memory available without compressing or swapping, in bytes: the * reclaimable page counts when readable, else the share of RAM neither wired * nor compressed. `null` when neither is known. * * @param s - parsed signals. * @param totalBytes - physical RAM in bytes. */ export declare function darwinHeadroomBytes(s: DarwinSignals, totalBytes: number): number | null; /** * Share of RAM (0–1) that is wired or compressed: `1 − memorystatus_level`, * or the compressor's share when that is larger or the level is unreadable. * `null` when neither is known. * * @param s - parsed signals. * @param totalBytes - physical RAM in bytes. */ export declare function darwinSqueeze(s: DarwinSignals, totalBytes: number): number | null; /** * Memory pressure score (PSI `some`-equivalent, 0–100) from darwin signals: * the higher of the kernel-verdict score (warning or critical, graded by * squeeze and swap) and the headroom score (see the module doc). Returns * `null` when neither the kernel level nor the squeeze is readable. * * @param s - parsed signals. * @param totalBytes - physical RAM in bytes (for the headroom and swap share). * * @example * ```ts * // kernel warning, 41% neither wired nor compressed, 13.6 GiB swap on 48 GiB * darwinMemorySome(signals, 48 * 1024 ** 3); // → 38.2: backoff * ``` */ export declare function darwinMemorySome(s: DarwinSignals, totalBytes: number): number | null; /** * Cores that absorb load: performance cores plus half the efficiency cores, * else `hw.ncpu`. `null` when neither is known. */ export declare function effectiveCores(s: DarwinSignals): number | null; /** CPU waiting share (0–100) for a load average on `ncpu` cores. */ export declare function cpuSomeFromLoad(load: number, ncpu: number): number; /** * Build the memory and CPU PSI-equivalents from parsed signals. * * @param s - parsed signals. * @param totalBytes - physical RAM in bytes. */ export declare function darwinPressure(s: DarwinSignals, totalBytes: number): { memory: PsiData | null; cpu: PsiData | null; }; /** Options for {@link DarwinResourceBackend}. */ export interface DarwinBackendOptions { /** SQLite `-wal` sidecars to watch (same role as on Linux). */ readonly walPaths?: readonly string[]; /** How long one sysctl read is reused. @defaultValue 2000 */ readonly cacheTtlMs?: number; /** Injectable sysctl reader (tests). Injected readers bypass the shared cache. */ readonly sysctlFn?: SysctlFn; /** Injectable `ps` reader (tests). */ readonly psRssFn?: PsRssFn; /** Injectable stat (tests). */ readonly statFileFn?: StatFileFn; /** Total RAM in bytes, for `memAvailableBytes`. @defaultValue os.totalmem() */ readonly totalMemBytes?: number; /** Clock (tests). @defaultValue Date.now */ readonly now?: () => number; } /** Reset the process-wide sysctl cache (tests). @internal */ export declare function resetDarwinSysctlCache(): void; /** * macOS implementation of {@link ResourceBackend}. * * `pressureAvailable` is `true` when the kernel memory signals were readable, * so `evaluateState` uses the PSI path rather than degraded mode. */ export declare class DarwinResourceBackend implements ResourceBackend { private readonly walPaths; private readonly cacheTtlMs; private readonly sysctlFn; private readonly psRssFn; private readonly statFileFn; private readonly totalMemBytes; private readonly now; private local; constructor(opts?: DarwinBackendOptions); /** At most one sysctl exec per TTL; on failure the sample is degraded. */ sample(): Promise; /** RSS via one `ps` exec (no PSS on macOS: `pssBytes` mirrors RSS). */ sweepChildRss(pids: readonly number[]): Promise; private readSysctl; } //# sourceMappingURL=darwin-backend.d.ts.map