/** * keyedFold — the value of ONE state key at one commit, folded from the run's * own fold base. * * Role: Fold. It reads a finished run's commit log plus the base that log * was recorded against, and answers "what was `key` at commit `idx`?" * It executes nothing, records nothing and stores nothing beside the * log. Its answers are DETACHED — deep-frozen before they are cached, * because a memo that hands out live references is a fold that lets a * reader rewrite the run (`keyedFold.ts` · `freezeDeep`). * Reads: a `FoldSource` — a snapshot (`commitLog` + `initialState`) or a * subflow subtree (`history` + `initialState`), through footprintjs's * own `stateAt` (for the base) and `applySmartMerge` (for the replay). * Emits: N/A. * * ── WHY THIS FILE EXISTS ─────────────────────────────────────────────────── * A commit log stores DIFFS. `commitValueAt` folds them for one key and says * so in its own docstring: a value that came from the run's INITIAL state and * was never `set` again folds to ABSENT, because the log alone cannot see the * pre-run base. * * That is not an edge case here. A RESUMED agent run is a fresh executor * seeded from `checkpoint.sharedState`, so the entire pre-pause world — * `systemPromptInjections`, `history`, `dynamicToolSchemas`, every build-time * constant `seed` wrote before the pause — is base, not log. Folding the * resumed run with `commitValueAt` returns an empty system prompt and a * truncated conversation and declares no gap: a confident falsehood about the * one question this family exists to answer. footprintjs 9.17 shipped * `RuntimeSnapshot.initialState` and `stateAt` for exactly this, and every * read in `epochs.ts` goes through here so it lands on that base. * * ── WHY NOT JUST CALL `stateAt` PER READ ─────────────────────────────────── * `stateAt` folds the WHOLE state, and it clones that state once per bundle * (`applySmartMerge` opens with a `structuredClone` of its base). One fold is * therefore O(commits × state size) — measured on a real 100-turn agent * (1,719 commits, 101 epochs): 323 ms for a single fold, and 11.7 s to fold * once per epoch, against 78 ms for the whole per-epoch scrub before this * release. A per-epoch reader cannot pay that. * * So `stateAt` is used for the ONE thing only it can give — the fold base and * the honest `basis` verdict, at `commitIdx: -1`, where it folds nothing and * clones the base once — and the forward replay is done per key, through * footprintjs's own `applySmartMerge`. There is deliberately no second * implementation of the trace verbs here: every `set` / `append` / `merge` / * `delete` is applied by the same function the live commit uses. * `keyed-fold-equivalence.test.ts` pins the result against `stateAt` itself on * real runs, including a resumed one and a merge with no `set` anchor — the * one shape that cannot be folded at all without the base. */ import type { CommitBundle } from 'footprintjs/advanced'; /** * How a fold was derived — footprintjs's own two answers, restated here so a * caller of this module does not have to import the engine's trace barrel to * name them. * * - `'initial+log'` — the recording carried its fold base, so a value seeded * before the run (or before a resume) folds correctly. * - `'log-only'` — it did not. Anything the log never `set` reads as absent, * and a reader must say so rather than present the hole as a proof. */ export type FoldBasis = 'initial+log' | 'log-only'; /** * Anything that carries a commit log and, ideally, the base it was recorded * against — the two field spellings footprintjs already has: `commitLog` on a * run snapshot, `history` on a subflow subtree. */ export interface FoldSourceLike { readonly commitLog?: readonly CommitBundle[]; readonly history?: readonly unknown[]; readonly initialState?: Record; } /** * One key's value at one commit — the question, and the answers already * computed for it. */ export interface KeyedFold { /** How this fold was derived — see {@link FoldBasis}. */ readonly basis: FoldBasis; /** How many bundles the log holds. */ readonly length: number; /** * The value of `key` folded through commit ARRAY INDEX `idx`, inclusive. * `-1` (or lower) folds nothing and returns the base's value — the state * before the log's first commit. * * DEEP-FROZEN, and the same object on every call for the same question. It * is detached from the engine's own bundles AND unmutable, so a caller * cannot rewrite what a later index folds to — `keyedFold.ts` · `freezeDeep` * says why that is a law here rather than a courtesy. Copy it * (`structuredClone`, a spread) if you need something to edit. */ valueAt(key: string, idx: number): unknown; } /** * The keyed fold for one source, memoized on the source object. * * Memoized because a per-epoch reader asks the same source for the same keys * at many different commits, and the index is the expensive half: building it * is one pass over the log, and answering a read afterwards is a binary search * plus a replay from the nearest full-value write (normally exactly one). * * @example * ```ts * import { keyedFold } from 'agentfootprint'; * * const fold = keyedFold(agent.getSnapshot()); * fold.basis; // 'initial+log' — the base travelled * fold.valueAt('history', 12); // the conversation after the 13th commit * fold.valueAt('history', -1); // …and what the run STARTED from * ``` */ export declare function keyedFold(source: FoldSourceLike | undefined): KeyedFold; //# sourceMappingURL=keyedFold.d.ts.map