/**
 * buildAgentChart — assemble the agent's full footprintjs FlowChart
 * from stage functions + slot subflows + memory wiring.
 *
 * This is the "chart composition" that used to live inline in
 * `Agent.buildChart()`. Extracted for v2.11.2 so:
 *
 *   1. Agent.ts focuses on Agent class lifecycle (constructor, run,
 *      attach, getSpec) instead of chart wiring details.
 *   2. The reliability gate chart (v2.11.x) wires into ONE focused
 *      file rather than surgically into Agent.ts's 250-line composition
 *      block.
 *   3. The composition is independently readable + reviewable —
 *      consumers building custom agent shapes have a reference.
 *
 * Chart shape:
 *
 *     Initialize
 *       → [memory READ subflows for each .memory()]
 *       → InjectionEngine (subflow)               ← loop target (tool-calls loops here)
 *       → Context (selector, PARALLEL fan-out, failFast)
 *             ⇉ {System Prompt ‖ Messages ‖ Tools}  (slot subflows)
 *             → converge
 *       → UpdateSkillHistory
 *       → Cache (sf-cache subflow: decideCacheMarkers → CacheGate
 *                → ApplyMarkers / SkipCaching)
 *       → CallLLM (also emits the per-iteration iteration_start marker)
 *       → [NormalizeThinking] (subflow, only when a ThinkingHandler resolved)
 *       → Route (decider)
 *             ├─ tool-calls (pausable) → loopTo(InjectionEngine)   ← branch-sourced loop
 *             └─ final (subflow) → terminal leaf
 *                          ┌────── PrepareFinal
 *                          ├──── [memory WRITE subflows]
 *                          └──── BreakFinal ($break)
 *
 * (This chart has no reliability subflow, and never grew one. The plan
 * described here — "the reliability gate chart mounts as a subflow before
 * CallLLM with a TranslateFailFast stage after it. Lands in the next
 * commit." — did not land: `.reliability()` is implemented INLINE in the
 * CallLLM stage by `executeWithReliability`, and
 * `buildReliabilityGateChart` is reachable from no shipped path. Verified
 * 2026-07-28.)
 */
import type { FlowChart, StructureRecorder } from 'footprintjs';
import type { CachePolicy } from '../../cache/types.js';
import type { MemoryDefinition } from '../../memory/define.types.js';
import { readerWindowsOf } from '../../time/windows.js';
import { type TimeLimitFacts } from '../../semantics/coverage/timeLimitFacts.js';
import { type AnswerMountDeps, type InputsMountDeps, type ResultsMountDeps } from './honesty/mounts.js';
import type { RouteBranch } from './stages/route.js';
/**
 * Stage handlers + slot subflows the chart composer needs. Mostly
 * passed through verbatim from Agent.buildChart() — the chart shape
 * is identical to what was inline before.
 */
export interface AgentChartDeps {
    /** Memory READ/WRITE pipeline definitions (one per `.memory()`). */
    readonly memories: readonly MemoryDefinition[];
    /** Evidence bridge (#5): `causalEvidenceRecorder().collect`, threaded into
     *  CAUSAL memories' write mounts so snapshots persist real evidence
     *  (decisions/toolCalls/iterations/duration/tokens) instead of zeros.
     *  Set by the Agent when any mounted memory is CAUSAL. */
    readonly causalEvidenceSource?: () => import('../../memory/causal/evidenceRecorder.js').RunEvidence;
    /** Cache policy for the system-prompt slot, threaded into
     *  CacheDecision's inputMapper so its decision rules can match. */
    readonly systemPromptCachePolicy: CachePolicy;
    /** Hard ReAct iteration cap, threaded into CacheDecision's
     *  inputMapper for max-iteration policies. */
    readonly maxIterations: number;
    readonly seed: (scope: never) => void | Promise<void>;
    readonly callLLM: (scope: never) => Promise<void>;
    readonly routeDecider: (scope: never) => RouteBranch | Promise<RouteBranch>;
    readonly toolCallsHandler: import('footprintjs').PausableHandler<never>;
    /**
     * The schema re-ask branch (7.26). Present ONLY when the agent was built
     * with `.outputSchema(parser, { retries })` — and when present it is a
     * THIRD branch of the Route decider carrying the same `{ loopTo }` the
     * tool branch does, because a re-ask is one more ordinary turn.
     *
     * Conditional mount, for the reason every conditional mount here exists:
     * an agent that did not opt in gets no branch, no stage, no scope key and
     * no event — its chart and its commit log are the ones it always had.
     */
    readonly outputRetryStage?: (scope: never) => void;
    readonly injectionEngineSubflow: FlowChart;
    /** Relevance entry router (`entryByRelevance`) — a once-per-turn function stage
     *  mounted before the InjectionEngine (off the ReAct loop). Present only when the
     *  skill graph was built with a relevance scorer. */
    readonly pickEntryStage?: (scope: never) => Promise<void>;
    /**
     * The turn-start routing CASCADE stage (SG-C) — subsumes PickEntry on
     * graphs that run it (`classify` configured, or `continuity:
     * 'conversation'`). Mounted in the SAME slot under the SAME id
     * (`STAGE_IDS.PICK_ENTRY`) so recorded structures stay stable; never
     * present together with `pickEntryStage` (Agent.buildChart picks exactly
     * one). Absent → the chart is byte-identical to 9.16.
     */
    readonly routeTurnStage?: (scope: never) => Promise<void>;
    /**
     * The window-strategy stage (`.window()` / `.compaction()`). Present ONLY
     * when the consumer configured a strategy — and when present it BECOMES the
     * ReAct loop target, mounted immediately before the current one.
     *
     * It has to be the loop target rather than merely sit in the loop body:
     * the loop is branch-sourced (`tool-calls → { loopTo }`), so anything ahead
     * of the target runs once and is never seen again. Being the target also
     * puts the window change BEFORE the injection engine and the slots, which
     * is the point — the triggers, the three slots and the wire then all see
     * the same window, and no component gets a different past than the model
     * does.
     *
     * `strategyName` rides along so the chart itself says which policy is
     * mounted; a reader of the graph should not have to guess.
     */
    readonly windowStage?: {
        readonly strategyName: string;
        readonly run: (scope: never) => Promise<void>;
    };
    /**
     * The messages-slot DELIVERY stage (7.21). Present ONLY when the agent has
     * something that could target the messages slot — a registered injection
     * declaring `inject.messages`, or any `.memory()` whose recall might format
     * as a non-system role. When absent the chart is the one it always was, so
     * an agent with nothing to deliver is byte-identical to 7.20.
     *
     * Mounted between the InjectionEngine and the Context fan-out: after the
     * engine has decided what is active, before anything reads the window. The
     * placement is the design — a delivered message has to be part of the past
     * that the slots project, the cache decision indexes, and the wire sends,
     * or the recording and the request would disagree about the conversation.
     */
    readonly deliverStage?: (scope: never) => void;
    readonly systemPromptSubflow: FlowChart;
    readonly messagesSubflow: FlowChart;
    readonly toolsSubflow: FlowChart;
    /**
     * Optional thinking-normalization sub-subflow (v2.14+). Mounted as a
     * stage AFTER CallLLM, BEFORE Route, only when a `ThinkingHandler`
     * resolved (declared by the provider or explicitly set
     * via `.thinkingHandler()`). When undefined, the stage is NOT added —
     * zero overhead for non-thinking agents (build-time conditional mount).
     */
    readonly thinkingSubflow?: FlowChart;
    readonly updateSkillHistoryStage: (scope: never) => void;
    /**
     * Whether ≥1 Skill is registered. The `UpdateSkillHistory` stage (and
     * therefore the cache's skill-churn rule) is mounted ONLY when true:
     * with no skills the window would record "no skill" every iteration and
     * `detectSkillChurn` could never fire, so the stage would be pure dead
     * weight + a misleading box. Mirrors the `skills.length > 0` gate that
     * auto-attaches `read_skill`, and the `thinkingSubflow` conditional mount.
     */
    readonly hasSkills: boolean;
    /**
     * Whether ≥1 registered skill declares `steps` (9.18.0). Gates the step
     * pointer's mapper threading — engine in/out, the tools-slot arg, and (in
     * the grouped chart) the `sf-llm-call` boundary — so an agent without
     * stepped skills seeds and commits exactly the keys it always did. The
     * threading lives HERE in the builders, not in the subflow (the SG-C
     * blast-radius lesson): the alias discipline is
     * `stepPointer` in (readonly input) → Evaluate writes `nextStepPointer`
     * (the currentSkillId/nextSkillCursor precedent — the pointer changes
     * every iteration, so the turn-constant `turnRoute` pattern would serve
     * the tools slot a stale value) → mappers carry the alias back onto the
     * parent's `stepPointer`.
     */
    readonly hasSteps?: boolean;
    /**
     * The mount kernel's plan (9.58.0) — present ONLY on an agent built with
     * `.maps()`. Gates the `mapEngagement`/`nextMapEngagement` alias round
     * trip through the injection-engine boundary (the stepPointer discipline
     * verbatim). Absent → the keys are never threaded, so every other agent's
     * mapper bytes are exactly what they were.
     */
    readonly engagementPlan?: import('../../maps/engagement/types.js').EngagementPlan;
    /**
     * The unfinished-steps nudge branch (9.18.0). Present ONLY on an agent
     * with ≥1 stepped skill — and when present it is one more branch of the
     * Route decider carrying the same `{ loopTo }` the tool branch does,
     * because a nudge is one more ordinary turn (the SchemaRetry mechanism
     * verbatim). Absent → no branch, no stage, no scope key, no event.
     */
    readonly stepNudgeStage?: (scope: never) => void;
    /**
     * The evidence recheck branch (9.35.0). Present ONLY on an agent built with
     * `.namesAndNumbersFromEvidence({ posture: 'guard' | 'rails' })` — the
     * `'assist'` posture records and never loops, so it mounts no branch. One
     * more branch of the Route decider carrying the same `{ loopTo }` the tool
     * branch does, because a correction is one more ordinary turn (the
     * SchemaRetry mechanism verbatim). Absent → no branch, no stage, no event.
     */
    readonly evidenceRecheckStage?: (scope: never) => void;
    readonly negativeRecheckStage?: (scope: never) => void;
    /**
     * The out-of-budget wrap-up branch (9.56.0). Present on any agent that can
     * CALL a tool — a registered tool, or a `ToolProvider` that might list one —
     * unless `wrapUpAtMaxIterations: false` turned it off. An agent with no tool
     * surface can never run out of budget mid-action (a limit only cuts a turn
     * short when tool calls were pending), so it mounts no branch and its chart
     * is drawn exactly as it always was.
     *
     * One more branch of the Route decider carrying the same `{ loopTo }` the
     * tool branch does, because a wrap-up is one more ordinary turn (the
     * SchemaRetry mechanism verbatim). Absent → no branch, no stage, no scope
     * key, no event.
     */
    readonly wrapUpStage?: (scope: never) => void;
    /**
     * The evidence gate is mounted (9.35.0), at ANY posture. In the GROUPED
     * chart this gates bubbling `systemPromptInjections` out of `sf-llm-call`:
     * the slot writes it INSIDE the subflow and the outer Route decider needs
     * it to exempt values the app's own prompt (base prompt, skill body, a
     * retrieved passage) supplied — without the mapper key the gate would flag
     * the prompt's own identifiers in the default grouped shape (the
     * `hasEscalation` blast-radius lesson, one field down). The flat chart
     * shares one scope and needs no key; the flag is still threaded there so
     * both builders read the same deps object.
     */
    readonly hasEvidenceGate?: boolean;
    /**
     * `.limitsTravelWithTheAnswer()` is configured on a PROSE answer (no output
     * schema — a typed one sets `coverageLimitsAsData` instead). Swaps the
     * final branch's first stage for the variant that folds the run's declared
     * coverage into the answer before the turn payload is captured. Absent →
     * the same stage function the chart has always mounted, byte for byte; the
     * RECORDING half (events + `coverageDeclared`) is unconditional and does
     * not depend on this flag.
     */
    readonly attachCoverageLimits?: boolean;
    /**
     * `.limitsTravelWithTheAnswer()` on an agent with an output schema — the
     * answer is TYPED, so its limits travel as DATA and the block is never
     * appended (JSON followed by prose is not JSON). Swaps the final branch's
     * first stage for the variant that captures the model's answer unchanged
     * and projects the Route decider's committed `answerCoverage` onto
     * `turn_end`. The Agent sets this OR `attachCoverageLimits`, never both;
     * absent → the stage the chart has always mounted.
     */
    readonly coverageLimitsAsData?: boolean;
    /**
     * `.scriptTravelsWithTheAnswer()` — the final stage carries this turn's
     * recorded programs on `turn_end.scripts` and, on a prose answer (`typed`
     * unset), appends them after the `---` separator (`stages/prepareFinal.ts` ·
     * `prepareFinalFor`). Absent → the stage the chart has always mounted.
     */
    readonly scriptTravel?: import('./scripts/types.js').ResolvedScriptTravel & {
        readonly typed?: true;
    };
    /** Opt-in terminal guard and one committed token; absent preserves the original final stage. */
    readonly hasAnswerValidation?: boolean;
    /** Output middleware owns delivery: publish only the final captured answer. */
    readonly releaseOutputTokens?: true;
    /** The evidence gate's `'rails'` posture: a refused answer is recorded flagged and ends the branch (`prepareFinalFor`). */
    readonly evidenceRails?: true;
    /**
     * A tool's `displayName` (the app's words for a call), read through the
     * agent's own tools — present only where a tool can declare one. The prose
     * limits block names an item's declaring calls in those words
     * (`stages/prepareFinal.ts` · `prepareFinalFor`). Absent → the stage the
     * chart has always mounted.
     */
    readonly displayNameOf?: (toolName: string) => string | undefined;
    /**
     * The time layer (`.time()`) is armed: under `attachCoverageLimits`, the
     * final stage renders each `Period:` line in the run's clock zone
     * (`stages/prepareFinal.ts` · `prepareFinalFor`). Absent → the stage the
     * chart has always mounted.
     */
    readonly timeLayer?: true;
    /**
     * The time layer is armed (`.time()`). Gates ONE mount arg on the Tools
     * branch's `inputMapper`: `timeWindows`, the turn's windows with the turn's
     * clock (`time/windows.ts` · `readerWindowsOf`, off
     * `parent.findingsLedger`) — the reader's (step T6b) and the app's control
     * window (`time.window`) — value-conditional, so a turn with none crosses no
     * key. The slot serves the reader's on each tool that declares a period and
     * the control window on every request (`ToolsSlotConfig.timeWindows`, G14).
     */
    readonly timeWindows?: true;
    /**
     * The time layer is armed with the inputs layer (`.time()` over a tool that
     * declares a period, step T8). Gates ONE mount arg on the Tools branch's
     * `inputMapper`: `timeLimits`, the turn's result-check facts for the model
     * (`semantics/coverage/timeLimitFacts.ts` · `timeLimitFactsOf`, audience `model`, off
     * `parent.findingsLedger`) — value-conditional, so a turn whose reads match
     * what was asked crosses no key. The slot renders and serves them in the
     * one time line (`ToolsSlotConfig.timeLimits`); the renderer loads there,
     * never on a plain agent's graph.
     */
    readonly timeLimits?: true;
    /**
     * An escalation brain is declared (9.19.0). In the GROUPED chart this
     * gates threading `skillEscalated` across the `sf-llm-call` boundary —
     * the flip is written by tool-calls on the OUTER scope and read by
     * callLLM INSIDE the subflow, so without the mapper key the escalation
     * would silently never serve a call in the default grouped shape (the
     * SG-C blast-radius lesson: the threading lives in the builders). The
     * flat chart shares one scope and needs no key; the flag is still
     * accepted here so both builders take identical deps.
     */
    readonly hasEscalation?: boolean;
    /** Findings is armed. The tools slot plants a stable optional schema;
     * live call assembly serves the current offer and ledger in late DATA.
     * This flag never makes the cache subflow suppress findings cache markers.
     */
    readonly hasFindingsLedger?: boolean;
    /**
     * The inputs layer is armed (honesty layer 2) — present ONLY when a tool
     * the build can see declares `askOrAssume`, or the agent was built with
     * `.inputsLayer()`. Mounts `sf-inputs` between the LLM call and Route
     * (`honesty/mounts.ts` · `mountInputsLayer`, the ONE helper both builders
     * call) and swaps the final branch's limits stage for the variant that also
     * carries the assumed values, when `.limitsTravelWithTheAnswer()` is on.
     * Absent — the default — and the chart is byte-identical.
     */
    readonly inputsLayer?: InputsMountDeps;
    /**
     * The results layer is armed (honesty layer 3, step 7b) — present ONLY when a
     * tool the build can see declares a `ToolPeriod`, or the agent was built
     * with `.resultsLayer()`. Mounts `sf-results` at the loop head, before the
     * window strategy, and makes it the loop target (`honesty/mounts.ts` ·
     * `mountResultsLayer`, the ONE helper both builders call). Absent — the
     * default — and the chart is byte-identical.
     */
    readonly resultsLayer?: ResultsMountDeps;
    /**
     * The answer layer is armed (honesty layer 4, `.answerLayer()`). Starts the
     * final branch with the layer's stage — the answer's standing, folded from
     * the committed record, filed for PrepareFinal and announced once
     * (`honesty/mounts.ts` · `startFinalBranch`, the ONE helper both builders
     * call) — and swaps PrepareFinal's body for the variant that carries the
     * standing on `turn_end` (and, under `standingLine`, appends its line to a
     * prose answer). Absent — the default — and the branch is byte-identical.
     */
    readonly answerLayer?: AnswerMountDeps;
    /**
     * Tool choice by classifier is armed (`.toolChoice()`, 9.105.0). Gates
     * THREE mount args on the Tools branch's `inputMapper` — `userMessage`
     * (what the classifier reads), `priorToolChoices` (the parent's rows,
     * under an alias because a mount input is frozen inside the subflow and
     * the slot writes the fresh list under the base key) and `wrapUpAsked`
     * (the wrap-up skip) — and ONE line on its `outputMapper`, carrying
     * `toolChoices` back onto the parent key `callLLM` appends the outcome
     * row to. In the grouped chart the same key crosses the `sf-llm-call`
     * boundary both ways. Absent — the default — the mount maps exactly the
     * keys it always did.
     */
    readonly hasToolChoice?: boolean;
    /**
     * The declared ontology is armed (`.ontology()`, 9.106.0). The flat chart
     * needs nothing from it — `callLLM` reads the run constant `ontology` off
     * the root scope; in the grouped chart `callLLM` lives INSIDE `sf-llm-call`
     * and the constant is written by seed on the OUTER scope, so the boundary's
     * `inputMapper` carries it across, under this gate and value-conditionally.
     * Absent — the default — the boundary maps exactly the keys it always did.
     */
    readonly hasOntology?: boolean;
    /**
     * ReAct loop semantics. `'dynamic'` (default) re-runs the InjectionEngine +
     * all 3 slots every iteration (loop → InjectionEngine). `'classic'`
     * engineers context ONCE (InjectionEngine + system-prompt + tools up front)
     * and loops only the Messages slot (loop → Messages). See AgentOptions.reactMode.
     */
    readonly reactMode?: 'classic' | 'dynamic';
    /** Structure recorders threaded into both `flowChart()` calls (the
     *  main chart and the PrepareFinal sub-chart). Each recorder
     *  observes per-node build events (`onStageAdded` /
     *  `onSubflowMounted` / etc.) for the Agent's chart. Undefined when
     *  the consumer didn't attach any. */
    readonly structureRecorders?: readonly StructureRecorder[];
}
/**
 * Build the agent's complete FlowChart from the supplied deps.
 */
/** The Tools mount's `timeWindows` arg — the turn's windows (the reader's, the control's), or no key at all. */
export declare function timeWindowsArg(ledger: unknown): {
    timeWindows?: ReturnType<typeof readerWindowsOf>;
};
/**
 * The Tools mount's `timeClock` arg — the turn's run clock for the served
 * line's first sentence (G16, `agent/arguments/serve.ts` · `clockSentence`),
 * or no key when the turn has no clock. Under an unknown zone (G15) the zone
 * is the UTC spelling, marked `zoneUnknown`.
 */
export declare function timeClockArg(ledger: unknown): {
    timeClock?: {
        now: string;
        zone: string;
        zoneUnknown?: true;
    };
};
/**
 * The Tools mount's `timeLimits` arg — the turn's result-check FACTS for the
 * model (unrendered: the slot's served line renders them, under the arm), or
 * no key.
 */
export declare function timeLimitsArg(ledger: unknown): {
    timeLimits?: TimeLimitFacts;
};
export declare function buildAgentChart(deps: AgentChartDeps): FlowChart;
