import { type SystemctlRunner, type SystemctlResult } from './systemd-scope.js'; export type Logger = (line: string) => void; /** cgroup v2 `memory.events` is `key value` lines; return the `oom_kill` * count (cumulative cgroup-OOM kills in this scope), or 0 if absent. */ export declare function parseMemoryEventsOomKill(blob: string): number; /** Pull every `claude-session-*.scope` unit that systemd logged a terminal * `Failed with result 'oom-kill'` for out of a journalctl blob. That line * fires only when the scope's main process (the claude leader) is OOM- * killed — i.e. the whole-scope teardown, the case `memory.events` misses. * The survivor line ("The kernel OOM killer killed some processes in this * unit.") is deliberately NOT matched: a surviving scope is caught by the * memory.events read instead. Deduped, first-seen order. */ export declare function parseOomKilledScopes(stdout: string): string[]; /** Global (system-wide, non-cgroup) kernel OOM-kill lines from `journalctl * -k` (the kernel ring — distinct from the `--user` journal * pollJournalKills reads). Matches the classic * `Killed process () ... anon-rss:kB` summary line every * Linux OOM report emits, converts anon-rss from kB to bytes. Returns every * match in the blob, in order — the caller filters to pids it actually * tracks. */ export declare function parseKernelOomVictims(stdout: string): Array<{ pid: number; rssBytes: number; }>; export declare function formatOomVictim(f: { sessionId: string; pid: number; rssBytes: number; }): string; export declare function formatCapKill(f: { sessionId: string; scope: string; oomKills: number; memCurrentMb: number; }): string; export declare function formatCapKillFromJournal(f: { sessionId?: string; scope: string; }): string; /** True only when the unit's `ActiveState` is actually `active`. Any other * state (inactive, failed, activating, not-found) is "the manager believes * this session is alive but its scope is not there" — the Task 1297 * reconciliation this exists for. */ export declare function isScopeActive(activeState: string): boolean; export declare function formatScopeMissing(f: { sessionId: string; scope: string; state: string; }): string; /** Logs `op=scope-missing` at most once per unit until it becomes active * again (the same event-log dedup convention as `capKillLogged`/ * `oomVictimLogged`) — an unbounded per-tick re-log would defeat the * task's own goal of a clean, attributable signal. Clearing the entry on * recovery means a later loss is re-logged, not permanently suppressed. */ export declare function checkScopeMissing(f: { activeState: string; sessionId: string; scope: string; scopeMissingLogged: Set; logger: Logger; /** True when the systemctl binary is absent (darwin). There are no systemd * scopes to reconcile against, so "scope not active" is expected, not a * Task 1297 reconciliation miss — skip the log (Task 1403). */ systemctlAbsent?: boolean; }): void; /** `systemctl --user show -p ActiveState -p ControlGroup` (key=value * lines, order-independent) -> both values in one systemctl call. Task * 1297 originally issued a separate `is-active` call per live session per * tick alongside this one; merged here so the reconciliation check and the * cap-kill check share a single spawn per session per tick instead of two. */ export declare function parseScopeStatus(result: SystemctlResult): { activeState: string; controlGroup: string; absent: boolean; }; /** Synchronous runner for `journalctl `, same shape as * `SystemctlRunner`. Tests inject a recording stub. */ export type JournalctlRunner = (args: readonly string[]) => SystemctlResult; export declare function defaultJournalctlRunner(args: readonly string[]): SystemctlResult; /** Window cursor for the journal read. `lastSinceMs` is the lower bound of * the next query (epoch ms); null on the very first poll (no backfill). */ export interface JournalPollState { lastSinceMs: number | null; } /** One journal poll, folded into the audit tick. Reads systemd's durable * whole-scope OOM-kill record since the last window, emits one * `source=journal` line per scope NOT already recorded by the * memory.events path (de-dup by scope unit), records each emitted scope in * `capKillLogged` at `tickIndex`, and advances the window. On a journalctl * failure it logs `op=journal-read-failed` and leaves the window * unadvanced, so the next tick re-queries the same span. Pure given its * injected `run`/clock; mutates the passed `capKillLogged` and `state`. */ export declare function pollJournalKills(deps: { run: JournalctlRunner; nowMs: number; marginMs: number; tickIndex: number; capKillLogged: Map; resolveSessionId: (unit: string) => string | undefined; state: JournalPollState; logger: Logger; }): void; /** One kernel-journal poll, folded into the same audit tick as * pollJournalKills. A global (system-wide) OOM kill is invisible to the * memory.events/scope-failure paths above — the kernel picks a victim * outside any cgroup ceiling, so those never fire. This is the correlation * Task 1297's incident review found completely missing: the killed pid * could not be tied to a session from any available journal field. Scoped * to pids the manager actually tracks (a monitored session's PTY leader) — * a global OOM victim outside that set (neo4j, the brand server, an * untracked process) is not this module's concern and is not logged. */ export declare function pollKernelOomVictims(deps: { run: JournalctlRunner; nowMs: number; marginMs: number; tickIndex: number; oomVictimLogged: Map; resolveSessionId: (pid: number) => string | undefined; state: JournalPollState; logger: Logger; }): void; /** Drop `capKillLogged` (or the Task 1297 `oomVictimLogged`) entries older * than `ttlTicks` so the map stays bounded. A reported scope/pid's journal * line falls out of the `--since` window within ~1 interval, so it can * never reappear after the ttl. */ export declare function pruneCapKillLogged(map: Map, tickIndex: number, ttlTicks: number): void; /** Same TTL-prune rule as pruneCapKillLogged, generalized to a map whose * value is a record carrying the last-seen tick rather than a bare number * (unitToSessionId, pidToSessionId) — `getTick` extracts it. */ export declare function pruneByLastSeen(map: Map, tickIndex: number, ttlTicks: number, getTick: (v: V) => number): void; export interface SessionCapAuditDeps { intervalMs: number; logger: Logger; runSystemctl?: SystemctlRunner; runJournalctl?: JournalctlRunner; now?: () => number; } export interface SessionCapAudit { start(): void; stop(): void; tickOnce(): void; } export declare function createSessionCapAudit(deps: SessionCapAuditDeps): SessionCapAudit; //# sourceMappingURL=session-cap-audit.d.ts.map