import type { Context, Message, Model, StreamFn, Tool, Usage } from "../llm/index.js";
import { type AgentCoreCompletionRuntimeDeps } from "../loop/runtime-deps.js";
import type { AgentMessage, ThinkingLevel } from "../loop/types.js";
import { CompactionError, type Result, type SessionTreeEntry } from "../harness/types.js";
import { type FileOperations, type InvokedSkillRetention } from "./utils.js";
/** File-operation details stored on generated compaction entries. */
export interface CompactionDetails {
    /** Files read in the compacted history. */
    readFiles: string[];
    /** Files modified in the compacted history (sorted, for stable display). */
    modifiedFiles: string[];
    /**
     * The same modified set ordered most-recently-touched FIRST. Top-N consumers (working-file
     * attachments) pick from here so an alphabetical sort cannot push the file the model actually
     * needs out of the window (the LONGRUN-2b miss). Optional: absent on details persisted before
     * this field existed.
     */
    modifiedFilesByRecency?: string[];
    /**
     * Invoked-skill retention area (CC 2.1.207 parity): the skills invoked in the COMPACTED range
     * (merged over the previous compaction's retained set), most recent invocation first, already
     * budgeted (per-skill truncation marker + total-area skip semantics — see compaction/utils.ts).
     * Rendered onto the compaction summary message by `buildSessionContext`; kept OUT of the summary
     * string so the summarizer LLM never re-chews skill bodies. Optional: absent on details persisted
     * before this field existed, and omitted when no skill was ever invoked.
     */
    invokedSkills?: InvokedSkillRetention[];
    /**
     * RB-398-b①: CUMULATIVE count of transcript messages this compaction's summary stands in
     * for (this range's folded messages + the previous compaction's carried count). WRITE-side
     * accounting on purpose: deriving the count at render time from the branch walk broke the F3
     * "bounded wake equals full wake" invariant (a floored storage cannot see below the floor, so the
     * two sides disagreed — caught by test/session-floor.test.ts). Drives the summary wrapper's scale
     * disclosure (`buildSessionContext` → `CompactionSummaryMessage.elidedMessages`). Optional-absent
     * on pre-field entries (⇒ no disclosure, byte-identical legacy wrapper).
     */
    elidedMessages?: number;
    /**
     * PTL-shortfall accounting: CUMULATIVE count of folded messages the summary does NOT cover — the ones a reactive
     * prompt-too-long retry shed from the summarization input (`summarizeWithPtlRetry`'s group
     * rollback) after `prepareCompaction` had already committed the cut. Those messages leave the
     * transcript with everyone else (so {@link elidedMessages} stays the honest FOLD count) but
     * nothing in the summary stands in for them, and the wrapper used to claim otherwise.
     *
     * Deliberately a SECOND number rather than a correction of the first: lowering `elidedMessages`
     * to the covered count would under-report the fold scale, which is the exact judgment that field
     * exists to inform ("was a whole investigation folded, or three turns?"). Same cumulative +
     * write-side + optional-absent discipline as its sibling; a lossless pass omits it entirely, so
     * details written by the common path stay byte-identical. Counted in the summarization-input
     * coordinate — a LOWER BOUND on uncovered transcript messages (see
     * {@link SummarizationInputTruncation.droppedMessages}).
     */
    unsummarizedMessages?: number;
    /**
     * RB-398-c: `<persisted-output ref="…">` handles found in the COMPACTED range
     * (merged over the previous compaction's retained set, oldest-first, capped at
     * {@link PERSISTED_OUTPUT_REFS_MAX_ENTRIES} newest). The offloaded blobs outlive the preview that
     * carried their only address — without this channel the handle survived only if the summarizer
     * model happened to copy the string. Rendered as a deterministic summary footer by `compact()`
     * (`formatPersistedOutputRefs`), the same channel `readFiles`/`modifiedFiles` already have.
     * Optional-absent when the range held no offloaded results.
     */
    persistedOutputRefs?: string[];
    /**
     * RB-402: the activated deferred-tool names LIVE at the
     * moment this compaction landed — the design/36 activation state whose transcript carriers
     * (the ToolSearch toolCall parts, and since RB-431 the successful direct calls, that
     * `extractDiscoveredToolNames` replays at every task boundary)
     * this very compaction just replaced with summary prose. Same field shape as the checkpoint
     * seed's `CheckpointState.activeTools` (design/45 §4.bis — the sibling seam that already
     * survives a suspend/resume). Written by the runtask compaction lanes
     * (`MaybeCompactOptions.activeTools` → `maybeCompact`'s appendCompaction, NOT by
     * `prepareCompaction` — the engine layer has no view of the runner's activation Set); consumed
     * by prepare-task's boundary re-derivation via `readCompactionActiveTools` and intersected with
     * the NEXT leg's own deferred registry (a name no longer deferred there is inert).
     * Optional-absent on pre-RB-402 entries and on legs with no activations.
     */
    activeTools?: string[];
}
/** Generated compaction data ready to be persisted as a compaction entry. */
export interface CompactionResult<T = unknown> {
    /** Summary text that replaces compacted history in future context. */
    summary: string;
    /** Entry id where retained history starts. */
    firstKeptEntryId: string;
    /** Estimated context tokens before compaction. */
    tokensBefore: number;
    /** Optional implementation-specific details stored with the compaction entry. */
    details?: T;
}
/** Compaction thresholds and retention settings. */
export interface CompactionSettings {
    /** Enable automatic compaction decisions. */
    enabled: boolean;
    /** Tokens reserved below the context window for the TRIGGER threshold (threshold = window −
     *  reserve). Historically this single knob ALSO seeded the summary legs' output budget
     *  (0.8×/0.5×); that second role is now owned by {@link summaryOutputBudgetTokens} (with this
     *  field as its default base), so trigger-geometry repairs (`sanitizeCompactionSettings`) can
     *  never silently move the summary budget. */
    reserveTokens: number;
    /**
     * Explicit base for the summary OUTPUT budget (the 0.8× history-leg / 0.5× turn-prefix-leg
     * max_tokens derivation), decoupled from `reserveTokens`' trigger role. Absent ⇒ `reserveTokens`
     * is the base (historical behavior). Either way the effective base is capped by the SUMMARY
     * model's own max output (see {@link summaryOutputBudgetTokens}) — budgeting output the model
     * cannot emit only starves the pre-send input clamp. `sanitizeCompactionSettings` passes this
     * field through untouched: it is not trigger geometry.
     */
    summaryOutputBudgetTokens?: number;
    /** Approximate recent-context tokens to keep after compaction. */
    keepRecentTokens: number;
    /** design/145 §1 — window-safety clamp tolerance for an independent (smaller-window) compaction
     *  model: when the summarization input would be clamp-truncated by MORE than this ratio (the
     *  {@link dryRunSummarizationClamp} judgment), the summary call becomes a candidate to fall back
     *  to the MAIN model, which is always window-safe (the content grew inside its window). 0 = any
     *  truncation is a fallback candidate; 1 = never fall back (the pre-145 clamp-only behavior, the
     *  escape hatch). Default {@link DEFAULT_CLAMP_TOLERANCE}. */
    clampTolerance?: number;
}
/** design/145 §1 — default `clampTolerance`: head+tail clamping keeps ≤10% mid-section loss BOUNDED
 *  AND DISCLOSED (`compaction.clamp_disclosure`), not "lossless" — the honest wording is in the
 *  design doc. Above this, a cheap-model summary is judged too lossy and the main model takes over. */
export declare const DEFAULT_CLAMP_TOLERANCE = 0.1;
/**
 * Default compaction settings used by the harness.
 *
 * `keepRecentTokens: 0` is a ZERO BUDGET, not "keep nothing" — {@link findCutPoint} has a
 * structural floor underneath the budget (see its doc), so the cut still lands on the last valid
 * cut point and the summary is followed by the last exchange VERBATIM. Measured on the real
 * Runner: the first request after an auto-compaction is `[summary wrapper][last assistant turn]
 * [new user]`, and a within-turn boundary keeps `[emitting assistant][its tool results]` because
 * a trailing tool-result batch pulls the floor back to its call site.
 *
 * That is the CC posture, arrived at through a different knob. CC 2.1.223's LOCAL threshold
 * compaction always routes to the reactive pipeline (`y9s` :432704 — the `GNo` full-compaction
 * branch with `messagesToKeep: []` :431788 is reached only for a remote/in-process runner, and a
 * `source:"auto"` window makes the local threshold not fire at all, `hky` :432687 / `aYe` :242816).
 * Reactive splits the session into GROUPS (`Ubr` :399459 — a new assistant `message.id` starts a
 * group and its tool results ride with it) and preserves the last `s = 1` of them verbatim
 * (`YMo` :399971 / :399982), zeroing the preserved assistants' usage on the way out (`ryt` :401285
 * — our `buildSessionContext` kept-tail `stripAssistantUsage`, design/123 D1, is the same act).
 * `s` only grows on ITS retry path, so one group is the default posture on both sides.
 *
 * design/169-C moved this default from 20000 to 0 and anchored the change on `messagesToKeep: []`;
 * the anchor names the minority path, but the VALUE lands on the majority path's behavior, and
 * raising it again would keep MORE than CC does (measured: a 2000-token budget keeps two whole
 * exchanges). A positive `keepRecentTokens` remains the explicit knob for callers that want a
 * wider verbatim tail than either engine's default.
 *
 * Known residual (recorded, not repaired): when the last entry is a USER message the floor takes
 * it alone, where CC's group would also carry the assistant before it. The runner's lanes evaluate
 * compaction at turn boundaries and at finish — after the assistant/tool results, before the next
 * prompt — so that shape belongs to direct `maybeCompact` callers.
 */
export declare const DEFAULT_COMPACTION_SETTINGS: CompactionSettings;
/**
 * design/123 D2 — default structural coefficient (chars per token) for every structural token
 * estimate. Threaded as an explicit parameter through the estimate chain so caller-side coordinates
 * never split (estimate vs cut-point vs defense); per-model override via `Model.charsPerToken`.
 *
 * 4 → 3 (design/374 slice 4, ruled 2026-08-28) — this is a RE-ANCHORING, not a divergence.
 * CC's coefficient is a two-branch function, not a constant: `sf(model)` (cli250.js, `Z6` table) is
 *   `Z6.has(family) ? 4 : 3`
 * where `Z6` lists exactly the OLD families (claude-3.x, opus-4-0/4-1/4-5/4-6, sonnet-4-0/4-5/4-6,
 * haiku-4-5). **3 is CC's DEFAULT branch; 4 is the whitelist hit** — and CC's own current generation
 * (opus-5 / sonnet-5) is not in the table, so upstream estimates its own newest models at 3. This
 * engine is BYOM: its models are always outside that table, so 4 was the branch CC would never have
 * taken for them. We had it backwards, and said so in the parity register as an open item
 * (`compaction-chars-per-token-default-4-vs-cc-out-of-table-3`) until this ruling.
 *
 * Direction of the change: a token estimate is chars/coefficient, so a SMALLER coefficient estimates
 * MORE tokens — every structural trigger (compaction threshold, the clearStale frontier, the guard,
 * the cut point, the prompt-overhead term) fires EARLIER. That is the conservative direction and the
 * measured one: design/123's own live calibration puts real code near 2.18 chars/token and CJK at
 * 1–1.6, i.e. the truth is on 3's side of the choice, and the failure mode 4 produced was the
 * defense self-certifying an over-window request as fitting.
 *
 * NOT affected, deliberately: the microCompact ≥20k minimum-savings estimate
 * (`context-edit.ts` `structuralClearSavings`) hard-codes 4. That is CC-exact and stays — CC's own
 * `tQt` calls its estimator with NO coefficient argument, taking that function's `t = 4` default,
 * while the CONTEXT estimator is called WITH the per-model coefficient. The asymmetry is upstream's,
 * and flipping this constant must not move that gate (pinned).
 */
export declare const DEFAULT_CHARS_PER_TOKEN = 3;
/** Calculate total context tokens from provider usage. */
export declare function calculateContextTokens(usage: Usage): number;
/**
 * Return usage from the last successful assistant message in session entries.
 *
 * design/123 D1 stale-anchor note (fable-m2, same-family point): this scans RAW session entries, so
 * unlike `buildSessionContext` (which shallow-strips the usage of kept-tail assistants replayed
 * across a compaction boundary — the anchor "naturally disappears", CC 198 parity) it CAN still see
 * a pre-compaction anchor. Any consumer using this across a compaction boundary must treat such an
 * anchor as STALE: it reflects the pre-compaction request and overestimates the post-compaction
 * context. (Currently exported with no in-repo production consumer.)
 */
export declare function getLastAssistantUsage(entries: SessionTreeEntry[]): Usage | undefined;
/** Estimated context-token usage for a message list. */
export interface ContextUsageEstimate {
    /** Estimated total context tokens. */
    tokens: number;
    /** Tokens reported by the most recent assistant usage block. */
    usageTokens: number;
    /** Estimated tokens after the most recent assistant usage block. */
    trailingTokens: number;
    /** Index of the message that provided usage, or null when none exists. */
    lastUsageIndex: number | null;
}
/** Estimate context tokens for messages using provider usage when available.
 *  `charsPerToken` (design/123 D2): structural coefficient for the anchor-less fallback and the
 *  trailing (post-anchor) increment — pass `Model.charsPerToken` so the estimate matches the model. */
export declare function estimateContextTokens(messages: AgentMessage[], charsPerToken?: number): ContextUsageEstimate;
/** Return whether context usage exceeds the configured compaction threshold. */
export declare function shouldCompact(contextTokens: number, contextWindow: number, settings: CompactionSettings): boolean;
/** Estimate token count for one message using a conservative character heuristic.
 *  `charsPerToken` (design/123 D2): per-model structural coefficient (default 3 since design/374
 *  slice 4; the historical value was 4 = byte-compatible
 *  legacy heuristic). Images contribute a FIXED token weight independent of the coefficient. */
export declare function estimateTokens(message: AgentMessage, charsPerToken?: number): number;
/** Find the user-visible message that starts the turn containing an entry. */
export declare function findTurnStartIndex(entries: SessionTreeEntry[], entryIndex: number, startIndex: number): number;
/** Cut point selected for compaction. */
export interface CutPointResult {
    /** Index of the first entry retained after compaction. */
    firstKeptEntryIndex: number;
    /** Index of the turn-start entry when the cut splits a turn, otherwise -1. */
    turnStartIndex: number;
    /** Whether the selected cut point splits an in-progress turn. */
    isSplitTurn: boolean;
}
/** Find the compaction cut point that keeps approximately the requested recent-token budget.
 *  `charsPerToken` (design/123 D2): the keep-recent accumulation must live in the SAME structural
 *  coordinate as the trigger estimate, or the kept tail is systematically over/under-sized.
 *
 *  STRUCTURAL FLOOR — the budget can raise the kept tail, never empty it. The backward scan breaks
 *  at the FIRST message it visits when the budget is already satisfied (a budget of 0 is satisfied
 *  immediately), and then lands on a valid cut point, so the kept tail is never smaller than
 *  "the last cut point onward". Two shapes follow, and both are load-bearing:
 *    - the tail ends on an assistant/user message ⇒ that message is itself a cut point and is kept;
 *    - the tail ends inside a tool-result batch ⇒ tool results are not cut points, so the `!found`
 *      fallback below pulls the floor back to the LARGEST cut point ≤ i, i.e. the emitting
 *      assistant, and the whole batch is kept with it.
 *  Reading `keepRecentTokens: 0` as "no verbatim tail" is therefore wrong at both the name and the
 *  behavior (see {@link DEFAULT_COMPACTION_SETTINGS} for the measured shape and the CC coordinates
 *  it matches). The pins live in test/compaction-keep-tail-floor.test.ts. */
export declare function findCutPoint(entries: SessionTreeEntry[], startIndex: number, endIndex: number, keepRecentTokens: number, charsPerToken?: number): CutPointResult;
export declare const SUMMARIZATION_SYSTEM_PROMPT = "You are a context summarization assistant. Your task is to read a conversation between a user and an AI coding assistant, then produce a structured summary following the exact format specified.\n\nDo NOT continue the conversation. Do NOT respond to any questions in the conversation. ONLY output the structured summary.";
/**
 * Render the host-supplied instruction files as the FIRST section of an independent-form
 * summarization request, or `""` when the seat is absent (see
 * `CompactionPreparation.contextInstructionFiles`).
 *
 * TRUST TIER — read before wiring a host: this content is DEPLOYMENT supply, the same tier as the
 * system prompt and `EnvironmentFacts`, and it is handed to the model with instruction standing for
 * the summarization task. It is NOT a model-writable position and must never be filled from tool
 * output, model text, or any other in-band channel; the prompt's own provenance sentence keeps
 * those classified as data, and this seat must not become the way around it. Hosts pass the same
 * instruction-file content their prompt assembly mounts (already fence-sanitized upstream by
 * `composeMemoryBlock` on the runner path).
 *
 * SINGLE SOURCE for the request bytes AND the budget charge: `generateSummary`,
 * `generateTurnPrefixSummary` and `dryRunSummarizationClamp` all render through this function and
 * charge `.length` of what it returns, so the window-safety judgment cannot under-count the section
 * the real call sends (the failure mode the dry run was built to make impossible).
 */
export declare function contextInstructionFilesSection(contextInstructionFiles?: string): string;
/**
 * design/169-A (CC 220 :516490-516516 / :387325-387336) — the CC-form FORK context for a summary
 * request: the main lane's latest REAL request. When present (and the summary model IS the main
 * model), the summary request is the main conversation itself — same `systemPrompt` bytes, same
 * message-list byte PREFIX, same tools — plus ONE appended user-role instruction message. The
 * provider's prompt cache then re-serves the already-paid main prefix, which is the economics that
 * make "summarize on the main model" cheap (an independent request re-prefills the whole
 * conversation at full price every boundary).
 *
 * Mirrored structurally (not imported — core cannot be imported from the engine layer) by
 * `CompactionForkContext` in `src/core/auto-compaction.ts`; the two must stay assignable.
 */
export interface CompactionForkContext {
    /** The main request's system prompt, byte-identical (absent when the main request had none). */
    systemPrompt?: string;
    /** The main request's PHYSICAL system-block layout (Context.systemBlocks), when present — the
     *  cache-span boundaries are part of the prefix identity a fork must reproduce. */
    systemBlocks?: Context["systemBlocks"];
    /** The main request's message list — reused as the fork's byte-identical prefix. */
    messages: Message[];
    /** The main request's tool declarations (part of the provider's cached prefix; optional — the
     *  instruction forbids tool calls either way). */
    tools?: Tool[];
    /** The model id that SERVED the recorded request. The model is part of the provider's cache key:
     *  a fork whose summary call runs on a different model (mid-run budget degradation swapped models
     *  after the snapshot) pays a full prefill — the eligibility gate compares this against the
     *  summary model and degrades to the independent form on mismatch. Optional so a direct caller
     *  that manages its own model constancy can omit it (absent = caller vouches). */
    modelId?: string;
}
/** Extract the fork-form summary from its envelope; `undefined` = non-conforming response.
 *
 *  Grammar (falsification-style round 3, both arms pinned): after stripping the permitted `<analysis>`
 *  scratch blocks, the ENTIRE remaining response must be one envelope — text outside it is a
 *  rejection, not noise ("<summary>placeholder</summary> I refuse" must not pass as "placeholder").
 *  The inner match is GREEDY with the closing tag anchored at the END, because the summary quotes
 *  user messages VERBATIM — a quoted literal `</summary>` inside the body must not truncate an
 *  otherwise conforming summary into an accepted fragment (the first-match form silently dropped
 *  every section after the quote). */
export declare function extractForkSummaryEnvelope(text: string): string | undefined;
/** design/169-A — the single user-role instruction message appended to the forked main prefix.
 *  Semantics = SUMMARIZATION_SYSTEM_PROMPT's text-only contract + SUMMARIZATION_PROMPT's nine-section
 *  body ("The messages above…" reads exactly right in the fork form: the conversation IS the
 *  messages above, un-serialized) + the fork-only instruction-file authority clause (see
 *  {@link FORK_CONTEXT_INSTRUCTION_AUTHORITY}) + the fork-only summary envelope (see
 *  {@link FORK_SUMMARY_ENVELOPE_DEMAND}; the independent form keeps its envelope-free contract).
 *  Instructions inject in the same CC-verbatim shape as the independent form so every channel keeps
 *  one injection grammar. */
export declare function forkSummarizationInstruction(customInstructions?: string, contextInstructionFiles?: string): string;
/**
 * Trigger-reserve / summary-budget decoupling (blocker fix, 触发点对标批复审追补): the base for the
 * summary legs' OUTPUT budget (history leg = 0.8× this, turn-prefix leg = 0.5× this).
 *
 * Two semantics used to ride one knob: `reserveTokens` is the TRIGGER deduction (threshold =
 * window − reserve, repaired by `sanitizeCompactionSettings` against the trigger frontier), and the
 * same — possibly repaired — value seeded the summary output budget. That coupling made the summary
 * budget a hostage of trigger geometry: when the frontier moved (0.7W → W−33000), a 200k-window
 * default's budget silently shrank 48000 → 26400 with zero summary-semantics justification.
 *
 * Decoupled shape:
 *   base      = settings.summaryOutputBudgetTokens ?? settings.reserveTokens  (explicit knob wins)
 *   effective = min(base, summary model's max output)                          (never budget output
 *                                                                               the model can't emit)
 * CC contrast (2.1.207): CC's summary output cap is purely model-derived (its summary call runs at
 * the model's max output) and fully separate from the trigger deduction — the `min(model.maxTokens,…)`
 * leg IS that posture. The base deliberately stays reserve-derived rather than jumping to the raw
 * model max: sema (unlike CC) clamps the summarization INPUT pre-send, and the clamp budgets
 * `window − max(0.8×base, reasoning floor)` — a model-max base (e.g. 64k) would eat half a small
 * summarizer's window in output reservation and starve the input side. Callers wanting the pure CC
 * posture set `summaryOutputBudgetTokens = model.maxTokens` explicitly.
 *
 * SINGLE SOURCE: consumed by `compact()` (both real legs), `dryRunSummarizationClamp` (the 1.286
 * same-params dry-run judgment), and the prepare-time `compaction.window_config_warning` estimate —
 * so the dry run and the real clamp cannot drift.
 */
export declare function summaryOutputBudgetTokens(model: Model, settings: CompactionSettings): number;
/** Fidelity-disclosure payload for a truncated summarization input (bridged by the caller to the
 *  `compaction.input_truncated` trace frame). `keptChars` = surviving conversation chars (marker excluded). */
export interface SummarizationInputTruncation {
    label: "history" | "turn_prefix";
    droppedChars: number;
    keptChars: number;
    /**
     * How many WHOLE messages this drop removed from the summarization input, when the drop was the
     * PTL retry's group rollback (`summarizeWithPtlRetry`) rather than the proactive head+tail clamp.
     * The clamp cuts a mid-section of TEXT and leaves every message present, so it reports chars only
     * and omits this field; the rollback shifts entire assistant-turn groups off the front, and those
     * messages are folded out of the transcript by the same pass WITHOUT ever reaching the summarizer
     * — the fact the wrapper's scale disclosure has to state (see
     * {@link CompactionDetails.unsummarizedMessages}). Counted in the summarization-INPUT coordinate
     * (messages actually handed to the summarizer, i.e. post-`convertToLlm`), so it is a LOWER BOUND
     * on the transcript messages the summary does not cover.
     */
    droppedMessages?: number;
}
/** design/145 §1 — result of a window-safety clamp DRY RUN (see {@link dryRunSummarizationClamp}). */
export interface SummarizationClampDryRun {
    /** Conversation chars the clamp would drop across BOTH summary legs (history + split-turn prefix). */
    droppedChars: number;
    /** Conversation chars that would survive (marker excluded), across both legs. */
    keptChars: number;
    /** `droppedChars / max(1, droppedChars + keptChars)` — always ∈ [0,1]; 0 for empty content
     *  (nothing to drop) and <1 even at negative headroom (the clamp keeps a MIN_KEEP floor). */
    truncationRatio: number;
    /** Total serialized conversation chars (pre-clamp), across both legs — the honest "content size"
     *  coordinate for the fallback cost gate (chars ÷ the consumer's charsPerToken). */
    conversationChars: number;
    /** #500 — instruction-file SECTION chars this pass would send, SUMMED over the legs that run (a
     *  split turn sends it twice, and each leg's copy is bounded by its own geometry, so this is not
     *  `section.length` and cannot be re-derived by a caller). `0` when the seat is unfilled.
     *
     *  Exists for the design/145 门A fallback cost gate, which estimates what a MAIN-model re-run would
     *  prefill: the section is real input the fallback pays for, so a gate that counted only the
     *  conversation could pass a fallback that then exceeded the cap — the "钱花了任务照死" double loss
     *  the gate exists to prevent, reintroduced through the one input it did not know about. */
    instructionFilesChars: number;
}
/**
 * design/145 §1 — the SINGLE-SOURCE window-safety judgment: dry-run `clampSummarizationConversation`
 * with the SAME parameters the real summary call(s) would use (same serialization, same fixed-prompt
 * charge, same output reservation), WITHOUT sending anything. Consumed by `maybeCompact` to decide
 * whether an independent (smaller-window) compaction model would truncate the conversation past
 * `CompactionSettings.clampTolerance` — the r2 review killed the r1 token-domain twin formula
 * precisely because two formulas drift; this function IS the clamp, so the judgment cannot.
 *
 * Split-turn boundaries aggregate both legs (history + turn prefix): the real path clamps each leg
 * against its own budget (0.8× vs 0.5× reserve), so the dry run mirrors both and reports the summed
 * ratio. Multi-round compactions are covered by construction: the preparation's `previousSummary`
 * charges the fixed-prompt budget exactly as `generateSummary` does, so round 2's judgment is over
 * "prior summary + new increment", not the original history.
 */
export declare function dryRunSummarizationClamp(preparation: CompactionPreparation, model: Model, customInstructions?: string, charsPerToken?: number): SummarizationClampDryRun;
/** CC 198 jNl — verbatim marker inserted where the oldest conversation groups were dropped. */
export declare const COMPACTION_PTL_RETRY_MARKER = "[earlier conversation truncated for compaction retry]";
/** Generate or update a conversation summary for compaction.
 *  `summaryBudgetTokens` — the OUTPUT budget base (history leg = 0.8× it). Callers going through
 *  `compact()` get the decoupled, model-capped value from {@link summaryOutputBudgetTokens}; this
 *  parameter was historically the raw `settings.reserveTokens` (trigger reserve). */
export declare function generateSummary(currentMessages: AgentMessage[], model: Model, summaryBudgetTokens: number, apiKey: string | undefined, headers?: Record<string, string>, signal?: AbortSignal, customInstructions?: string, previousSummary?: string, thinkingLevel?: ThinkingLevel, streamFn?: StreamFn, runtime?: AgentCoreCompletionRuntimeDeps, charsPerToken?: number, onInputTruncated?: (info: SummarizationInputTruncation) => void, onPtlRetry?: () => void, contextInstructionFiles?: string): Promise<Result<string, CompactionError>>;
/** Prepared inputs for a compaction run. */
export interface CompactionPreparation {
    /** Entry id where retained history starts. */
    firstKeptEntryId: string;
    /** Messages summarized into the history summary. */
    messagesToSummarize: AgentMessage[];
    /** Prefix messages summarized separately when compaction splits a turn. */
    turnPrefixMessages: AgentMessage[];
    /** Whether compaction splits a turn. */
    isSplitTurn: boolean;
    /** Estimated context tokens before compaction. */
    tokensBefore: number;
    /** Previous compaction summary used for iterative updates. */
    previousSummary?: string;
    /** File operations extracted from summarized history. */
    fileOps: FileOperations;
    /** Budgeted invoked-skill retention for the DROPPED range (merged over the previous compaction's
     *  retained set), most-recent-first — ready to persist as `CompactionDetails.invokedSkills`. */
    invokedSkills: InvokedSkillRetention[];
    /** RB-398-c: offload handles from the DROPPED range (merged over the previous compaction's
     *  retained set, oldest-first, capped) — ready to persist as
     *  `CompactionDetails.persistedOutputRefs` and render as the summary footer. Optional so
     *  hand-built preparations (tests, external callers) stay valid; absent = none. */
    persistedOutputRefs?: string[];
    /** RB-398-b①: cumulative folded-message count (this range + the previous carrier's count) —
     *  ready to persist as `CompactionDetails.elidedMessages`. Optional (same rule as above). */
    elidedMessages?: number;
    /** PTL-shortfall accounting: the previous carrier's uncovered-message count, CARRIED FORWARD only — this pass's own
     *  uncovered count is not knowable at prepare time (it is produced by the summary leg's PTL
     *  rollback), so `compact()` adds its own drops on top before persisting
     *  `CompactionDetails.unsummarizedMessages`. Absent = nothing carried. */
    carriedUnsummarizedMessages?: number;
    /** #500 — the user's / project's instruction-file content, supplied by the DEPLOYMENT, for the
     *  summarization request — BOTH forms: as the independent request's first section, and inside the
     *  one message the fork form appends (below the cached prefix, so the fork's economics are
     *  untouched; the fork's own system prompt usually carries these files already, but nothing
     *  establishes that, so a populated seat is delivered rather than assumed). Rendered by
     *  {@link contextInstructionFilesSection} as
     *  the request's first section, and charged to the fixed-prompt budget by both summary legs and
     *  {@link dryRunSummarizationClamp} off this one value. Optional, and ABSENT ⇒ every summarization
     *  request byte is what it was before this field existed. Read
     *  {@link contextInstructionFilesSection}'s trust-tier note before filling it: host supply only.
     *
     *  SCOPE OF THAT INVARIANT, stated because the change has one deliberate effect OUTSIDE it
     *  (raised in review as a contradiction; it is not one, but it was unstated). Byte-identity is
     *  over the SUMMARIZATION REQUEST ASSEMBLY, and there it is exact. Registering
     *  `<instruction-files>` in the authority census additionally puts it in the derived
     *  fenced/shelled-lane sets, so external bytes that literally spell the tag are now defused with a
     *  ZWSP on those lanes — for every deployment, seat or no seat. That is not a compatibility break
     *  smuggled in: those lanes rewrite bytes BY CONTRACT (that is what makes them fences), it is what
     *  registering any new authority envelope means in this tree, and the alternative — a
     *  model-facing authority tag that the fences built to defuse forgeries do not defuse — is
     *  strictly worse. Disclosed rather than hidden behind a flag. */
    contextInstructionFiles?: string;
    /** Settings used to prepare compaction. */
    settings: CompactionSettings;
}
/** Prepare session entries for compaction, or return undefined when compaction is not applicable.
 *  `charsPerToken` (design/123 D2, signature threading): keeps `tokensBefore` and the cut-point
 *  accounting in the same per-model structural coordinate as the caller's trigger estimate.
 *  `windowTokens` (window-aware retention clamp): the trigger window of the model that will consume
 *  the post-compaction context. When provided, the invoked-skills retained area is clamped to the
 *  headroom that window leaves after the existing geometry (`window − reserveTokens −
 *  keepRecentTokens`, in chars) — the CC-shaped 20k/100k retention caps are UPPER BOUNDS tuned for
 *  large windows, and on a small-window model an unclamped area would push every post-compaction
 *  request straight back over the window the compaction just freed. Absent ⇒ the plain CC-shaped
 *  budget (legacy behavior).
 *  `contextInstructionFiles` (#500): the deployment's instruction-file content for the INDEPENDENT
 *  summary form — carried straight through onto {@link CompactionPreparation}, whose field doc and
 *  {@link contextInstructionFilesSection}'s trust-tier note are the contract. Absent/blank ⇒ the
 *  field is omitted and nothing downstream changes. */
export declare function prepareCompaction(pathEntries: SessionTreeEntry[], settings: CompactionSettings, charsPerToken?: number, windowTokens?: number, contextInstructionFiles?: string): Result<CompactionPreparation | undefined, CompactionError>;
export { computeFileLists, serializeConversation } from "./utils.js";
/** Generate compaction summary data from prepared session history.
 *  `charsPerToken` (design/123 D2, signature threading): coefficient for the summarization-input
 *  window guard. Defaults to the SUMMARY model's own `charsPerToken` (the estimated input is sent to
 *  THAT model), then 4. */
export declare function compact(preparation: CompactionPreparation, model: Model, apiKey: string | undefined, headers?: Record<string, string>, customInstructions?: string, signal?: AbortSignal, thinkingLevel?: ThinkingLevel, streamFn?: StreamFn, runtime?: AgentCoreCompletionRuntimeDeps, charsPerToken?: number, onInputTruncated?: (info: SummarizationInputTruncation) => void, onPtlRetry?: () => void, 
/** design/169-A — when present, the summary is attempted in the CC FORK form first (main prefix +
 *  one instruction message; see {@link CompactionForkContext}). A prompt-too-long on the fork form
 *  falls back ONCE to the independent form below (which can clamp; the fork form must not — a
 *  clamp would rewrite the cached prefix). The caller only passes this when the summary model IS
 *  the main model; an independent compaction model has no main prefix to share. */
forkContext?: CompactionForkContext): Promise<Result<CompactionResult, CompactionError>>;
/** The prefix leg's base prompt, with the caller's instructions injected in the SAME shape the
 *  history leg uses (`\n\nAdditional Instructions:\n${…}`, CC 198 verbatim). Single source so the
 *  dry-run clamp charges exactly what the real call sends. */
export declare function turnPrefixSummarizationPrompt(customInstructions?: string): string;
