import type { NodeMeta } from './types.js'; /** pi's InteractiveMode.MAX_WIDGET_LINES — the hard cap on lines in a string * array widget; anything beyond it pi truncates with its own "... (widget * truncated)". Our GRAPH viewport stays at/under this and scrolls internally. */ export declare const PI_MAX_WIDGET_LINES = 10; export declare const VIEWPORT_FALLBACK_ROWS = 30; export declare const ESC = "\u001B["; export declare const RESET = "\u001B[0m"; export declare const BOLD = "\u001B[1m"; export declare const DIM = "\u001B[2m"; export declare const REVERSE = "\u001B[7m"; /** Dark-green background bar marking an ATTACHED node (a human is currently * watching it) — distinct from the cursor's reverse-video bar; chosen so * default-fg text stays readable. */ export declare const BG_ATTACHED = "\u001B[48;5;22m"; export declare const GREEN = "\u001B[32m"; export declare const RED = "\u001B[31m"; export declare const YELLOW = "\u001B[33m"; export declare const CYAN = "\u001B[36m"; export declare const GRAY = "\u001B[90m"; /** Overlay-aware glyph for a node: hanging ⚠ outranks streaming ⟳, which * outranks the base status glyph. `remote` skips the local `readFault` / * `isPidAlive` / `isBusy` reads entirely (a remote node id may collide with a * local one) and renders the bare status glyph instead. */ export declare function nodeGlyph(node: NodeMeta | null, remote?: boolean): string; /** Visible width, ignoring ANSI escapes. */ export declare function visibleWidth(s: string): number; /** Truncate to `max` VISIBLE columns: escape sequences are copied through * verbatim (so a cut never lands mid-escape) and the result always ends in * RESET, so a clipped style can't bleed into the editor below. */ export declare function truncate(s: string, max?: number): string; /** Visible columns available to ONE widget line — the cap for every line we * emit, and the width a full-width reverse-video SELF bar fills to. * * pi does NOT clip widget lines; it WRAPS them. Each string line is wrapped in * a `Text(paddingX = 1)` inside a full-terminal-width container, so the usable * content width is `columns - 2` (a 1-col margin on each side). A line wider * than that wraps, and the overflow spills onto a second row as a stray * reverse-video block (the bug this guards against). Clamp to `columns - 2`. */ export declare function fillWidth(): number; /** Wrap `content` in a full-width background bar opened by `open` (REVERSE for * the cursor, BG_ATTACHED for a human-watched node). `open` is re-asserted after every * embedded RESET so a coloured cell (the status dot) can't punch a hole in the * bar; the visible width is padded out to `width`; the line closes with a real * RESET so the style never bleeds into the editor below. */ export declare function fillBar(content: string, width: number, open: string): string; /** Clear the per-frame telemetry cache. Called from nav-model's beginFrame so * the graph half and the render half share one frame boundary. */ export declare function beginFrameTelem(): void; export interface Telemetry { /** Live context-window size (pi's getContextUsage) — the figure pi's own footer * shows. This is the node's window fill, NOT a per-turn delta. */ context_tokens?: number; /** One-line "what is it doing" cue (`tool: detail`), written on every tool start. */ last_activity?: string; } export declare function readTelemetry(nodeId: string): Telemetry; export declare function fmtTokens(n: number): string; /** The context-window cell — live window fill rounded to the nearest 1k (the same * figure pi's footer shows), NOT a per-turn token delta. `remote` skips the local * `telemetry.json` read entirely and reports 0 (a remote node id may collide * with a local one). */ export declare function tokensCell(id: string, remote?: boolean): string; /** Dimmed live "what is it doing" cue for an ACTIVE node — the last tool it ran. * Empty for non-active rows (stale once a node parks) and when none is recorded. * `remote` skips the local `telemetry.json` read entirely and reports empty. */ export declare function activityCell(id: string, node: NodeMeta | null, remote?: boolean): string; /** Revive-count badge (meta.cycles). Hidden on the first cycle (0) to cut noise. */ export declare function cycleBadge(node: NodeMeta | null): string; /** Short on-screen label: the explicit handle when set, else the pi-generated * description, else the bare name. fullName joins BOTH (`handle description`); * the nav chrome shows just the first so rows stay compact. */ export declare function navLabel(node: NodeMeta | null, id: string): string; export declare function shortId(id: string): string; /** True when a human is watching `id` right now via the broker viewer count * (attach.json). The tmux-focus axis (a `focuses` row) is a canvas-db read and * stays in nav-model's `isAttached`, which ORs this signal with the focus set. * `remote` skips the local `attach.json` read entirely and reports false (a * remote node id may collide with a local one). */ export declare function hasBrokerViewers(id: string, node: NodeMeta | null, remote?: boolean): boolean; /** True when a human is watching `id` right now (tmux focus or broker viewer). * `focused` is the tmux-focus set (a canvas-db read the caller supplies). The * broker-viewer half is a pure `attach.json` read. `remote` skips the local read * entirely and reports false (a remote node id may collide with a local one). */ export declare function isAttached(id: string, node: NodeMeta | null, focused: ReadonlySet, remote?: boolean): boolean; /** ⚑K pending-asks badge for a node, read from the supplied per-node ask map. */ export declare function askBadge(id: string, asks: Record): string; /** "Live work below" badge — green ⇣N when this node is NOT itself active but has * N active descendants. The one signal that an idle/parked node isn't dead: real * work is still running beneath it. Count comes from the single subtree-activity * pass (no per-row walk). Suppressed on active rows (their own ● already says so). */ export declare function liveBelowBadge(node: NodeMeta | null, activeBelow: ReadonlyMap): string; export declare function fetchAsksMap(rootId: string): Record; export interface FlatRow { id: string; hasKids: boolean; isSelf: boolean; branch: string; cycle: boolean; collapsed: boolean; } /** Manual fold OVERRIDES, keyed by id. `userCollapsed` forces a fold, `userExpanded` * forces an unfold; both override the default activity-driven policy. Held by the * consumer (the extension / viewer) and threaded into buildGraphModel so this layer * carries no mutable singletons. */ export interface FoldState { userExpanded: ReadonlySet; userCollapsed: ReadonlySet; } /** One bottom-up O(N) pass result over the local graph (see computeSubtreeActivity * in nav-model.ts): * - `expand`: which nodes auto-UNFOLD — a node expands when a child subtree holds * a running ('active') agent or self. * - `activeBelow`: per node, the count of ACTIVE descendants (excluding itself). */ export interface SubtreeActivity { expand: Set; activeBelow: Map; } /** Total lines the GRAPH widget may emit. pi hard-caps extension widgets at * MAX_WIDGET_LINES — anything past that pi truncates itself, eating our own * scroll chrome — so never exceed it (and shrink on a very short terminal). * The viewport scrolls WITHIN this cap as the cursor moves. */ export declare function graphWidgetBudget(): number; export declare const GRAPH_HINT = "\u001B[2mjk move \u00B7 hl fold \u00B7 \u21B5 focus \u00B7 e expand \u00B7 x kill \u00B7 m mgr \u00B7 esc\u001B[0m";