Skip to content

StatusPoller

Defined in: src/poller.ts:86

new StatusPoller(store, herdr, onChange, onBlock, intervalMs?, reclassifyMs?, classify?, now?, probe?, stallCfg?, probeCheckMs?, onReady?, onActivity?, preview?, liveness?, onWorkingBlocked?, pruneHooks?, onStopWindow?, onHalt?, usageLimits?): StatusPoller

Defined in: src/poller.ts:206

SessionStore

Pick<HerdrDriver, "list" | "read" | "readAsync">

(id, status) => void

(id, block) => void

number = 1000

number = 3000

(text) => BlockReason

() => number

(s) => TranscriptSignals

Both transcript-derived signals (stall snapshot + activity) for a running session, from a SINGLE read+parse of its JSONL. Defaults to reading the file; injectable in tests. One read feeds both the stall decision and the activity emit, so the transcript is no longer parsed twice per running agent per tick.

StallConfig = DEFAULT_STALL

number = 7000

(id, ready) => void

Pushed when a session’s manual readyToMerge flag is auto-cleared on resume.

(id, activity) => void

Pushed when a running session’s heartbeat or current activity changes.

Partial<PreviewWiring>

Preview wiring: injectable for tests; defaults to real PreviewService + real scan/pick + config.previewSweepMs. Omit to leave preview disabled (existing poller tests that don’t pass this still work).

Partial<LivenessWiring>

Claude-liveness wiring: injectable for tests; defaults to the real /proc scan with a no-op onChange. Drives session:claude-alive so the UI only offers Resume when the claude process is actually gone (husk shell).

(id, working) => void

Pushed when a session enters/leaves the working-while-blocked display state (herdr latched “blocked” but the TUI shows a live turn spinner). true fires once per suppression episode, false when the episode ends — same tick as (and before) any re-armed block emission.

(activeIds) => void

Phase-1 (issue #704): prune the HookIngest ring buffers for sessions no longer active, called from pruneInactive so dead sessions’ buffers don’t grow unbounded by session count (Task-2 reviewer flag). Undefined ⇒ no-op (the ingest module isn’t wired, e.g. in tests / flag off).

((id, windowMs) => void) | undefined

Observe-only Stop↔herdr-done window emitter (issue #713), flag-gated. Called once per resolved pairing with the SIGNED offset between a Stop hook and herdr’s done flip: windowMs > 0 ⇒ Stop arrived first (stop-wins); windowMs <= 0 ⇒ herdr flipped first (herdr-wins); windowMs === null ⇒ a done-flip never paired (no Stop within the horizon). Pure measurement — never mutates status/routing. Defaults to a single greppable [hooks] log line; tests inject a capturer.

(id, haltReason, haltedAt) => void

Pushed when a session’s transcript signals a usage-limit halt at the done transition. Wired in src/index.ts to emit session:halt. Defaults to a no-op so existing tests that omit it still compile. Optional (undefined ⇒ no-op) so callers can skip it with a positional undefined after onStopWindow.

Usage-limits service — provides the latest window percentages for the corroboration check in classifyHalt. Injectable for tests; defaults to a stub that returns fully-null limits (uncalibrated fallback). Optional (undefined ⇒ stub) so callers can skip it.

StatusPoller

acknowledgeStall(id): boolean

Defined in: src/poller.ts:1166

Manually clear a stall flag without re-arming it: broadcasts the clear but keeps lastSig so maybeProbe’s once-per-episode guard suppresses an immediate re-fire. The episode re-arms on its own when activity resumes (the !isStalled path in maybeProbe calls clearBlock), so a later genuine stall still surfaces. No-op (returns false) unless a stall is live.

string

boolean


activitySnapshot(): Record<string, SessionActivity>

Defined in: src/poller.ts:620

Last-emitted activity signal per running session, for client bootstrap.

Record<string, SessionActivity>


claudeAliveSnapshot(): Record<string, boolean>

Defined in: src/poller.ts:393

Last-swept claude-process liveness per session, for client bootstrap.

Record<string, boolean>


ingestActivity(id, ev): void

Defined in: src/poller.ts:637

Phase-1 push activity (issue #704), fed by HookIngest.onSignal for both PostToolUse (status:"ok") and PostToolUseFailure (status:"error"). Builds a SessionActivity from the tool name + a windowed heat-strip tick and routes it through the existing dedup emitActivity — the SAME sink the poll path uses, so push + poll never bypass the dedup or oscillate. A PostToolUse push carries a REAL tool summary (vs. the interim heartbeat’s summary:null), so it beats the interim emit on the freshness guard below.

No-op when config.hooksSignals is off (the sink is never wired in that case, so this is belt-and-suspenders for direct callers/tests). Records lastHookActivityAt so maybeProbe knows the push path is fresh.

string

"ok" | "error"

string

number

void


ingestNotification(id, type): void

Defined in: src/poller.ts:680

Phase-1 push notification (issue #704), fed by HookIngest.onSignal for Notification events. An awaiting-input type (see BLOCK_NOTIFICATION_TYPES) sets a marker consumed by reconcileAgent to classify THIS tick — surfacing the block ≤ ~1 tick after the agent asks, independent of herdr’s latchy blocked status. idle_prompt clears the marker (idle is handled by herdr mapping; never force a block). Any other type is a no-op here — unknown types are already logged upstream, and an awaiting-input edge we haven’t confirmed simply stays on the existing herdr-blocked → classifyBlocked fallback (no regression).

No-op when config.hooksSignals is off.

string

string

void


ingestSessionStart(id): void

Defined in: src/poller.ts:696

Phase-2 push lifecycle (issue #709): a SessionStart hook confirms the agent booted. Flips claude-liveness → true immediately (ahead of the throttled liveness sweep), reusing the sweep’s own map + flip-dedup so push + poll never double-emit. Boot liveness is monotonic (true until exit), so this cannot oscillate with the sweep, which will agree (the process is alive — the hook fired from inside it). No-op when config.hooksSignals is off. (Stop/SessionEnd consumption is deferred to #713 — observe-only this phase.)

string

void


ingestStopMeasure(id, stopAt): void

Defined in: src/poller.ts:715

Observe-only Stop measurement (issue #713), fed by HookIngest.onSignal for Stop events. Records the Stop’s receive time as one half of the Stop↔herdr-done pairing. If a done-flip is already pending within the horizon, herdr won this turn’s race — emit the (≤0) window immediately and clear the pending done. Otherwise park the Stop so the next done-flip in measureStopWindow can pair it (a stale prior pending stop is silently overwritten — Stop is a per-turn edge, only the latest matters).

Pure measurement: never touches status, routing, or any block/activity state — polling stays authoritative. No-op when config.hooksSignals is off.

string

number

void


start(): void

Defined in: src/poller.ts:1314

void


stop(): void

Defined in: src/poller.ts:1317

void


tick(): void

Defined in: src/poller.ts:321

void


workingBlockedSnapshot(): Record<string, boolean>

Defined in: src/poller.ts:398

Sessions currently in the working-while-blocked display state, for client bootstrap.

Record<string, boolean>