/**
* Runner — consumer-facing interface for every primitive/composition/pattern.
*
* Pattern: Facade (GoF) over the footprintjs FlowChart + EventDispatcher.
* Role: The one object consumers hold. Exposes:
* - `.run()` (execute)
* - `.getSpec()` (the design-time FlowChart blueprint —
* same value footprintjs's `addSubFlowChart*` accepts)
* - `.on() / .off() / .once()` (listener subscription)
* - `.attach()` (attach custom CombinedRecorder)
* - `.emit()` (consumer-defined custom events on the same
* dispatcher, matches DOM CustomEvent)
* Emits: N/A — this file defines the INTERFACE. Concrete runners
* (LLMCall, Agent, Sequence, etc.) implement it.
*/
import type { CombinedRecorder, FlowChart, FlowChartExecutor, FlowchartCheckpoint, RunOptions } from 'footprintjs';
import type { RunnerPauseOutcome } from './pause.js';
import type { EventListener, ListenOptions, Unsubscribe, WildcardListener, WildcardSubscription } from '../events/dispatcher.js';
import type { AgentfootprintEvent, AgentfootprintEventType } from '../events/registry.js';
import type { FlowchartHandle, FlowchartOptions } from '../recorders/observability/FlowchartRecorder.js';
import type { LocalObservabilityHandle, LocalObservabilityOptions } from '../recorders/observability/localObservability.js';
import type { ObservabilityEnableOptions, CostEnableOptions, LiveStatusEnableOptions } from '../strategies/attach.js';
/**
* High-level feature-enable methods. Each attaches a pre-built observability
* recorder and returns an Unsubscribe function. Additional methods land in
* Phase 5 (lens, tracing, cost, guardrails, ...).
*/
export interface EnableNamespace {
/**
* Live composition graph — subflow / fork-branch / decision-branch
* nodes accumulate as execution unfolds. Hook into any graph renderer
* (React Flow, Cytoscape, D3) without touching footprintjs internals.
*
* Returns a handle with `getSnapshot()` so the UI can query the graph
* at any time (not just via onUpdate). Wires the boundary recorder's
* three connections, commit tracking included, so the recording it
* leaves behind can be replayed with its step strip intact.
*/
flowchart(opts?: FlowchartOptions): FlowchartHandle;
/**
* Tier-3 / Debug — RETAIN a live run model: watch it via `onLive` (a
* fresh `StepGraph` per event, for your own renderer) AND freeze it for
* OFFLINE replay via `handle.getTrace()` / `onRecorded`.
*
* This handle is NOT a Lens recorder — `` wants a
* `LensRecorder`, a different object with a different surface. To put a
* run in front of Lens, record it with `recordRun()` and hand the
* recording to lens's `observeRecording()`.
*
* Contrast `observability({ strategy })` below (Tier-4 / Monitor), which
* ships each event to a vendor and forgets. `localObservability` keeps the
* model so you can look at it — locally, with full content. The serialized
* `Trace` is redactable at the serialize boundary (`redact` / `getTrace`).
*/
localObservability(opts?: LocalObservabilityOptions): LocalObservabilityHandle;
/**
* v2.8+ — grouped strategy enabler for observability. Pipes every
* typed event into a vendor strategy (Datadog, OTel, AgentCore,
* CloudWatch, …) or the default `consoleObservability()`. See
* `agentfootprint/strategies` + `docs/inspiration/strategy-everywhere.md`.
*/
observability(opts?: ObservabilityEnableOptions): Unsubscribe;
/**
* v2.8+ — grouped strategy enabler for cost. Subscribes the strategy
* to `cost.tick` events; defaults to `inMemorySinkCost()` for
* read-back / test inspection.
*/
cost(opts?: CostEnableOptions): Unsubscribe;
/**
* v2.8+ — grouped strategy enabler for chat-bubble live status.
* Maintains the thinking-state machine; calls strategy.renderStatus
* each time the rendered line changes (deduped — not on every token).
* Strategy is required (consumer must wire UI).
*/
liveStatus(opts: LiveStatusEnableOptions): Unsubscribe;
}
/**
* Every primitive (LLMCall, Agent), every composition (Sequence, Parallel,
* Conditional, Loop), and every pattern factory result implements Runner.
* That makes them freely nestable: any runner can be a child of any
* composition.
*/
export interface Runner {
/**
* Return the footprintjs FlowChart for this runner — the canonical
* design-time blueprint. Stable across calls. Pairs with the run-time
* accessors (`getLastSnapshot`, `getCommitCount`) and matches
* `ExplainableShell.spec` + `specToReactFlow(spec, ...)` consumer
* conventions.
*
* Its `buildTimeStructure` field is the CHART — the one ingredient a
* finished run does not leave behind, and the only route to a drawable
* graph. Save it alongside the snapshot when storing a run (or let
* `recordRun()` do it).
*
* Subflow mounting (footprintjs `addSubFlowChart*`) accepts the
* `FlowChart` value directly:
*
* parent.addSubFlowChartNext('sf-agent', child.getSpec(), 'Agent')
*/
getSpec(): FlowChart;
/**
* The footprintjs snapshot of the most recent run — shared state, the
* commit log, the execution tree, and every attached recorder's data.
* `undefined` before the first run.
*
* The canonical structure: consumers read this for shape and join
* their own per-stage payload by `runtimeStageId`, rather than
* re-deriving structure from typed events.
*/
getLastSnapshot(): ReturnType | undefined;
/**
* How many commits the run has written so far — the run's TIME AXIS.
*
* One commit lands per executed stage, in order, so the count sampled
* at a moment is that moment's position in the run. Observers stamp it
* to record WHEN they fired — it is what `boundaryRecorder({
* getCommitCount })` puts on every boundary, and the only reason a
* step strip can be rebuilt from a stored recording later.
*
* `0` before the first run; cumulative across `resume()` on the same
* executor. Sample it live (a closure), never a captured number.
*/
getCommitCount(): number;
/**
* Return the consumer-shaped UI group for this runner — produced by
* invoking the `groupTranslator` (if one was attached at constructor
* time) with this composition's metadata. Returns `undefined` when no
* translator was attached.
*
* Companion of `getSpec()`: `getSpec()` is the canonical (UI-
* agnostic) blueprint; `getUIGroup()` is the consumer-shaped view.
* Both are stable post-construction.
*
* See `core/translator.ts` for the `GroupTranslator` /
* `GroupMetadata` types.
*/
getUIGroup(): T | undefined;
/**
* Translate this runner's group metadata with a CALLER-SUPPLIED
* translator that OVERRIDES whatever translator (if any) the runner
* was constructed with. Used by parent compositions to apply
* per-method translator overrides (e.g.,
* `Parallel.create(...).branch('special', runner, { groupTranslator: ... })`
* — for the `'special'` branch only, this `override` runs against
* `runner`'s own `GroupMetadata` instead of the runner's default
* translator).
*
* NOT cached at the runner level. The caller invokes this exactly
* once per build (parent's `buildUIGroupMetadata`) and caches the
* resulting `uiGroup` via the parent's `RunnerBase.uiGroupCache`.
*
* Returns `undefined` when this runner has no group metadata to
* translate (i.e., `buildUIGroupMetadata()` returned `undefined`).
*/
getUIGroupWith(override: import('./translator.js').GroupTranslator): T | undefined;
/**
* Execute the runner. On happy-path completion, resolves with `TOut`.
* If any stage (Agent tool via `pauseHere`, nested runner, or consumer
* scope code) called `scope.$pause()`, resolves with a `RunnerPauseOutcome`
* carrying the serializable checkpoint. Discriminate with `isPaused()`.
*/
run(input: TIn, options?: RunOptions): Promise;
/**
* Resume a previously-paused execution from its checkpoint. `input` is
* delivered to the paused stage's resume handler. The same return shape
* as `run()`: `TOut` on completion, `RunnerPauseOutcome` if execution
* pauses again (e.g., a multi-step approval flow).
*/
resume(checkpoint: FlowchartCheckpoint, input?: unknown, options?: RunOptions): Promise;
/**
* Subscribe a typed listener. Returns unsubscribe.
*
* Lifecycle: the subscription lives until you call the returned
* Unsubscribe, the `{ signal }` you passed aborts, or
* `removeAllListeners()` runs. Nothing auto-expires per-run — pass a
* per-run AbortSignal for request-scoped listeners on long-lived
* runners (servers).
*/
on(type: K, listener: EventListener, options?: ListenOptions): Unsubscribe;
/** Subscribe to a domain wildcard (e.g. 'agentfootprint.context.*') or '*'. */
on(type: WildcardSubscription, listener: WildcardListener, options?: ListenOptions): Unsubscribe;
/** Unsubscribe a previously-registered listener. */
off(type: K, listener: EventListener): void;
off(type: WildcardSubscription, listener: WildcardListener): void;
/** Subscribe a one-shot listener (fires once then auto-removes). Accepts `{ signal }`. */
once(type: K, listener: EventListener, options?: Omit): Unsubscribe;
once(type: WildcardSubscription, listener: WildcardListener, options?: Omit): Unsubscribe;
/**
* Drop every event listener on this runner in one call (typed,
* domain-wildcard, and `'*'`). Lifecycle escape hatch for server
* consumers that can't keep Unsubscribe handles. Also removes
* listeners wired by `enable.*` strategies; does NOT detach recorders
* added via `attach()`.
*/
removeAllListeners(): void;
/**
* Diagnostic — listeners currently retained. No argument = total
* (the leak-detection number); with a subscription key = that exact
* bucket only (wildcards not folded in — use the dispatcher's
* `hasListenersFor` semantics for "would anything fire").
*/
listenerCount(type?: AgentfootprintEventType | WildcardSubscription): number;
/**
* Attach a footprintjs CombinedRecorder to observe the execution.
* Returns an unsubscribe function — call it to detach the recorder
* from future runs. (Already-running executions continue using it.)
*
* Recorders live for the RUNNER's lifetime: nothing auto-expires
* per-run, and `removeAllListeners()` does not touch them. The caller
* owns cleanup via the returned Unsubscribe.
*/
attach(recorder: CombinedRecorder): Unsubscribe;
/**
* Enable-namespace for high-level observability features. Each method
* attaches a pre-built CombinedRecorder and returns an unsubscribe
* function. Consumers write ONE line to enable rich observability,
* instead of N `.on()` subscriptions.
*/
readonly enable: EnableNamespace;
/**
* Emit a consumer-defined custom event through the same dispatcher.
*
* Matches DOM CustomEvent. Useful for domain-specific events outside
* the 47-event registry (e.g. `myapp.billing.checkpoint`). Library
* events are reserved under the `agentfootprint.*` namespace.
*/
emit(name: string, payload: Record): void;
}
/** Kept for convenience — mirrors the RunOptions export from footprintjs. */
export type { RunOptions };
/**
* Union used in emit() for the `AgentfootprintEvent` type constraint. A
* consumer emitting a custom event passes a plain object payload; the
* dispatcher wraps it as AgentfootprintEvent only when the name is a
* registered type. Otherwise it flows through as an opaque custom event.
*/
export type EmittedEvent = AgentfootprintEvent;