/**
 * conventions — subflow + stage ID constants (builder↔recorder protocol).
 *
 * Pattern: Single Source of Truth constants (Ward Cunningham's SSOT).
 * Role:    Contract between `core/` builders and `recorders/core/` observers.
 *          Builders mount subflows with these IDs; recorders pattern-match
 *          on the IDs to emit grouped domain events.
 * Emits:   N/A (constants only).
 *
 * Rename any ID here → both builders and recorders stay in sync.
 */
import type { ContextSlot } from '../events/types.js';
/** Subflow IDs — mounted by builders, observed by recorders. */
export declare const SUBFLOW_IDS: {
    /** Injection Engine subflow. Evaluates every Injection's trigger
     *  and writes activeInjections[] for the slot subflows to consume. */
    readonly INJECTION_ENGINE: "sf-injection-engine";
    /** Inner subflow inside LLMCall that wraps the invocation
     *  (seed + slots + call-llm + optional thinking + extract-final).
     *  Mounted by LLMCall's outer `client` chart. */
    readonly LLM_CALL: "sf-llm-call";
    /** System-prompt slot subflow. Observed by ContextRecorder. */
    readonly SYSTEM_PROMPT: "sf-system-prompt";
    /** Messages slot subflow. */
    readonly MESSAGES: "sf-messages";
    /** Tools slot subflow. */
    readonly TOOLS: "sf-tools";
    /** ReAct router subflow (inside Agent). */
    readonly ROUTE: "sf-route";
    /** Tool-call execution subflow (inside Agent loop). */
    readonly TOOL_CALLS: "sf-tool-calls";
    /** Merge step inside Parallel. */
    readonly MERGE: "sf-merge";
    /** Final-answer composition inside Agent. Mounted via
     *  `addSubFlowChartBranch('final', ...)` so the subflow id is the
     *  Route decider's branch key — `'final'`, no `sf-` prefix. The
     *  decider returns `'final'` as a routing value AND the same string
     *  becomes the subflow's id. */
    readonly FINAL: "final";
    /** Cache subflow (v2.14). Wraps the whole per-turn cache machinery —
     *  decide markers → CacheGate decider → apply/skip — as ONE collapsible
     *  boundary in the chart. Provider-independent decision layer; the
     *  attached provider's CacheStrategy turns markers into wire format.
     *  UpdateSkillHistory stays OUTSIDE (in the main loop) so the rolling
     *  skillHistory window persists across iterations without round-tripping
     *  through this subflow. */
    readonly CACHE: "sf-cache";
    /** Cache decision subflow (v2.6). Walks activeInjections, emits
     *  agnostic CacheMarker[]. Provider-independent. Standalone building
     *  block; the agent now uses the `decideCacheMarkers` stage inside
     *  `sf-cache` instead of mounting this directly. */
    readonly CACHE_DECISION: "sf-cache-decision";
    /** Thinking-normalization mount (v2.14). Wraps the consumer's
     *  ThinkingHandler.normalize() in a real footprintjs subflow so it
     *  has its own runtimeStageId for tracing. The result lands on the
     *  parent LLMCall's `thinkingBlocks` payload, so this subflow is
     *  pure plumbing from the agent step's POV — never a user-facing
     *  step in the StepGraph. */
    readonly THINKING: "sf-thinking";
    /** The inputs layer (honesty layer 2) — mounted between the LLM call and
     *  Route ONLY when armed (a registered tool declares `askOrAssume`, or the
     *  agent was built with `.inputsLayer()`). Runs once per batch: Declare →
     *  Verify → Record → Resolve, filing one `argument` row per ruled argument
     *  per call and handing ToolCalls the values the library fills. Absent on
     *  every other agent — the chart is byte-identical. */
    readonly INPUTS: "sf-inputs";
    /** The results layer (honesty layer 3, step 7b) — mounted at the LOOP HEAD
     *  (before `Compact`, which it precedes as the loop target) ONLY when armed
     *  (a registered tool declares a `ToolPeriod`, or the agent was built with
     *  `.resultsLayer()`). Reads the batch ToolCalls just ran — Declare → Verify
     *  → Record → Resolve — and files one `period` row per call that declared a
     *  period or whose tool declares a `ToolPeriod`. Absent on every other agent
     *  — the chart is byte-identical. */
    readonly RESULTS: "sf-results";
};
export type SubflowId = (typeof SUBFLOW_IDS)[keyof typeof SUBFLOW_IDS];
/**
 * The subflow-id prefix the framework reserves for its OWN composition
 * segments. Every id in {@link SUBFLOW_IDS} carries it except
 * {@link SUBFLOW_IDS.FINAL} (a decider BRANCH KEY that doubles as a mount id,
 * not a name we chose), and the set is deliberately open-ended — builders
 * generate more of them per feature and per release (`sf-router-llm`,
 * `sf-memory-read-<id>`, `sf-memory-write-<id>`, …).
 *
 * Reserved rather than merely conventional because every reader downstream
 * tells LIBRARY PLUMBING from CONSUMER STRUCTURE by this prefix and nothing
 * else:
 *
 *   • commentary skips `sf-*` segments while walking `meta.subflowPath` back
 *     to a user-facing agent name (`commentary/commentaryTemplates.ts`);
 *   • `BoundaryRecorder` hides them from the StepGraph so they never show up
 *     as steps a person has to scrub past;
 *   • the OTel bridge drops a slot-fork selection whose members are all
 *     `sf-*` (`adapters/observability/otel.ts`);
 *   • {@link stageRole} and the pattern/fingerprint readers classify by the
 *     same convention.
 *
 * So a consumer branch named `sf-billing` does NOT fail like a name clash —
 * it is silently read as framework plumbing and vanishes from the very views
 * it was built to appear in. That is two different facts (framework segment /
 * consumer segment) sharing one namespace with no law, which is why the doors
 * that accept a consumer-chosen segment name refuse this prefix outright.
 */
export declare const RESERVED_SUBFLOW_PREFIX = "sf-";
/** True when a subflow-path segment falls inside the reserved namespace. */
export declare function isReservedSubflowSegment(segment: string): boolean;
/**
 * Refuse a consumer-chosen name that would become a reserved subflow-path
 * segment — at DECLARATION time, where the name was typed, rather than at run
 * time where the damage is invisible.
 *
 * Refuse-by-domain: a name that cannot be declared cannot collide. There is no
 * escape hatch and no rename-behind-the-scenes, because a silently rewritten
 * id would break the one thing the consumer wanted (finding their own segment
 * in the trace) in a second, quieter way.
 *
 * @param door  the API that accepted the name, e.g. `Parallel.branch()`
 * @param noun  what the name IS at that door, e.g. `branch id`
 * @param id    the consumer-supplied name
 */
export declare function assertUnreservedSubflowSegment(door: string, noun: string, id: string): void;
/** Stage IDs — plain function stages that builders mount. */
export declare const STAGE_IDS: {
    readonly SEED: "seed";
    /** Relevance entry router (`entryByRelevance`). A once-per-turn function stage
     *  mounted between Initialize and InjectionEngine (off the ReAct loop) that
     *  picks the starting skill by embedding similarity → sets `currentSkillId`. */
    readonly PICK_ENTRY: "pick-entry";
    /** Context-assembly selector stage. Runs AFTER InjectionEngine and
     *  fans the 3 slot subflows (system-prompt / messages / tools) out in
     *  PARALLEL (selector picks all 3 every iteration; failFast so a
     *  required slot's throw aborts the turn). They converge before
     *  CacheDecision. Shared by buildAgentChart + buildDynamicAgentChart;
     *  the flat viz proof chart uses the same id as its root selector. */
    readonly CONTEXT: "context";
    /** Outer "client" stage in LLMCall's wrapped chart. Receives args on
     *  the first visit, $break()s on the second (post-loop) visit with
     *  the LLM answer as TraversalResult. This is the lens-friendly
     *  affordance — the User pill maps to this stage. */
    readonly CLIENT: "client";
    readonly CALL_LLM: "call-llm";
    /** Final-response extraction stage that runs after CallLLM (and
     *  optional sf-thinking). For LLMCall this is mostly symmetric with
     *  Agent's `sf-final` branch — gives lens a "Final" node and a
     *  clear commit boundary marking "we have the answer." */
    readonly EXTRACT_FINAL: "extract-final";
    readonly FINAL: "final";
    /** The Route decider's third branch (7.26), mounted ONLY on an agent built
     *  with `.outputSchema(parser, { retries })`. It writes the corrective turn
     *  and loops back — a re-ask is an ordinary iteration, not a mode. */
    readonly OUTPUT_RETRY: "output-retry";
    /** The Route decider's unfinished-steps branch (9.18.0), mounted ONLY on an
     *  agent with ≥1 stepped skill. When a would-be-final answer leaves declared
     *  steps unrun, ONE teaching nudge goes back and the loop turns once more —
     *  the SchemaRetry mechanism verbatim (an ordinary iteration, not a mode).
     *  At most once per turn; never a forced continue. */
    readonly STEP_NUDGE: "step-nudge";
    /** The Route decider's evidence branch (9.35.0), mounted ONLY on an agent
     *  built with `.namesAndNumbersFromEvidence({ posture: 'guard' | 'rails' })`.
     *  When a would-be-final answer states names or numbers that appear in no
     *  tool result, they are named back to the model and the loop turns once
     *  more — the SchemaRetry mechanism verbatim (an ordinary iteration, not a
     *  mode). At most once per turn. */
    readonly EVIDENCE_RECHECK: "evidence-recheck";
    readonly NEGATIVE_RECHECK: "negative-recheck";
    /** The Route decider's out-of-budget branch (9.56.0), mounted on any agent
     *  that can call tools unless `wrapUpAtMaxIterations: false` turned it off.
     *  When `maxIterations` runs out while the model is still asking for tools,
     *  it appends one instruction and the loop turns ONCE more with the tools
     *  withheld — the SchemaRetry mechanism verbatim (an ordinary iteration, not
     *  a mode). At most once per turn, and that last call cannot loop: with no
     *  tools on the wire there is nothing for the model to ask for. */
    readonly WRAP_UP: "wrap-up";
    /** The final branch's first stage: the turn's answer is captured — and, with `.answerValidation()`, checked — here (9.98.1: a milestone, so a scrub can stop on the answer). */
    readonly PREPARE_FINAL: "prepare-final";
    readonly FORMAT_MERGE: "format-merge";
    readonly MERGE_LLM: "merge-llm";
    readonly EXTRACT_MERGE: "extract-merge";
    /** Window-strategy stage (7.16 as compaction; any strategy since 7.17).
     *  Mounted ONLY when `.window()` / `.compaction()` is configured, and when
     *  it is, it BECOMES the ReAct loop target — so it runs once per iteration
     *  boundary and everything downstream in the turn (injection triggers, all
     *  3 slots, the wire) sees one window. Without a strategy the stage does
     *  not exist and the loop target is unchanged. The id stays `compact` for
     *  every strategy: it is the protocol name every lens and matcher already
     *  binds to, and which strategy ran is on the record. */
    readonly COMPACT: "compact";
    /** Messages-slot delivery stage (7.21). Mounted ONLY when the agent has
     *  something that could target the messages slot (a registered injection
     *  that declares `inject.messages`, or any `.memory()` whose recall might).
     *  Runs right after the InjectionEngine and before the slots, so the
     *  delivered message is part of the window every downstream reader sees.
     *  Absent otherwise — an agent with nothing to deliver runs the chart it
     *  always ran. */
    readonly DELIVER: "deliver";
    /** Updates the rolling skill-history window before CacheGate
     *  evaluates skill-churn (v2.6). */
    readonly UPDATE_SKILL_HISTORY: "update-skill-history";
    /** CacheGate decider stage — routes to apply-markers / no-markers
     *  based on kill switch / hit rate / skill churn (v2.6). */
    readonly CACHE_GATE: "cache-gate";
    /** CacheGate branch (routing key) when markers SHOULD be applied
     *  this iteration. Pass-through stage; markers stay in scope. (v2.6) */
    readonly APPLY_MARKERS: "apply-markers";
    /** CacheGate branch (routing key) when markers should be SKIPPED
     *  this iteration. Stage clears scope.cacheMarkers. (v2.6) */
    readonly SKIP_CACHING: "no-markers";
    /** BuildLLMRequest stage — calls strategy.prepareRequest to apply
     *  markers to the wire request (v2.6). */
    readonly BUILD_LLM_REQUEST: "build-llm-request";
    /** The inputs layer's four stages, inside `sf-inputs` (honesty layer 2):
     *  which calls carry rules and which ruled values are missing … */
    readonly DECLARE_ARGUMENTS: "declare-arguments";
    /** … where each ruled value came from … */
    readonly VERIFY_ARGUMENTS: "verify-arguments";
    /** … one `argument` row per ruled argument, and its event … */
    readonly RECORD_ARGUMENTS: "record-arguments";
    /** … and the fills and refusals ToolCalls applies. */
    readonly RESOLVE_ARGUMENTS: "resolve-arguments";
    /** The results layer's four stages, inside `sf-results` (honesty layer 3):
     *  which calls of the batch just run declared a period, or come from a tool
     *  that declares a `ToolPeriod` … */
    readonly DECLARE_RESULTS: "declare-results";
    /** … the verdict on each one's period … */
    readonly VERIFY_RESULTS: "verify-results";
    /** … one `period` row per call, and its event … */
    readonly RECORD_RESULTS: "record-results";
    /** … and what the layer does about them: flag (the rows are the flags). */
    readonly RESOLVE_RESULTS: "resolve-results";
    /** The answer layer (honesty layer 4) — mounted as the FIRST stage of the
     *  final branch ONLY when armed (`.answerLayer()`): the answer's standing,
     *  folded from the committed record, filed as data for PrepareFinal and
     *  announced once (`agentfootprint.answer.assessed`). Absent on every other
     *  agent — the final branch starts at PrepareFinal, byte-identical. */
    readonly ASSESS_ANSWER: "assess-answer";
};
export type StageId = (typeof STAGE_IDS)[keyof typeof STAGE_IDS];
/** True when a subflow id corresponds to one of the 3 context slots. */
export declare function isSlotSubflow(id: string): id is typeof SUBFLOW_IDS.SYSTEM_PROMPT | typeof SUBFLOW_IDS.MESSAGES | typeof SUBFLOW_IDS.TOOLS;
/** Map a slot subflow id to its ContextSlot type. Undefined for non-slot ids. */
export declare function slotFromSubflowId(id: string): ContextSlot | undefined;
/**
 * Resolve the context slot a scope write belongs to FROM THE WRITE'S OWN
 * `runtimeStageId` — not from a "currently-open slot" stack.
 *
 * Why: once the 3 slot subflows run in PARALLEL (selector fan-out), their
 * entry/write/exit events INTERLEAVE — a stack top is unreliable, so a write
 * inside `sf-messages` could be attributed to (or dropped against)
 * `sf-tools`. The write's `runtimeStageId` (`[subflowPath/]stageId#index`)
 * always encodes which slot subflow enclosed it; we scan the path segments
 * innermost-first for a slot id. Matches the sequential result exactly
 * (the write is still inside its own slot), so it is behavior-preserving.
 */
export declare function slotFromRuntimeStageId(runtimeStageId: string): ContextSlot | undefined;
/** True when an id is any of the library's known subflow IDs. */
export declare function isKnownSubflow(id: string): id is SubflowId;
/** True when an id is any of the library's known stage IDs. */
export declare function isKnownStage(id: string): id is StageId;
/**
 * Semantic role of a stage, used by renderers to decide visual emphasis.
 *
 * The agent's chart mixes a handful of stages users actually care about
 * (the HEROES — what context was built, what the model decided, what it did)
 * with mechanism stages (PLUMBING). This is the ONE place that says which is
 * which; renderers stay generic and style purely off this role (e.g. heroes
 * prominent, plumbing muted). Keeping it here — the semantic owner — avoids
 * the "name-based filter list duplicated across renderers" anti-pattern.
 *
 * - `hero-slot`   — a context slot (system-prompt / messages / tools)
 * - `hero-llm`    — the LLM invocation
 * - `hero-action` — tool execution (the agent's actions)
 * - `plumbing`    — mechanism (injection engine, cache, route, thinking, …)
 * - `boundary`    — neutral chart boundaries (Initialize root, Final) +
 *                   anything unrecognised (rendered normally, never muted)
 */
export type StageRole = 'hero-slot' | 'hero-llm' | 'hero-action' | 'plumbing' | 'boundary';
/**
 * Classify a stage id into its {@link StageRole}. Accepts a path-qualified id
 * (`sf-llm-call/call-llm`) — only the LOCAL segment matters, so it works at
 * any nesting depth. Built entirely from the id constants above, so adding a
 * stage to the chart only requires listing it here.
 */
export declare function stageRole(id: string): StageRole;
/**
 * A {@link Milestone}'s kind — the domain vocabulary for "meaningful step you'd
 * scrub to" in the agent's run.
 */
export type MilestoneKind = 'iteration' | 'slot' | 'llm-turn' | 'tool-call' | 'decision';
/**
 * A time-travel milestone: a domain-declared scrub stop. Conceptually each
 * milestone marks the boundary of a COLLECTION of commits (the commits that
 * belong to that step) — so the Lens slider can step stage-by-stage
 * (iteration → llm-turn → tool-call → …) instead of stopping only on
 * structural subflow boundaries. The renderer iterates whatever the domain
 * classifies; it never hardcodes agent vocabulary.
 */
export interface Milestone {
    readonly kind: MilestoneKind;
    /** Human-readable base label ("LLM turn"); the renderer may add an ordinal. */
    readonly label: string;
}
/** Every {@link MilestoneKind}, for validating a kind read off a recording. */
export declare const MILESTONE_KINDS: readonly MilestoneKind[];
/**
 * Classify a stage id into a {@link Milestone}, or `null` when the stage is NOT
 * a milestone boundary (its commits fold into the surrounding milestone's
 * collection). This is the DOMAIN's declaration of which steps are scrub-worthy;
 * the Lens consumes it to build the time-travel slider (see
 * agentfootprint-lens `cursorPositionsAtDrill`).
 *
 * **Since 9.90.0 this is the FALLBACK, not the fact.** The charts declare the
 * same milestones as tags at build time ({@link milestoneTagsFor}), footprintjs
 * 9.21 stamps them on the stage's first commit bundle (`CommitBundle.tags`), and
 * `milestoneStops` reads the bundle first — it derives from the id only for a
 * bundle that carries no tags (a recording made before 9.90.0). Every milestone
 * stage is declared, slot branch mounts included (footprintjs 9.21.1). Same
 * table either way, so the two readings agree.
 *
 * Mirrors {@link stageRole}: accepts a runtimeStageId (`call-llm#17`), a
 * path-qualified id (`sf-llm-call/call-llm`), or a bare local id — only the
 * LOCAL stage segment matters, so it works at any nesting depth and for both
 * commit ids and subflow-group ids.
 */
export declare function milestoneFor(id: string): Milestone | null;
/** Prefix of the tag that names a milestone's KIND: `milestone:llm-turn`. */
export declare const MILESTONE_TAG_PREFIX = "milestone:";
/** Prefix of the tag that names a milestone's LABEL: `milestone-label:LLM turn`. */
export declare const MILESTONE_LABEL_TAG_PREFIX = "milestone-label:";
/** The kind tag for a {@link MilestoneKind} — `'milestone:' + kind`. */
export declare function milestoneTag(kind: MilestoneKind): string;
/**
 * The tags that DECLARE a {@link Milestone} on a stage: its kind tag and its
 * label tag, in that order. What a declaration site spreads into `.tag(...)`
 * or `{ tags }`; what {@link milestoneFromTags} reads back.
 */
export declare function milestoneTags(milestone: Milestone): readonly string[];
/**
 * The tags to declare on the stage with this LOCAL id, from the same table
 * {@link milestoneFor} reads — so a chart-building site names the id it is
 * mounting and never spells a kind or a label. Throws at build time for an id
 * the table does not classify: a declaration site that is not a milestone is
 * a wiring mistake, and a silent no-op would be a forgotten tag by another name.
 *
 * @example
 * ```ts
 * builder
 *   .addFunction('CallLLM', callLLM, STAGE_IDS.CALL_LLM, 'LLM invocation')
 *   .tag(...milestoneTagsFor(STAGE_IDS.CALL_LLM));   // 'milestone:llm-turn', 'milestone-label:LLM turn'
 * ```
 */
export declare function milestoneTagsFor(localStageId: string): readonly string[];
/**
 * Read a {@link Milestone} back off a commit bundle's declared `tags`, or
 * `null` when they carry no milestone kind tag. The kind must be one of
 * {@link MILESTONE_KINDS}; the label is the `milestone-label:` tag when one was
 * declared, else `labelWhenUndeclared` (a reader passes the stop's own label —
 * the chart's word for the stage), else the kind itself. A stored row is
 * `unknown[]`-shaped until narrowed: non-strings are ignored, never a milestone.
 *
 * @example
 * ```ts
 * milestoneFromTags(['milestone:llm-turn', 'milestone-label:LLM turn']);
 * // { kind: 'llm-turn', label: 'LLM turn' }
 * milestoneFromTags(['audit']);            // null — tagged, but not a milestone
 * milestoneFromTags(undefined);            // null — an untagged bundle
 * ```
 */
export declare function milestoneFromTags(tags: readonly unknown[] | undefined, labelWhenUndeclared?: string): Milestone | null;
/**
 * Scope-key convention for context injections.
 *
 * Each slot subflow writes its injections to a well-known scope key.
 * ContextRecorder observes writes to these keys to emit context.injected
 * events. Builders that mount slot subflows MUST write injections to the
 * corresponding key; this is the data-level contract between builder and
 * recorder.
 */
export declare const INJECTION_KEYS: {
    readonly SYSTEM_PROMPT: "systemPromptInjections";
    readonly MESSAGES: "messagesInjections";
    readonly TOOLS: "toolsInjections";
};
export type InjectionKey = (typeof INJECTION_KEYS)[keyof typeof INJECTION_KEYS];
/** Map a slot to its injection scope key. */
export declare function injectionKeyForSlot(slot: 'system-prompt' | 'messages' | 'tools'): InjectionKey;
/** True when a scope key is any of the known injection keys. */
export declare function isInjectionKey(key: string): key is InjectionKey;
/**
 * The scope key the receipt is committed under. Written by `callLLM` and the
 * message-API charts, read back by `time-travel/servedView.ts` and the replay
 * hooks — a state-key name both sides agree on, so it lives here rather than
 * beside the receipt's builder (`core/agent/receipt/receipt.ts`).
 */
export declare const RECEIPT_KEY = "receipt";
//# sourceMappingURL=conventions.d.ts.map