import { DocumentObject, paginate, OutlineNode } from "@adeu/core"; export interface ToolResult { content: { type: "text"; text: string; }[]; structuredContent?: any; isError?: boolean; [key: string]: unknown; } /** * Precomputed projection products (doc-cache): the split body/appendix and * the body pagination that every builder would otherwise recompute per call. * All builders accept it OPTIONALLY — omitted, they compute exactly what * they always did, so responses stay byte-identical cached vs uncached. */ export interface ProjectionBundle { body: string; appendix: string; pagination: ReturnType; } export { build_changes_response, build_fields_response, fields_discovery_hint, banner_for_path, } from "./ledger.js"; /** * Renders `prefix **match** suffix` with the document's own bold/italic * projection markers stripped first, so the highlight cannot collide with * markers already present — a regex match crossing styled runs used to * render as `**The **Supplier** _shall provide**_` (QA 2026-07-19 v8 F-10). * Markers are detected over the WHOLE region (a match boundary can cut a * marker away from its word-edge context), then each part is rebuilt from * the surviving characters. Mirrors Python's _emphasized_snippet. */ export declare function emphasizedSnippet(prefix: string, match: string, suffix: string): string; /** * Renders `region` with every matched span wrapped in `**…**`, stripping the * document's own style markers first (same rules as emphasizedSnippet). * Accepts MULTIPLE spans so one paragraph with several hits renders as one * entry with every hit highlighted (QA round 3, finding 3.10). */ export declare function emphasizedSnippetSpans(region: string, spans: Array<[number, number]>): string; export declare const SEARCH_TOKENS_PER_MATCH = 60; export declare const CHARS_PER_TOKEN = 4; export declare const SNIPPET_RADIUS_LADDER: number[]; export declare const SEARCH_FIXED_CHROME_TOKENS = 120; export declare const SEARCH_ENTRY_CHROME_TOKENS = 22; export declare const SEARCH_MIN_SNIPPET_TOKENS = 13; /** * Approximate-token ceiling for a search response that was ASKED for * `max_matches` hits and actually rendered `rendered_count` of them (defaults * to all of them). The chrome term is sized on hits RENDERED, not requested: * once the budget pass starts dropping hits it must stop granting per-hit * allowance for hits no longer in the response. Mirrors Python's * search_budget_tokens (_response_builders.py:181-202). */ export declare function search_budget_tokens(max_matches: number, rendered_count?: number): number; /** * Extends a snippet window until every CriticMarkup span it overlaps is whole, * returning the widened `[start, end]`. {>>…<<} meta bubbles are MULTI-line * and a clamped window cuts spans on BOTH edges, so agents could not harvest * ids/pairings from search results (QA round 3, finding 3.12). * * Delimiters are walked IN ORDER with a depth counter per pair, never counted: * a window holding one stray `--}` on the left and one stray `{--` on the * right balances arithmetically while reading `l1--}…{--del` (QA round 4, * finding 1). A closer seen at depth 0 belongs to a span that opened before * `start`, so `start` moves back to that opener; a pair still open when the * scan reaches `end` pushes `end` forward to its closer. Each step strictly * widens the window, so the loop terminates. * * Mirrors Python's _balance_snippet_window. NOTE the backward search: * Python's `body.rfind(opener, 0, start)` requires the opener to fit ENTIRELY * before `start`, while JS `lastIndexOf`'s second argument is the START of the * match — hence the `start - opener.length` bound, and the negative guard * (`lastIndexOf(x, -1)` searches at index 0 instead of failing). */ export declare function balanceSnippetWindow(body: string, start: number, end: number): [number, number]; export declare function render_outline_tree(nodes: OutlineNode[], max_level?: number, verbose?: boolean, no_chrome?: boolean): string; export declare function build_full_document_response(text: string, file_path: string, bundleOrOpts?: ProjectionBundle | { no_chrome?: boolean; fields_banner?: string | null; } | boolean, no_chrome_param?: boolean | { no_chrome?: boolean; fields_banner?: string | null; }): ToolResult; /** * The whole-document response budget refusal for an oversized unbounded read * (port of Python's build_budget_guard_message, _response_builders.py:654-700). * * The page count comes from the SAME pagination every reader path uses, so the * page numbers the refusal advertises are the page numbers `page=N` accepts. * The total is measured on `projected_text` (`:695` measures it too), so the * caller decides what is counted by choosing what it passes. Callers pass the * string the refused read WOULD have returned — `bundle.body`, the structural * appendix excluded — which is what Python's mode='full' text already is * (doc_cache.py projects it with include_appendix=False). * * `nodes` are the cached outline nodes OF THE VIEW being refused (the clean * view has its own — DocCache.ensureCleanOutline). Documents with no L1 * heading get no outline section at all, rather than a "(No headings * detected)" placeholder; `null` ships the refusal without a heading map. */ export declare function build_budget_guard_message(projected_text: string, file_path: string, nodes: OutlineNode[] | null, bundle?: ProjectionBundle): string; export declare function build_paginated_response(text: string, page: number, file_path: string, bundleOrOpts?: ProjectionBundle | { no_chrome?: boolean; fields_banner?: string | null; } | boolean, no_chrome_param?: boolean | { no_chrome?: boolean; fields_banner?: string | null; }): ToolResult; export declare function build_page_range_response(text: string, start: number, end: number, file_path: string, bundleOrOpts?: ProjectionBundle | { no_chrome?: boolean; } | boolean, no_chrome_param?: boolean | { no_chrome?: boolean; }): ToolResult; export declare function build_outline_response(doc: DocumentObject, projected_text: string, file_path: string, outline_max_level?: number, outline_verbose?: boolean, paragraph_offsets?: Map | null, no_chromeOrOpts?: boolean | { no_chrome?: boolean; }): ToolResult; /** * Assembly half of build_outline_response, byte-identical to it: lets the * doc-cache serve outline mode from precomputed nodes + pagination without a * document load. Kept as ONE shared function so the header/format can never * drift between the cached and uncached paths. */ export declare function render_outline_response(nodes: OutlineNode[], total_pages: number, file_path: string, outline_max_level?: number, outline_verbose?: boolean, no_chromeOrOpts?: boolean | { no_chrome?: boolean; }): ToolResult; export declare function build_appendix_response(text: string, page: number, file_path: string, bundleOrOpts?: ProjectionBundle | { no_chrome?: boolean; } | boolean, no_chrome_param?: boolean | { no_chrome?: boolean; }): ToolResult; /** * Filters projected Markdown to exact substring or regex matches. * * `page` semantics: * - undefined/null or "all" (case-insensitive): ALL matches across the whole * document. When matches span >1 document page, include a one-line * distribution summary. * - positive int N: only matches whose offset falls within document page N. * If N has zero hits but the query exists elsewhere, emit a helpful * empty-result pointer (not an error). If N exceeds the document's total * pages, throw. * - anything else (0, negative, a range string, a non-"all" string): throw. * * Occurrence counts (the "appears X times" line under each match) are always * computed from the FULL match set, never filtered. * * `max_matches < 1` renders NO match entries — just the counts header and a * note naming the knob to raise. It is never rewritten to the default 20. * * Port of Python's build_search_response (_response_builders.py:716-1278); the * `is_cli` strict-regex branch is not ported — the MCP path always downgrades * an uncompilable pattern to a literal search with a note. */ export declare function build_search_response(text: string, search_query: string, search_regex: boolean, search_case_sensitive: boolean, page: number | string | undefined, file_path: string, bundle?: ProjectionBundle, opts?: { max_matches?: number; match_offset?: number; full_paragraph?: boolean; no_chrome?: boolean; }): ToolResult; //# sourceMappingURL=response-builders.d.ts.map