e11d1ddc7f
Hosted agents can be OOM-killed hourly while the dashboard and the NAS agent card both look perfectly healthy — every memory signal the gateway already produces (heartbeat mem samples, lifecycle-ledger unclean-exit verdicts, cache-pressure evictions) dies in server-side log files. The BlueAtlas incident (NS-608) ran for three days like this. This is the read-side fix: * New gateway/memory_status.py distills the existing 30s loop heartbeat (gateway RSS + system MemAvailable/MemTotal + swap) and the lifecycle sentinel into a compact `memory` block: pressure ok/elevated/critical/ unknown, coarse MB numbers, and last-boot unclean/suspected-OOM flags. Pure file reads, no new sampling, no gateway IPC. Stale (>150s) or future-dated heartbeats degrade pressure to "unknown" so a dead gateway's final gasp can't render a live "critical" banner forever. Critical thresholds mirror the ledger's OOM-suspicion heuristics: if a level would make a later unclean death "suspected OOM", warn at that level while the process is still alive. * lifecycle_ledger.record_startup now carries prior_unclean_exit / prior_suspected_oom onto the reclaimed sentinel — previously the verdict survived only in append-only diag prose. Flags age out on the next sentinel rewrite (scoped to the life after the crash). * /api/status serves the block (profile-aware, executor-offloaded, fail-safe to pressure=unknown). Deliberately NOT folded into components/overall: memory pressure is advisory, and flipping overall to "degraded" on it would page NAS's availability sweep for a condition the eviction valve is already handling. Public-safety: coarse numbers/enums/booleans only — same disclosure class as the existing nous_session_valid field, added for the same NAS-sweep audience. * Dashboard: new MemoryPressureBanner (app-shell, next to ProfileScopeBanner) with worst-first trigger precedence (critical > suspected-OOM restart > elevated), per-trigger session-scoped dismissal, and escalation re-opening past a dismissal. i18n keys optional with English fallbacks, matching the managingProfileBanner convention. Tests: gateway/test_memory_status.py (classification bands, staleness, clock skew, corrupt files, bool-is-not-int), lifecycle sentinel carry-forward, /api/status contract (block always present, collector crash degrades instead of 500), and 7 banner component tests. NAS-side ingestion (agent-card notice + memory-tier upsell) ships separately. Refs NS-656; context: NS-608, NS-657, OOF-77.