footprintjs - v11.2.0
    Preparing search index...

    Interface SubflowMountOptions<TParentScope, TSubflowInput, TSubflowOutput>

    interface SubflowMountOptions<
        TParentScope = any,
        TSubflowInput = any,
        TSubflowOutput = any,
    > {
        arrayMerge?: ArrayMergeMode;
        convergeAt?: string;
        inputMapper?: (
            parentScope: TParentScope,
            binding?: NativeMappingBinding,
        ) => TSubflowInput;
        outputMapper?: (
            subflowOutput: TSubflowOutput,
            parentScope: TParentScope,
            binding?: NativeMappingBinding,
        ) => Record<string, unknown>;
        propagateBreak?: boolean;
        tags?: readonly string[];
    }

    Type Parameters

    • TParentScope = any
    • TSubflowInput = any
    • TSubflowOutput = any
    Index
    arrayMerge?: ArrayMergeMode

    Controls how array values from outputMapper are merged into parent scope. Applies only to top-level array keys. Nested arrays inside objects always use append (appendToArray) regardless of this setting.

    ArrayMergeMode.Concat
    
    convergeAt?: string

    STRUCTURE-ONLY: the downstream stage id this branch's convergence edge points to, overriding the default "converge at the next linear stage."

    When a fork/selector/decider branch carries convergeAt, its visualized next edge is fired to the named stage instead of to the shared convergence node its siblings use. This expresses an unequal-depth merge — e.g. a tools slot that bypasses the messageAPI assembly node and pairs with its output at call-llm (so call-llm reads as a 2-parent merge), faithful to the Anthropic wire protocol (system+messages = the message payload; tools = a separate top-level field).

    This affects the STRUCTURE/topology graph only — the runtime is unchanged (the branch's outputs ride shared scope; the named target reads them when it runs). It does NOT introduce a join barrier. The target is a FORWARD stage (added later in the chain), so it is not validated at branch-declaration time.

    inputMapper?: (
        parentScope: TParentScope,
        binding?: NativeMappingBinding,
    ) => TSubflowInput
    outputMapper?: (
        subflowOutput: TSubflowOutput,
        parentScope: TParentScope,
        binding?: NativeMappingBinding,
    ) => Record<string, unknown>

    Maps subflow output back into parent scope.

    Array merge behavior is controlled by arrayMerge:

    • 'concat' (default): [...existing, ...value] — return only delta items
    • 'replace': overwrites the parent value — return the full array

    Scalar values are always replaced regardless of arrayMerge.

    // Delta mode (default) — return only new items, concat appends
    outputMapper: (sf) => ({ messages: sf.newMessages })

    // Replace mode — return full array, parent value is overwritten
    // Useful in Dynamic loops where the subflow recomputes the full value each iteration
    outputMapper: (sf) => ({ toolDescriptions: sf.toolDescriptions }),
    arrayMerge: ArrayMergeMode.Replace,

    // Scalars are fine in both modes — always replaced
    outputMapper: (sf) => ({ status: sf.result, count: sf.total })
    propagateBreak?: boolean

    When true, an inner scope.$break(reason) call inside this subflow propagates up to the parent — i.e., the parent's breakFlag is set after the subflow exits, terminating the parent's outer loop too.

    Default: false (current behaviour — inner break stops only the subflow; parent continues).

    Set propagateBreak: true on subflow mounts that represent terminal branches — "if this subflow fires, the outer loop is done". Examples:

    • A human-review runner that takes over from an agent's tool-calling loop and produces the final response.
    • A safety-gate subflow that halts the outer workflow when a policy violation is detected.
    • An error-recovery subflow that restores state and then terminates.
    1. Inside the subflow, a stage calls scope.$break(reason).
    2. The subflow's own execution stops (normal $break behaviour).
    3. SubflowExecutor inspects the subflow's exit state. If propagateBreak === true AND the inner break fired, it forwards the break (and its reason) to the parent's breakFlag.
    4. The parent traverser sees shouldBreak on its next step and exits.
    5. A FlowRecorder.onBreak event fires at the parent-mount level with propagatedFromSubflow = the subflow's id and the inner reason.

    A fork child runs with its own break flag, and every fan-out call site passes no parent break flag to ChildrenExecutor. So a propagateBreak: true subflow mounted as a fork child stops its own branch only; the parent fan-out never breaks, even when every child broke.

    The subflow's outputMapper (if supplied) ALWAYS runs before the break propagates, so the subflow's partial state is still written to the parent scope. This is intentional — the typical use case is an "escalation" subflow whose output IS the final answer that needs to land in the parent scope before the outer loop terminates. If you want to suppress output mapping on break, check the break state inside your outputMapper and return {} early.

    false
    
    tags?: readonly string[]

    Declared tags (9.21.1) for the MOUNT itself — the NAMES the author puts on this mount at build time, landed by the same applyTags every other options.tags site uses, with the same refusals (empty, non-string, the reserved ~ marker, a duplicate, a second declaration).

    A mount is a stage that commits: its first bundle in the PARENT log is a stop in commitStops, so tagStops keeps it like any tagged stage and carries these names as Stop.meta. Stamped on that first bundle only (the mount's exit bundle carries none — the once-per-execution law). The subflow's OWN log is untouched: its inner stages declare their own tags, and drill(mount) reads those off the inner log.

    This is the ONLY tag site for a fork-child mount (addSubFlowChart) or a branch mount (addSubFlowChartBranch) — after those the cursor still points at the parent, so .tag() refuses rather than mis-attribute. A linear mount (addSubFlowChartNext) moves the cursor, so .tag() works there too; declare in one place, not both.

    .addSelectorFunction('Slots', pickSlots, 'slots')
    .addSubFlowChartBranch('memory', memoryChart, 'Memory', { tags: ['slot:memory'] })
    .addSubFlowChartBranch('tools', toolsChart, 'Tools', { tags: ['slot:tools'] })
    .end()