/** * TUI live event streaming bridge. * * Provides an in-memory event buffer that the render cycle reads in addition * to the polled snapshot — enabling sub-250ms UI updates without waiting for * the 1-second disk poll. * * Four event sources are merged: * 1. Opencode host events via `api.event.on()` * 2. Fast-poll (250ms) of dispatch-*.json files for rolebox task transitions * 3. Fast-poll (250ms) of fnstate-*.json files for rolebox function transitions * 4. Fast-poll (250ms) of graph-events-*.ndjson files for rolebox graph-engine * transitions, read with a monotonic per-file line-offset delta so each * appended line surfaces exactly one event. * * Attention notifications are dispatched for error/timeout events. * * @module */ import type { TuiPluginApi } from "@opencode-ai/plugin/tui"; export interface DispatchStartEvent { type: "dispatch_start"; ts: string; taskId: string; agent: string; description?: string; sessionId: string; } export interface DispatchEndEvent { type: "dispatch_end"; ts: string; taskId: string; agent: string; status: "completed" | "cancelled"; } export interface DispatchErrorEvent { type: "dispatch_error"; ts: string; taskId: string; agent: string; error: string; status: "error" | "timeout"; } export interface FunctionActivateEvent { type: "function_activate"; ts: string; name: string; sessionId: string; phase: string; } export interface FunctionDeactivateEvent { type: "function_deactivate"; ts: string; name: string; sessionId: string; } export interface SessionOpenEvent { type: "session_status"; ts: string; sessionId: string; opencodeStatus: string; } export interface SessionErrorEvent { type: "session_error"; ts: string; sessionId?: string; errorMessage?: string; } export interface GraphNodeStartEvent { type: "graph_node_start"; graphId: string; nodeId: string; agent: string; /** Node lifecycle status — `node_dispatched` lines always carry `running`. */ status: string; ts: string; } export interface GraphNodeEndEvent { type: "graph_node_end"; graphId: string; nodeId: string; agent: string; status: string; signalType?: string; ts: string; } export interface GraphSignalEvent { type: "graph_signal"; graphId: string; status: string; ts: string; } export type RoleboxEvent = DispatchStartEvent | DispatchEndEvent | DispatchErrorEvent | FunctionActivateEvent | FunctionDeactivateEvent | SessionOpenEvent | SessionErrorEvent | GraphNodeStartEvent | GraphNodeEndEvent | GraphSignalEvent; /** * Lightweight ring buffer for rolebox events. * * Supports push, drain, and peek operations. The render cycle calls * `drain()` to consume new events since the last read. */ export declare class EventBuffer { private buffer; private readonly capacity; constructor(capacity?: number); /** Push one or more events into the buffer, evicting oldest if over capacity. */ push(...events: RoleboxEvent[]): void; /** Return all buffered events and clear the buffer. */ drain(): RoleboxEvent[]; /** Peek at buffered events without clearing them. */ peek(): readonly RoleboxEvent[]; /** Number of events currently in the buffer. */ get size(): number; /** Clear all events. */ clear(): void; } /** * Fold drained graph events into the live-signal maps the engine-graph * display reads between disk snapshots (the 1s snapshot lags the 250ms poll). * * Two maps are maintained, one per event scope, so unrelated vocabularies * never share a slot: * * - **graph-level** `graphId → status`: fed ONLY by `graph_signal` events, * whose `status` is the engine phase (`idle` / `executing` / `complete`). * Node events are deliberately excluded so `signalType` (`answer` / * `revise_needed`) can never be conflated with engine phase. * - **node-scoped** `` `${graphId}::${nodeId}` → status ``: fed by * `graph_node_start` (status `running`) and `graph_node_end` (its terminal * `status` field). * * Pure and total — never throws, never mutates the input maps; callers hold * the returned copies as the new live state. */ export declare function foldGraphSignals(events: readonly RoleboxEvent[], graphSignals: ReadonlyMap, nodeSignals: ReadonlyMap): { graphSignals: Map; nodeSignals: Map; }; /** * Monotonic per-file line-offset delta reader over `graph-events-*.ndjson`. * * Each {@link poll} scans every `graph-events-*.ndjson` file in `stateDir` and * emits exactly one {@link RoleboxEvent} per newly-appended complete line. A * per-file character offset is advanced past the last complete line (one that * ends with `\n`); any trailing partial line is left unread and re-read on the * next poll until its terminating newline arrives, so a partial or corrupted * final line is never double-emitted. Independent of the snapshot-oriented * {@link readGraphEvents} (which returns the last N events regardless of read * position) — this reader is purely incremental. * * Never throws: a missing directory / unreadable file degrades to no events. */ export declare class GraphEventPoll { private readonly stateDir; private readonly offsets; constructor(stateDir: string); /** Return new {@link RoleboxEvent}s since the last poll (empty on none). */ poll(): RoleboxEvent[]; /** Forget all tracked offsets (e.g. on dispose). */ reset(): void; } export interface EventBridge { /** The event buffer — read by the render cycle. */ buffer: EventBuffer; /** Stop polling and cleanup subscriptions. */ dispose: () => void; /** Whether attention notifications are enabled (reads from api.attention). */ attentionEnabled: boolean; } /** * Create the event bridge for the TUI plugin. * * Must be called from within the TUI plugin setup function where `api` is * available. Returns an EventBridge with the event buffer and dispose handle. */ export declare function createEventBridge(api: TuiPluginApi, workspaceDir: string): EventBridge; //# sourceMappingURL=events.d.ts.map