/** * @module @nhtio/adk/batteries/orchestration/outline * * Progressive-disclosure reading for plans that are too large for a model's context window. * `planOutline` produces a single flat, self-describing index of a plan; `planRead` fetches a * small, self-locating slice of that plan by the exact identifiers the outline printed. * * @remarks * A model's context window is smaller than a plan will get, so it must be able to work on a plan * it cannot hold. This module is that mechanism, and its shape is load-bearing: * * - **ONE FLAT LEVEL. Never two.** `planOutline` returns a single flat list of phases — no * sub-phases, no index that needs an index. This is not a style preference: a controlled study * of this pattern found one routing level helps and a second "never helps and sometimes breaks * accuracy outright" (0.9126 → 0.6398 on one cell), because in a two-level pack every child * description sits in context before the router commits, recreating the very pressure * progressive disclosure exists to relieve. * - **Entries carry EXACT SURFACE FORMS, not paraphrase.** Per the same study, per-chunk metadata * must be a short summary PLUS a list of key elements, because the element list supplies "exact * surface forms that a one-sentence summary would paraphrase away". For a plan that is decisive: * a model writing `NodeRef{node:'archive_files'}` needs the EXACT node id, and a prose summary * of a phase destroys it. So each `PhaseEntry` carries, verbatim: the phase name, the node ids, * the tool name of each `call` node in that phase, an open-issue count, and a one-line summary. * - **`unphased` is addressed identically.** Nodes with no `phase` are not second-class — they get * their own `PhaseEntry` so a model reaches them the same way. * - **The outline's key IS the reader's key.** `planRead` takes the SAME identifiers the outline * printed — a phase name or a node id. No line numbers anywhere. * - **Each slice is SELF-LOCATING.** A returned slice carries its phase and the immediate * predecessors/successors of the slice as a whole (`boundary`), so a model can keep linking new * nodes without re-fetching the outline. * - **Scoped reading is available, not mandatory.** The study's own conclusion is that progressive * disclosure "buys context, not intelligence" — decisive once an artifact is too large to read, * redundant when an agent can navigate it directly. So a five-node plan should be read whole; * the outline hop is not compulsory. */ import { PlanStore } from "./store"; import type { NodeId, PlanOutline, PlanSlice } from "./types"; /** * Build a flat outline of a plan: one entry per phase, plus a single entry for unphased nodes. * * @remarks * Each `PhaseEntry` carries the exact surface forms a reader needs to fetch a slice — the phase * name, the node ids, the tool names of every `call` node, an open-issue count, and a one-line * summary. The outline is deliberately a single flat list with no second routing level; see the * module doc for why. * * @param store - the plan store to read from. * @param planId - the id of the plan to outline. * @returns the flat outline of the plan. */ export declare function planOutline(store: PlanStore, planId: string): Promise; /** * Read a self-locating slice of a plan by the exact identifier the outline printed. * * @remarks * The selection is either a phase name (`{ phase }`) or a node id (`{ node }`) — the SAME * identifiers the outline printed, never line numbers. The returned slice carries its phase and * the immediate predecessors/successors of the slice as a whole (`boundary`), so a model can keep * linking new nodes without re-fetching the outline. * * Scoped reading is available, not mandatory: a small plan should be read whole, and the outline * hop is not compulsory. * * @param store - the plan store to read from. * @param planId - the id of the plan to read. * @param sel - the selection: `{ phase }` to read a whole phase, or `{ node }` to read the slice * around a single node. * @returns the self-locating slice of the plan. * @throws if the phase or node id is unknown, naming the valid set — never an empty slice. */ export declare function planRead(store: PlanStore, planId: string, sel: { phase: string; } | { node: NodeId; }): Promise;