/** * builder/types.ts — All types used by the builder library. * * Shared types (StageNode, StageFunction, etc.) are imported from the engine. * Builder-specific types (FlowChartSpec, FlowChart, SerializedPipelineStructure) * are defined locally — they carry builder-only fields (description, outputMapper, etc.). * * NOTE: All engine imports are `import type` — zero runtime dependency. * The builder remains standalone at runtime. */ import type { StageNode } from '../engine/graph/StageNode.js'; import type { ILogger, ScopeFactory, StageFunction } from '../engine/types.js'; import type { ScopeProtectionMode } from '../scope/protection/types.js'; import type { StructureRecorder } from './structure/StructureRecorder.js'; export type { ResumeFn, StageNode } from '../engine/graph/StageNode.js'; export type { ILogger, StageFunction, StreamCallback, StreamHandlers, StreamLifecycleHandler, StreamTokenHandler, SubflowMountOptions, } from '../engine/types.js'; export { ArrayMergeMode } from '../engine/types.js'; /** Relaxed-generic alias for builder ergonomics. */ export type StageFn = StageFunction; export type { ScopeProtectionMode }; export interface SerializedPipelineStructure { name: string; id: string; type: 'stage' | 'decider' | 'selector' | 'fork' | 'streaming' | 'subflow' | 'loop'; /** Semantic icon hint for visualization (e.g., "llm", "tool", "rag", "agent", "start") */ icon?: string; description?: string; children?: SerializedPipelineStructure[]; next?: SerializedPipelineStructure; hasDecider?: boolean; hasSelector?: boolean; branchIds?: string[]; loopTarget?: string; isStreaming?: boolean; streamId?: string; isParallelChild?: boolean; parallelGroupId?: string; isSubflowRoot?: boolean; subflowId?: string; subflowName?: string; /** * Nested pipeline structure for a subflow node. * WARNING: Any future walker that traverses this field recursively must apply its own * depth guard (see MAX_WALK_DEPTH in contract/openapi.ts). The current `buildDescription` * walk in openapi.ts does NOT traverse subflowStructure — if it ever does, the depth * guard must cover both the `next` chain and this nested structure. */ subflowStructure?: SerializedPipelineStructure; iterationCount?: number; /** True when this subflow uses lazy resolution (deferred until execution). */ isLazy?: boolean; /** True when this node is a back-edge reference created by loopTo() — not an executable stage. */ isLoopReference?: boolean; /** When true, this stage can pause execution (PausableHandler pattern). */ isPausable?: boolean; /** * STRUCTURE-ONLY: for a fork/selector/decider branch, the downstream stage * id its convergence `next` edge points to, instead of the shared next-stage * its siblings converge at. Set from `SubflowMountOptions.convergeAt`; read by * `_fireNextEdgeFromParent` to express an unequal-depth merge (e.g. tools → * call-llm, bypassing message-api). Visualization-only — runtime convergence * is unchanged. */ convergeAt?: string; } export interface FlowChartSpec { name: string; id: string; /** Node type — matches `SerializedPipelineStructure.type` for visualization alignment. */ type?: 'stage' | 'decider' | 'selector' | 'fork' | 'streaming' | 'subflow' | 'loop'; /** Semantic icon hint for visualization (e.g., "llm", "tool", "rag", "agent", "start") */ icon?: string; description?: string; children?: FlowChartSpec[]; next?: FlowChartSpec; hasDecider?: boolean; hasSelector?: boolean; branchIds?: string[]; loopTarget?: string; isStreaming?: boolean; streamId?: string; isParallelChild?: boolean; parallelGroupId?: string; isSubflowRoot?: boolean; subflowId?: string; subflowName?: string; /** True when this node is a back-edge reference created by loopTo() — not an executable stage. */ isLoopReference?: boolean; } /** * Options-bag argument shape for the `flowChart()` factory. */ export interface FlowChartOptions { /** * Build-time recorders to attach BEFORE `start()` fires. Equivalent * to chaining `.attachStructureRecorder(rec)` immediately after the * factory returns — but registered EARLIER, so even the seed event * fires through the dispatcher without needing the seed-replay path. * * Multiple recorders attach in array order (same as * `.attachStructureRecorder` repeated). See `StructureRecorder` JSDoc * for event semantics, ordering invariants, and the trust model. */ structureRecorders?: StructureRecorder[]; /** Free-form description shown on the root spec node. */ description?: string; /** * Only meaningful for `flowChartSelector` (a root selector). When the * selector picks ≥2 branches they fan out in parallel: `failFast: true` * uses `Promise.all` (first branch error rejects + aborts), the default * uses `Promise.allSettled` (best-effort — all branches run even if some * fail). Use fail-fast when all selected branches are REQUIRED. Ignored for * non-selector charts. Same flag `addSelectorFunction` / `addListOfFunction` * expose. */ failFast?: boolean; } export type FlowChart = { root: StageNode; stageMap: Map>; subflows?: Record; }>; buildTimeStructure: SerializedPipelineStructure; enableNarrative?: boolean; logger?: ILogger; description: string; stageDescriptions: Map; /** Input schema (Zod or JSON Schema) — declared via setInputSchema() or .contract(). */ inputSchema?: unknown; /** Output schema (Zod or JSON Schema) — declared via setOutputSchema() or .contract(). */ outputSchema?: unknown; /** Output mapper — extracts response from final scope. */ outputMapper?: (finalScope: Record) => unknown; /** Scope factory — auto-embedded by flowChart(). Executor reads this if no factory param. */ scopeFactory?: ScopeFactory; }; export type SimplifiedParallelSpec = { id: string; name: string; fn?: StageFunction; }; export type ExecOptions = { defaults?: unknown; initial?: unknown; readOnly?: unknown; throttlingErrorChecker?: (e: unknown) => boolean; scopeProtectionMode?: ScopeProtectionMode; enableNarrative?: boolean; }; export interface SubflowRef { $ref: string; mountId: string; }