import type { CommitBundle, UntrackedSource, ValueBasis } from 'foottrace';
import type { EvidenceInput } from '../influence-core/index.js';
import { type ContextBugArtifacts, type HonestyFlag } from './types.js';
/** One source the loop's `call-llm` read, traced back to its live writer for THAT loop. */
export interface ContextSource {
    /** The state key call-llm#k read (e.g. 'systemPromptInjections'). */
    readonly key: string;
    /** runtimeStageId of the live writer (findLastWriter); undefined when never committed before. */
    readonly writerId: string | undefined;
    /** The writer's commitLog ARRAY position — NOT the optional CommitBundle.idx. */
    readonly writerArrayIdx: number | undefined;
    /** Materialized live value (commitValueAt); undefined under the pre-run-initial blind spot. */
    readonly value: unknown;
    /**
     * Why `writerId` / `value` are not exact, when they are not — the writer's and the value's
     * footprintjs basis codes, writer's first (`findLastWriterWithBasis` then
     * `commitValueAtWithBasis`; sentences in `HONESTY_CODES` on `foottrace`). E.g.
     * `['nested-rows']` when the writer reached the key only through paths inside it (a subflow
     * seed or merge-back). ABSENT when both answers are exact.
     */
    readonly basis?: readonly ValueBasis[];
    /** The bridge handed to scorers: { id, text, ancestorTexts }. */
    readonly evidence: EvidenceInput;
}
/** One ReAct iteration — bounded by the loop HEAD, pointing at the call-llm inside it. */
export interface LoopFrame {
    /** Anchor ordinal 0,1,2… DERIVED from the commit log (NOT TraversalContext.loopIteration). */
    readonly loopIndex: number;
    /** The call-llm#k runtimeStageId — the LLM-step pointer WITHIN the frame. */
    readonly llmCallId: string | undefined;
    /** call-llm#k's commitLog array index — the EXCLUSIVE beforeIdx for findLastWriter. */
    readonly llmCallArrayIdx: number | undefined;
    /** The loop-HEAD commit's array index — the body's lower bound. */
    readonly headArrayIdx: number;
    /** Every runtimeStageId in [head[k], head[k+1]) — the full multi-stage body of round k, in commit order. */
    readonly bodyIds: readonly string[];
    /** stepOutputText over the call-llm commit (assistant content + tool-call intents). */
    readonly intermediateText: string | undefined;
    /** One per key call-llm#k read. */
    readonly contextSources: readonly ContextSource[];
    /**
     * WALK-ONLY (proposal 008): the proximate tool result for this loop — the most recent
     * `lastToolResult` committed before this loop's call-llm, with the PRODUCING loop's tool-calls
     * stage as its `writerId` (the cross-loop provenance edge L4's `walkToRoot` descends along). NOT
     * in `contextSources` — L3's narrow never scores it. Absent on loop 0 / grouped frames.
     */
    readonly proximateToolSource?: ProximateToolSource;
    /**
     * GROUPED chart only: the `sf-llm-call` mount runtimeStageId this frame was projected from.
     * When set, ALL array indices on this frame (`headArrayIdx`, `llmCallArrayIdx`,
     * `bodyIds`, each `contextSource.writerArrayIdx`) are relative to that subflow's OWN inner
     * commit log (`subflowResults[subflowScope].treeContext.history`), NOT the run commit log.
     * Absent for FLAT-chart frames (indices are run-commit-log relative).
     */
    readonly subflowScope?: string;
    /** Pass-through of the call-llm bundle's untrackedSources ('args'|'env'|'silent'). */
    readonly incompleteSources?: ReadonlyArray<UntrackedSource>;
    /** True when incompleteSources is non-empty — "this step read untracked; slice may be
     *  incomplete here". NOT a model-internalized claim (that is undetectable — see Trajectory). */
    readonly untrackedReadsPresent: boolean;
}
/** The proximate tool result a loop's decision was conditioned on — L4's cross-loop hop edge (proposal 008). */
export interface ProximateToolSource {
    /** The committed `{ toolName, result }` (redaction-scrubbed, via commitValueAt). */
    readonly value: unknown;
    /** The producing loop's tool-calls stage runtimeStageId — resolves to an EARLIER frame. */
    readonly writerId: string | undefined;
    /** Why the writer / value are not exact, when they are not — as {@link ContextSource.basis}. */
    readonly basis?: readonly ValueBasis[];
    /**
     * The state key this value was materialized from (`'lastToolResult'`). Every
     * OTHER source the walk can hop along carries its key on `ContextSource.key`;
     * this one is not a contextSource, so the key is stamped here rather than
     * hard-coded at the consumer (variable-recall joins on it). WALK-ONLY, like
     * the rest of this record.
     */
    readonly stateKey: string;
    /** Honesty: the call-llm read `history`, NOT this key — an INFERRED proximate, not a direct read. */
    readonly proximate: true;
}
/** The run input, re-injected as a synthetic node — a PROXY (args is untracked), never a recorded edge. */
export interface SyntheticQuestionNode {
    readonly text: string;
    readonly incompleteSources: readonly ['args'];
    readonly injected: true;
}
export interface Trajectory {
    readonly frames: readonly LoopFrame[];
    /** Commits BEFORE the first head (seed, memory-read) — run setup, not a loop body. */
    readonly prelude: readonly string[];
    /** Only populated when the contrastive path is wired (proposal 005 L2 note). */
    readonly question?: SyntheticQuestionNode;
    /** Degrade-never-throw. STANDING caveat on EVERY trajectory: contextSources show only
     *  sources re-committed to tracked state; context the model retained internally (carried
     *  in its own reasoning, never re-committed) leaves no read→write edge and is NOT here. */
    readonly honestyFlags: readonly HonestyFlag[];
    /** Set only when maxFrames cut the run. */
    readonly truncated?: {
        readonly byFrames: boolean;
    };
}
/** One frame's raw partition: the head's array index + the runtimeStageIds in its half-open range. */
export interface AnchorBucket {
    readonly headArrayIdx: number;
    readonly bodyIds: readonly string[];
}
/**
 * Partition a commit log by a list of HEAD runtimeStageIds (taken as data — no agent
 * knowledge). Each frame is the half-open range `[head[k], head[k+1])`; commits before
 * the first head are the `prelude`. TOTAL: every commit lands in exactly one frame OR
 * the prelude. Heads not found in the log are ignored; ordering follows the log, not the
 * input list (an out-of-order or duplicate head list cannot reorder/duplicate commits).
 *
 * A head is anchored at the FIRST commit bearing its runtimeStageId — a single stage
 * execution can flush MORE THAN ONE commit bundle under one runtimeStageId (parallel
 * fork merges, multi-flush stages), and those repeats stay INSIDE the frame they open
 * rather than each spawning a spurious one-commit frame.
 */
export declare function bucketByAnchors(commitLog: readonly CommitBundle[], headRuntimeStageIds: readonly string[]): {
    frames: AnchorBucket[];
    prelude: string[];
};
/**
 * The flat-chart loop heads: the FIRST commit of each injection-engine ENTRY (one per
 * ReAct iteration, since the loop is branch-sourced back to the injection engine). A head
 * is a commit that is in the injection engine while the previous commit was not — so a
 * multi-commit injection-engine body yields exactly one head per loop.
 *
 * Returns the runtimeStageIds to feed `bucketByAnchors`. Empty when the run never enters
 * the injection engine (e.g. the grouped chart, where the loop lives in sf-llm-call —
 * the caller degrades with an honesty flag).
 */
export declare function findLoopHeads(commitLog: readonly CommitBundle[]): string[];
export interface AssembleTrajectoryOptions {
    /** Chars of each source value / intermediate text embedded. Default 2000. */
    readonly maxTextChars?: number;
    /** Keep only the first N frames (honesty-flagged via `truncated`). */
    readonly maxFrames?: number;
}
/**
 * Slice a recorded agent run into a {@link Trajectory} — one {@link LoopFrame} per ReAct
 * iteration, each carrying its `call-llm` pointer, the call's output text, and the live
 * {@link ContextSource}s that fed it (traced via `findLastWriter` + `commitValueAt` from the
 * SAME commit log — zero new capture).
 *
 * Takes the SAME `ContextBugArtifacts` bag the localizer takes — adopter call is just
 * `assembleTrajectory(artifacts)`.
 *
 * Handles BOTH chart shapes:
 *  - FLAT (`reactMode: 'dynamic'`, default): `call-llm` is a parent-level stage; frames are
 *    bucketed over the run commit log by the `sf-injection-engine` loop heads.
 *  - GROUPED (`reactMode: 'dynamic-grouped'`): the LLM turn runs in an `sf-llm-call` subflow;
 *    each loop is projected PER-SCOPE over its own inner commit log (retained per-iteration by
 *    footprintjs subflow-commit-visibility). Such frames carry `subflowScope` and their array
 *    indices are inner-log-relative.
 *
 * Standing caveat on every result: contextSources show only sources re-committed to tracked
 * state; context the model retained internally (carried in its own reasoning, never
 * re-committed) leaves no read→write edge and is NOT represented.
 */
export declare function assembleTrajectory(artifacts: ContextBugArtifacts, opts?: AssembleTrajectoryOptions): Trajectory;
//# sourceMappingURL=trajectory.d.ts.map