/** * The one "needs you" rule (`GET /system.attention[]`, api-types 0.140.0) and its companion, * `unresolved[]`. * * The operator's rule: anything that is work stuck (the system slowing or preventing it, work that * cannot move) is counted, prioritized by impact. So `attention[]` holds one item per ROOT CAUSE * holding work up right now: * * - `prevents`: work cannot move until a person acts (an open gate, a stalled run, a recent * failure, a down component, a seat gap with no alternative); * - `slows`: the system itself slows work (host load). * * One cause that holds several runs is ONE item, naming them in `blockedRunIds`. A merge needs * evidence, never a similar label: the same repo failing the same check on a base that fails it * too (plus the run whose stated problem IS that red base, when it waits at a gate: its gate is the * item's action); the same down component a held run's denial names; the same seats leaving runs * with no distinct judge. Seat problems fold into the run item they block. * * What is NOT stuck never counts, and is never hidden either (operator, 2026-10-27: "don't let * the system go green with stale jobs"): superseded runs, failed runs older than 7 days and asks * parked at their turn gate past a day move to `unresolved[]`, each with what it cost and how to * resolve it. Answered asks are not stuck. Pending proposals are `toReview`. * * Order (DEFINITION §4): runs held, then prevents before slows, then a component before one run's own * cause (a component also holds every new run), then the longest wait (the gate's arrival, never the * run's launch), then the units left. * * Pure: every input is a reading the route already made. */ import type { SessionView, SystemAttentionItem, SystemCli, SystemInfraKey, SystemInfraRow, SystemRun, SystemUnresolvedItem } from '../core/types.js'; /** A failed run launched longer ago than this is history to resolve, not a need. */ export declare const FAILED_FRESH_MS: number; /** An ask parked at its turn gate longer than this is unresolved work. */ export declare const PARKED_AFTER_MS = 86400000; export interface RunCost { costUsd: number | null; tokens: { input: number; output: number; } | null; } export interface NeedsInput { now: number; /** Every run the engine lists (terminal and archived ones too), decorated. */ views: readonly SessionView[]; /** `runs[]`: the unfinished runs with computed liveness and their open gate. */ runs: readonly SystemRun[]; infra: readonly SystemInfraRow[]; clis: readonly SystemCli[]; /** Runs with no distinct judge in their pool (`judgeGaps`). */ gaps: ReadonlyArray<{ run: SystemRun; creator: string | null; blockedBy: SystemCli[]; }>; /** Whether a down component holds every live run (the container floor's gateway, …). */ infraHoldsLive: (key: SystemInfraKey) => boolean; /** Per-run cost, when its log was read; absent ⇒ unknown. */ costOf?: (runId: string) => RunCost | undefined; } export interface Needs { attention: SystemAttentionItem[]; unresolved: SystemUnresolvedItem[]; unresolvedSpend: { costUsd: number | null; tokens: { input: number; output: number; } | null; costKnown: number; }; } /** "1 job", "4 jobs": a count in words, never "job(s)". */ export declare const plural: (n: number, one: string, many?: string) => string; /** * Which run replaced this one, if any: a run naming it in `retry_of`, or a LATER run with the same * repo, workflow and title that completed or delivered. A failed run with a retry is resolved by * that retry (`retried`); anything else superseded is still the operator's to close. */ export declare function supersession(views: readonly SessionView[]): Map; /** An ask's TURN gate (answered by the chat's next message): a terminal gate with every unit done. * An ask held at an escalation is a real gate (68c26704). */ export declare function isAnsweredAsk(v: SessionView, run: SystemRun | undefined): boolean; export declare function buildNeeds(input: NeedsInput): Needs; /** The level: `red` when an attention item is red; `yellow` when anything needs a look OR is * unresolved; `green` only when both are empty. */ export declare function needsLevel(attention: readonly SystemAttentionItem[], unresolved?: readonly SystemUnresolvedItem[]): 'green' | 'yellow' | 'red'; /** Fold one run's event log into what it cost (`cliUsage` frames). `null` fields when none carried it. */ export declare function foldRunCost(events: ReadonlyArray<{ type?: unknown; [k: string]: unknown; }>): RunCost; /** Per-run cost, read once per terminal run (it cannot change) and briefly for an open one. The * snapshot never waits past its bound: a run not read yet reads unknown, and the next read has it. */ export declare class RunCostCache { private readonly ttlMs; private readonly now; private readonly done; private inFlight; constructor(ttlMs?: number, now?: () => number); get(runId: string): RunCost | undefined; /** Read every listed run not cached, stale, or whose `stamp` moved (a failed run resumed and ended * again has a new end: its cost is read again); resolves when all are read. */ fill(runs: ReadonlyArray<{ id: string; final: boolean; stamp?: string; }>, read: (id: string) => Promise | null>): Promise; } //# sourceMappingURL=needs.d.ts.map