import type { CanvasSource } from './source.js'; import { type Fault } from '../runtime/fault.js'; import type { NodeStatus, Lifecycle, NodeMeta, NodeRow } from './types.js'; export declare function activeFaultForDisplay(node: T, fault: Fault | null): Fault | null; export declare function hangingFor(node: NodeMeta): Fault | null; export interface NodeTelemetry { tokens_in?: number; } export declare function readNodeTelemetry(nodeId: string): NodeTelemetry; /** Format a token count as `Nk` (rounded down to nearest 1 k). */ export declare function fmtCtx(tokensIn: number | undefined): string; export declare function renderTreeFromSource(source: CanvasSource, rootId: string): Promise; export declare function renderForestFromSource(source: CanvasSource): Promise; export interface DashboardRow { node_id: string; name: string; status: NodeStatus; kind: string; mode: string; ctx_tokens: number; asks: number; /** The dir the node is pinned to (its cwd). Drives the browser's cwd-scope * filter + the All-dirs basename cue. */ cwd: string; /** The stable profile identity the node runs under. Drives canvas browse's * default profile filter; null only for historical no-profile nodes. */ profile_id?: string | null; /** ISO 8601 birth timestamp — drives the recency sort + the relative-age cue. */ created: string; /** terminal = one-shot worker (finalizes on push --final); resident = persistent * agent you come back to. Drives the browser's resident-only lifecycle filter. * Only populated by dashboardRowsAll (the browser snapshot). */ lifecycle?: Lifecycle; /** "Most recent activity" sort key — epoch ms. The cheap boot stats the node's * job/telemetry.json (rewritten on every turn_end at a DETERMINISTIC path, so * no meta read), falling back to `created`'s epoch when a node never ran a turn. * This is the cheap proxy for last-message recency the attention sort tie-breaks * on. Always set by dashboardRowsAll; optional only so synthetic test rows + the * scoped dashboardRows can omit it. (NB: the pi session-file mtime would be truer * but its path lives in meta.json at a non-deterministic location — reading it * per node would defeat the cheap boot, so telemetry mtime is used instead.) */ mtimeMs?: number; /** The node's spawn prompt (context/initial-prompt.md), trimmed + capped. Populated * LAZILY by loadPreview (selected-row preview) — NOT on the cheap boot path. * Indexed by super-search and shown in the preview panel when there is no query. */ goal?: string; /** EVERY user prompt across the node's pi session — the whole conversation, not just * the spawn prompt — joined + capped. Populated LAZILY by loadPreview (ONE session * read, folded with lastAssistant); undefined for never-revived nodes (no session * file). Powers whole-conversation super-search + the windowed preview snippet. */ prompts?: string; /** The node's LAST assistant message (text only), trimmed + capped. Populated LAZILY * by loadPreview (same single session read as `prompts`); undefined for a node whose * session has no assistant reply yet. Shown in the preview's reply block. */ lastAssistant?: string; /** True when the node is GENUINELY mid-turn right now — its `busy` marker exists * AND its broker pid is alive (not merely an `active`, between-turns node). The * live "is it generating?" cue. Computed on the cheap boot path for LIVE nodes * only (dormant rows are always false). */ streaming?: boolean; /** Set when the node is parked on an active fault. The otherwise-invisible * "stuck, awaiting recovery" window. Mutually exclusive with `streaming` * (hanging means the turn ended, so `busy` is gone); when set, * dashboardRowsAll forces `streaming:false`. Computed on the cheap boot path * for every node, then suppressed only for stale daemon-owned provider faults * whose broker pid is gone. */ hanging?: Fault | null; /** True when a viewer (focus row) is attached to the node — i.e. someone has it * open on screen. Set on the cheap boot path from one listFocuses() query. The * focus registry is local viewer state, so callers pass it separately from the * canvas source and remote snapshots always suppress it. */ viewed?: boolean; /** Lazy-enrichment guard (internal): true once enrichRow/enrichRows has folded in * the full label (meta), ctx tokens (telemetry), and ⚑ asks. Idempotency marker so * progressive paint can re-call enrich for a viewport every keystroke for free. */ enriched?: boolean; /** Lazy-enrichment guard (internal): true once loadPreview has read goal + the * session (prompts + lastAssistant). Idempotency marker for the selected row. */ previewLoaded?: boolean; } export declare function readGoalText(nodeId: string): string | undefined; interface SessionParts { prompts?: string; lastAssistant?: string; } export declare function readSessionParts(sessionFile: string | null | undefined): SessionParts; /** Did this node's engine ever PRODUCE something — an assistant message with real * text, a tool call, or non-empty reasoning? The signal that a node did work and * is not an empty shell. A never-revived node (no session file) is trivially * false; an assistant turn that is only an empty/aborted `thinking` stub does NOT * count (that's a node started and killed before it did anything). CONSERVATIVE * by design: ANY substance keeps the node, so the reap never deletes real work * — a tool-call-only turn (no final text) still counts. Drives reap-on-close/ * detach. One file read with an early exit on the first substantive turn. */ export declare function nodeHasAssistantMessage(sessionFile: string | null | undefined): boolean; /** Is the node GENUINELY mid-turn right now? The `busy` marker (touched on * agent_start, removed on agent_end) AND-ed with broker-pid liveness, so a stale * marker from a crashed pi reads false. This is the live "generating?" signal * — distinct from `status: 'active'`, which only means the engine never closed * (an active node is usually dormant between turns). Read directly off disk * (mirrors telemetry) to avoid inverting the canvas→runtime dependency. */ export declare function isStreaming(nodeId: string, piPid: number | null | undefined): boolean; /** "Most recent activity" sort key (epoch ms) read CHEAPLY: a `statSync` (no file * read, no parse) of the node's job/telemetry.json — rewritten on every turn_end at * a deterministic canvas-home path, so it tracks last-message recency without a meta * read. Falls back to `created`'s epoch when a node never ran a turn (no telemetry). * Never throws. */ export declare function sessionMtime(nodeId: string, created: string, remote?: boolean): number; /** Build one cheap-boot row from a raw db/source row. `remote` suppresses the * LOCAL disk reads (fault/streaming/telemetry-mtime/viewers) that are meaningless * for a truly remote canvas. A local row is viewed when either its tmux focus * row exists or its live broker reports a terminal/web viewer. */ export declare function dashboardRowFromNodeRow(row: NodeRow, focusedNodeIds: ReadonlySet, remote: boolean): DashboardRow; /** One row per node visible in the sub-DAG of `rootId` (including root), read * entirely through the source — the openDb-free sibling of `dashboardRows`. * Ticket counts come from ONE `source.ticketCountsForView(rootId)` call (the * API/remote attention projection), never `countTickets` (openDb). */ export declare function dashboardRowsFromSource(source: CanvasSource, rootId: string): Promise; /** CHEAP boot builder over a source — one row per node across the whole canvas, * fields that need no per-node meta/session read. Local API callers may supply * the node ids from one `listFocuses()` query so `viewed` remains visible without * coupling the canvas source to local viewer state. Remote sources ignore that * set. Local disk telemetry/fault/streaming are likewise suppressed remotely. */ export declare function dashboardRowsAllFromSource(source: CanvasSource, localFocusedNodeIds?: ReadonlySet): Promise; export declare function enrichRowsFromSource(source: CanvasSource, rows: DashboardRow[], asks?: Record): Promise; /** goal (initial-prompt.md) and session parts are local disk reads — suppressed * to undefined for a remote source. */ export declare function loadPreviewFromSource(source: CanvasSource, row: DashboardRow): Promise; export {};