/**
 * buildMessageApiChart — PROOF of the locked "messageAPI merge-tree" shape
 * (MENTAL_MODEL.md ★ LOCKED DESIGN), LLM-only (no tools subflow yet).
 *
 * This is Step 1 of the agreed build order: prove the Context-selector →
 * slot subflows → messageAPI stage → Call-LLM tree works + renders, BEFORE
 * bringing it to the Agent (Step 2 adds the tools subflow + the loop).
 *
 * Chart shape (LLM-only):
 *
 *     Seed
 *       → Context (SELECTOR stage — picks which slots to engineer)
 *           ├─ sf-system-prompt ┐   (selected branches run in parallel)
 *           └─ sf-messages ──────┴─→ messageAPI stage   (the join point)
 *       → Call-LLM
 *
 * WHY a selector (not a plain fork): "Context = Selector stage" — it RETURNS
 * the list of slot branch ids to engineer this iteration, and `select()`
 * captures evidence (which slots + why). That is what will unify Static and
 * Dynamic agent in ONE chart later: Static picks only `messages` per loop;
 * Dynamic also picks `system-prompt` (and `tools`) when they re-engineer.
 * The picked-set IS the lit/unlit-pill signal. For this LLM-only proof the
 * selector picks BOTH slots (a one-shot call engineers everything once).
 *
 * WHY messageAPI is a REAL stage: it assembles the LLM request bulk that the
 * agent's `callLLM` builds invisibly today (`buildCallLLMStage`,
 * callLLM.ts · buildCallLLMStage) — `systemPrompt`
 * (separate field) + `messages` (the conversation, incl. tool-results) → the
 * message-API payload. Making it a stage makes that assembly visible +
 * inspectable in Lens/Trace. (Tools is a separate field added at Call-LLM —
 * it joins in Step 2.)
 *
 * Slots are REAL subflows (reused verbatim: buildSystemPromptSlot /
 * buildMessagesSlot) writing the convention INJECTION_KEYS, so ContextRecorder
 * emits context.injected and Lens renders them — no bespoke collapser.
 */
import type { FlowChart } from 'footprintjs';
import type { LLMProvider } from '../../provider-contract/types.js';
export interface MessageApiChartDeps {
    readonly provider: LLMProvider;
    readonly model: string;
    readonly systemPrompt: string;
    readonly structureRecorders?: readonly import('footprintjs').StructureRecorder[];
    /**
     * The id of the run this chart is about to make (9.91.0) — supply it and
     * Call-LLM mints a receipt, the fingerprint of what the model was handed,
     * committed at the call for `receiptAt` and `servedAt` to read.
     *
     * IT IS A DEP BECAUSE THIS IS A CHART BUILDER, NOT A RUNNER. `Agent` and
     * `LLMCall` own their executor and mint a run id per run; this chart is
     * handed to a `FlowChartExecutor` the caller owns, and nothing in a stage's
     * scope carries that executor's run id. So the one value the receipt cannot
     * do without has to come from whoever starts the run.
     *
     * OMIT IT AND NO RECEIPT IS MINTED — deliberately, rather than minting an
     * unsalted one. Every hash on a receipt is salted with the run id precisely
     * so a short system prompt or a two-word turn cannot be fingerprinted across
     * runs (`receipt.ts`, the third law), and a receipt is committed state that
     * travels in recordings. A chart that minted with an empty salt would ship
     * dictionary-attackable fingerprints by default. `servedAt` then declares
     * the absence — `no-receipt-on-chart`, cause `'no-receipt-committed'` — and
     * rebuilds the view as it always did.
     *
     * @example (internal — `buildMessageApiChart` is not on the package's public
     * surface; the type is, so a caller composing this chart inside the library
     * reads the contract here)
     * ```ts
     * const runId = `run-${Date.now()}`;
     * const chart = buildMessageApiChart({ provider, model, systemPrompt, getRunId: () => runId });
     * const executor = new FlowChartExecutor(chart);
     * await executor.run({ input: { message: 'hi' } });
     * receiptAt(executor.getSnapshot(), 1)?.basis.runId; // runId
     * ```
     */
    readonly getRunId?: () => string | undefined;
}
/**
 * Build the LLM-only messageAPI merge-tree chart.
 *
 * **Cancellation.** Every model call reads its signal from the run's `env`.
 * For an abort or a deadline to cancel the provider's request, pass the signal
 * there too: `env: { signal }`, and for a deadline `AbortSignal.timeout(ms)`. A
 * bare `FlowChartExecutor` hands its own `signal` and `timeoutMs` to the
 * traversal only. That stops the chart, but a call already in flight runs to
 * completion, and you are billed for it.
 *
 * ```ts
 * const controller = new AbortController();
 * await new FlowChartExecutor(chart).run({
 *   input: { message: 'hi' },
 *   signal: controller.signal, // stops the traversal
 *   env: { signal: controller.signal }, // cancels the model call in flight
 * });
 * ```
 */
export declare function buildMessageApiChart(deps: MessageApiChartDeps): FlowChart;
