/** * Graph Execution Engine v2 — Stateless `graph_status` Render Helpers * * Version: 2.0 * Date: 2026-09-18 * * The render / format half of the `graph_*` tool layer, extracted from the * 3333-line `GraphToolSet` god-module (FIX-PLAN Y30). Every function here is a * free function over explicitly-passed inputs — no registry, no engine wiring, * no `this` — so the toolset class keeps only the stateful surface (registry, * deps, engine assembly, the eight tool entry points) while these helpers stay * directly unit-testable. The public toolset contract is unchanged. * * Contract notes: * - Every reader returns REAL recorded engine state or an explicit * honest-empty result; none fabricates rows (see `status-queries.ts`). * - Signatures take `(state, args, …)` in that order wherever both are * needed, mirroring the `graph_status` entry point. * - {@link flagData} is the pure replacement for the old mutating * `mergeFlagData(target, …)`: it returns the C-WIRE flag keys to spread onto * a snapshot, so the `graph_status` JSON shape is carried by * `GraphStatusSnapshot` / `GraphNodeSummary` / `GraphLoopSummary` instead of * being assembled through a `Record` (Y27). * - {@link checkpointEntries} reads `checkpointHistory` as the authoritative * record and treats `checkpoints` as its derived latest-snapshot view; the * legacy fallback is surfaced with a warning rather than passing silently * (Y9). */ import type { MaterializedResultRef } from "../../dispatch/types.ts"; import type { CheckpointRecord, EngineState, GraphBudgetState, NodeRuntimeState, RoundHistoryEntry, SignalLedgerEvent } from "../../types.engine-v2.ts"; import type { GraphDeclaration, LoopMode } from "../../types.graph-v2.ts"; import type { PersistedStateScan } from "./persisted-state.ts"; import type { GraphStatusArgs } from "./graph-tools.ts"; /** Default `graph_status` output cap (chars) when `max_chars` is unset. */ export declare const DEFAULT_MAX_CHARS = 16000; /** One loop group's requested round history (see {@link loopRoundEntries}). */ export interface GraphRoundHistoryEntry { loop_id: string; rounds: RoundHistoryEntry[]; requested_round?: number; } /** One node's recorded lifecycle checkpoints (see {@link checkpointEntries}). */ export interface GraphCheckpointEntry { node_id: string; checkpoints: CheckpointRecord[]; } /** One node's recorded artifacts / evidence (see {@link artifactsEvidenceEntries}). */ export interface GraphArtifactsEvidenceEntry { node_id: string; artifacts?: string[]; evidence?: string[]; } /** One node's timestamped signal-event history (see {@link signalStreamEntries}). */ export interface GraphSignalStreamEntry { node_id: string; events: SignalLedgerEvent[]; } /** * The C-WIRE observability keys {@link flagData} contributes to a * `graph_status` JSON snapshot, and onto the node- / loop-scoped summaries. * Every key is optional and present only for the flag that produced it, so * default output stays byte-identical. */ export interface GraphFlagData { round_history?: GraphRoundHistoryEntry[]; checkpoints?: GraphCheckpointEntry[]; artifacts_evidence?: GraphArtifactsEvidenceEntry[]; signal_stream?: GraphSignalStreamEntry[]; } /** Graph-level budget breakdown (see {@link budgetSummary}). */ export interface GraphBudgetSummary { graph: GraphBudgetState; nodes: Array<{ node_id: string; sessions: number; tokens: { input: number; output: number; }; cost: number; }>; } /** One loop group's summary rows (see `GraphToolSet.loopSummary`). */ export interface GraphLoopSummary { loop_id: string; traversals: string; nodes: string[]; consecutive_stale: number; mode?: LoopMode; } /** A node's latest progress signal (see {@link progressForNode}). */ export interface NodeProgressSummary { /** Whether the node's ledger carries a `progress` entry at all. */ recorded: boolean; /** The recorded progress payload (any JSON value), when present. */ payload: unknown; /** * The node's LAST SIGNAL time of ANY type (`SignalLedgerEntry.lastSignalAt`), * not a progress-specific stamp — a recency anchor for the payload. */ lastSignalAt: number | undefined; } /** * Copy a declaration deeply enough that a later mutation of the caller's * object graph cannot reach the committed registry entry (Y31). One level per * collection: nodes / edges / loop groups / budget are fresh objects, and the * arrays that hang off an edge mapping are copied by their callers before the * declaration is built. */ export declare function shallowCloneDeclaration(d: GraphDeclaration): GraphDeclaration; /** * Atomically write `content` to `exportPath`: write to a sibling * `..tmp` file, then rename it into place. Renaming is atomic on * POSIX filesystems, so a reader never observes a partially-written target * and no `.tmp` artifact remains after a successful write. */ export declare function writeAtomic(exportPath: string, content: string): void; /** Read a materialized node result from its sidecar file, best-effort. */ export declare function resultText(ref: MaterializedResultRef): string; /** * Resolve the member node ids of a loop group from the graph **declaration**. * The runtime `LoopGroupRuntimeState` does not carry the member list; it lives * on `graphDeclaration.loop_groups`. */ export declare function loopNodeIds(state: EngineState, loopId: string): string[]; /** * Resolve a loop group's declared session-isolation `mode` from the graph * **declaration**. Like the member list, the mode lives on * `graphDeclaration.loop_groups` (the runtime `LoopGroupRuntimeState` does not * carry it). Returns `undefined` when unset — callers must omit it from output * to keep the default render byte-identical. */ export declare function loopDeclMode(state: EngineState, loopId: string): LoopMode | undefined; /** * Materialize the node set narrowed to `nodeFilter` as a `Map` * (or the whole state node map when no filter is active). Used by the * `group_by` view, which needs a keyed node set rather than an id list. */ export declare function visibleNodeMap(state: EngineState, nodeFilter?: Set): ReadonlyMap; /** * Build the set of node ids matching the active filter/query args, or return * `undefined` when no filter is present (the renderer then shows all nodes). * The matching is delegated entirely to the pure `status-queries.ts` module. */ export declare function buildNodeFilter(state: EngineState, args: GraphStatusArgs): Set | undefined; /** Sum the cumulative budget consumption across the graphs in scope. */ export declare function crossSessionBudget(states: EngineState[]): GraphBudgetState; /** True when a filter/group_by/include_budget view is active (drives the * cross-session aggregate rather than the plain graph list). */ export declare function crossSessionViewRequested(args: GraphStatusArgs): boolean; /** Honest-empty note for a persisted store that yielded no hydrated graph. */ export declare function persistedEmptyNote(scan: PersistedStateScan): string; /** * Extract a node's recorded `progress` signal, if any, from the engine state. * * The graph engine records every signal a node emits into both * `node.signalsObserved[type]` and the graph-level `state.signalLedger[nodeId]` * (`signal-bridge.ts:record`). `progress` is an INFO signal (one of * `INFO_SIGNALS`), so — when a node emitted progress during execution — its * latest payload is genuinely available here. The ledger read goes through the * shared `getSignal` / `SIGNAL_KEY` seam (contract C2 / Y8), so a missing or * malformed ledger answers `undefined` instead of throwing. Note this is the * **latest** payload per node, not a timestamped multi-event history (the * design's `dispatch_stream`-style `since`-based history is unbacked — see * `UNSUPPORTED_GRAPH_STATUS_FLAGS` `stream`/`since`). * * `lastSignalAt` is the node's LAST SIGNAL time of ANY type — the graph-level * `SignalLedgerEntry.lastSignalAt` (updated by signal-bridge.ts:record on * every signal, progress or not), NOT a progress-specific stamp. It rides along * with the progress payload so a consumer gets a recency anchor, but it is * named for what it actually is. */ export declare function progressForNode(state: EngineState, node: NodeRuntimeState): NodeProgressSummary; /** * Extract the per-loop round history from `LoopGroupRuntimeState.rounds[]`, * scoped to one loop (when `args.loop_id`) and optionally filtered to a single * `args.round`. Sorted ascending by round index. `rounds` is OPTIONAL-ADDITIVE * — absent (or empty) until a round is recorded; never fabricated. */ export declare function loopRoundEntries(state: EngineState, args: GraphStatusArgs): GraphRoundHistoryEntry[]; /** * Extract per-node lifecycle checkpoints from `EngineState.checkpointHistory` * (`Record` — the ordered, append-only list), * scoped to a node when `nodeId` is given. * * `checkpointHistory` is the authoritative record (every transition, earliest * first — `types.engine-v2.ts`); `EngineState.checkpoints` is its derived * latest-snapshot view, retained for backward compat with pre-history persisted * states. When a node has no history at all but does carry the derived snapshot, * this falls back to that single snapshot and emits an explicit degradation * warning naming the node (Y9) — the fallback is never silent. Absent until a * checkpoint is recorded. */ export declare function checkpointEntries(state: EngineState, nodeId?: string): GraphCheckpointEntry[]; /** * Extract per-node artifacts / evidence from `NodeRuntimeState.artifacts[]` / * `.evidence[]`, scoped to a node when `nodeId` is given. Nodes with no * recorded array for a requested flag are omitted from that entry (honest * absence — never invented values). */ export declare function artifactsEvidenceEntries(state: EngineState, args: GraphStatusArgs, nodeId?: string): GraphArtifactsEvidenceEntry[]; /** * Extract per-node timestamped signal-event histories from * `SignalLedgerEntry.history[]`, scoped to a node when `nodeId` is given. * When `args.since` is a valid ISO-8601 timestamp, events strictly before it * are filtered out; an INVALID `since` throws (aligned with the * from_date/to_date filter surface). Sorted ascending by `atMs`. An * absent/empty `history` yields an empty event list — the caller surfaces the * honest "no events" note. */ export declare function signalStreamEntries(state: EngineState, args: GraphStatusArgs, nodeId?: string): GraphSignalStreamEntry[]; /** * True when any of the seven C-WIRE observability flags is active. When none * are set, the flag sections / flag data are omitted and the base render is * returned byte-identical to legacy output. */ export declare function flagSectionsActive(args: GraphStatusArgs): boolean; /** * Build the structured C-WIRE flag data for a JSON snapshot (json formats). * Returns an empty object when no flag is active, so spreading the result keeps * the snapshot byte-identical otherwise. Data is extracted from the same * genuine engine fields as the text renderers — the pure replacement for the * old mutating `mergeFlagData(target, …)` (Y27). */ export declare function flagData(state: EngineState, args: GraphStatusArgs): GraphFlagData; /** * Render the graph's node dependency tree, optionally narrowed to * `nodeFilter` and pruned at `depth` levels (0 = roots only; `undefined` = * full depth, byte-identical to legacy output). Children come from the * declaration's edges; loop back-edges are annotated and never recursed into. */ export declare function renderTree(state: EngineState, nodeFilter?: Set, depth?: number): string; /** Graph + per-node budget breakdown for the `include_budget` JSON view. */ export declare function budgetSummary(state: EngineState): GraphBudgetSummary; /** One-line phase + per-status node counts, e.g. `phase=executing running=2`. */ export declare function metricsSummary(state: EngineState): string; /** Apply max_chars / offset / tail pagination to a string output. * * Monitor L3: when truncation actually drops content, a `…[truncated: N more * chars]` marker is APPENDED to the tail of the returned text (N = the number * of chars NOT included in the result), so a consumer can tell the output was * cut and by how much. The marker is emitted for both tail mode (head * dropped) and head mode (tail dropped) — the returned slice is always * `max_chars` chars, the marker rides after it. No truncation → no marker * (byte-identical to legacy output). */ export declare function paginate(text: string, args: GraphStatusArgs): string; //# sourceMappingURL=status-render.d.ts.map