/** * @param {string} text * @returns {boolean} */ export function needsMarkdownPipeline(text: string): boolean; /** * Warning fragment for Layer 2's stripped content — counts only, never the * content itself (which would re-inject what was just removed). * @param {{ comments: number, hidden: number }} removed * @returns {string} */ export function describeRemoved(removed: { comments: number; hidden: number; }): string; /** * Full warning for Layer 2's preserved-but-reported content (scripting and * resource tags, data: URIs), or "" when there is nothing to report. * @param {{ tags: Record, dataSrc: number }} warned * @returns {string} */ export function describeWarned(warned: { tags: Record; dataSrc: number; }): string; /** * Delete each verbatim span in `spans` from `text`. The secure Layer-5 * primitive: a filter can only ask for deletions, so this can never inject * bytes. Returns the new text and how many distinct span-occurrences were * removed (0 when no span was present). * @param {string} text * @param {string[]} spans * @returns {{ text: string, removed: number }} */ export function deleteVerbatimSpans(text: string, spans: string[]): { text: string; removed: number; }; /** * @typedef {{ * html?: boolean, * exfilScan?: boolean, * redact?: (text: string) => Promise | (RedactResult|null), * filterInjection?: (text: string) => Promise | (Layer5Result|null), * sgrCarveOut?: boolean, * }} SanitizeTextOptions */ /** * Run the configured layers over a single text blob. Layer 1 always runs; the * rest are opt-in via `options`. Layer 4 (`redact`) is the fail-closed path: a * redactor that throws is rethrown wrapped, so the caller suppresses the * output rather than emitting an unvetted value. That fail-closed behavior * also applies to Layer 4's re-scan after a Layer-5 span deletion (see Layer * 5, below) — a redactor failure there fails the whole call closed too. * `reveal` is the pre-Layer-2 text, present only when the HTML splice removed * bytes, so a caller can persist what was hidden for later inspection (see * {@link applyMarkdownPipeline}); the field is omitted otherwise. * @param {string} text * @param {SanitizeTextOptions} [options] * @returns {Promise<{ cleaned: string, warnings: string[], modified: boolean, sgrNote: boolean, reveal?: string }>} */ export function sanitizeText(text: string, options?: SanitizeTextOptions): Promise<{ cleaned: string; warnings: string[]; modified: boolean; sgrNote: boolean; reveal?: string; }>; /** * True only for arrays and PLAIN objects — the two shapes whose contents are * safe to walk via `Object.entries` without silently dropping data. An exotic * object (Map/Set/Date/RegExp/typed array/class instance) carries its data in * internal slots that `Object.entries` does not enumerate, so descending into * one and rebuilding it from its entries corrupts it to `{}` (or an empty * clone). Those pass through as OPAQUE LEAVES instead — unchanged — preserving * the tool-output shape a harness matches on. A null-prototype object is treated * as plain (its own enumerable string keys are the whole story). * @param {any} value * @returns {boolean} */ export function isWalkableContainer(value: any): boolean; /** * Sanitize every string leaf of a tool-output value, preserving its shape (a * structured tool output whose shape changes would be ignored by a harness, * leaking the raw value). Non-string leaves pass through; `warnings` * accumulates across leaves. `sgrNote` is the OR across leaves. * * Fails CLOSED on two hostile shapes that would otherwise throw a `RangeError` * as an unhandled async rejection (a DoS that leaves the output un-sanitized): * nesting past {@link MAX_DEPTH}, and a reference cycle. Either replaces the * offending subtree with a placeholder string + a warning, never passing the * raw subtree through. Keys are also screened for hidden chars (see below). * * `reveals` accumulates each string leaf's pre-Layer-2 text (present only when * the HTML splice removed bytes) so a caller can persist what was hidden — the * structured-output analogue of {@link sanitizeText}'s `reveal`. Same * mutated-accumulator contract as `warnings`. * @param {any} value * @param {SanitizeTextOptions} options * @param {string[]} warnings * @param {string[]} [reveals] * @returns {Promise<{ value: any, modified: boolean, sgrNote: boolean }>} */ export function sanitizeValue(value: any, options: SanitizeTextOptions, warnings: string[], reveals?: string[]): Promise<{ value: any; modified: boolean; sgrNote: boolean; }>; /** * Compose the model-facing context line for a sanitized/flagged tool output. * `injectionAlert` is the caller's optional trailing alert (e.g. appended only * for untrusted-ingress tools where a semantic-injection filter actually ran). * @param {boolean} modified output bytes were changed (vs. flagged only) * @param {string[]} warnings * @param {{ injectionAlert?: string }} [options] * @returns {string} */ export function composeContext(modified: boolean, warnings: string[], { injectionAlert }?: { injectionAlert?: string; }): string; /** * Replace every string leaf of `value` with `message`, preserving shape so a * fail-closed placeholder matches the tool's output schema. Non-string leaves * pass through. * * Shares {@link sanitizeValue}'s depth/cycle guard for the same reason: this * runs on the fail-closed path (an already-suspect output), so a 200k-deep or * self-referential value must NOT blow the stack here — that would re-open the * very hole suppression exists to close. Past {@link MAX_DEPTH} or on a cycle it * substitutes `message` for the offending subtree (already the suppression * sentinel, so the placeholder is consistent with the rest of the output). * @param {any} value * @param {string} message * @returns {any} */ export function suppressToolOutput(value: any, message: string): any; /** * Closed enum of LIBRARY-OWNED Layer-5 warning codes — the ONLY warning values * the injected `filterInjection` seam may return. This mirrors the `found`-code * contract (`CATEGORY` in ./invisible.mjs): the seam speaks a fixed vocabulary * of codes, and the LIBRARY owns the human-readable string each maps to. Free * text from the filter is REFUSED (see `mapFilterWarning`), because the filter * runs on attacker-influenced content and its output is concatenated into the * model-facing context WITHOUT passing back through Layer 1 — so a compromised * or prompt-injected filter that could emit arbitrary `warning` text would * defeat the "a compromised filter can only remove bytes, never inject" seam * contract. Branch on these codes; the prose below is not part of the contract. * @type {Readonly<{ SPANS_REMOVED: "spans-removed", FILTER_FLAGGED: "filter-flagged", FILTER_ERROR: "filter-error" }>} */ export const FILTER_WARNING: Readonly<{ SPANS_REMOVED: "spans-removed"; FILTER_FLAGGED: "filter-flagged"; FILTER_ERROR: "filter-error"; }>; /** * Maximum container nesting `sanitizeValue` / `suppressToolOutput` will descend * before failing closed. The JS engine's own call-stack limit is many thousands * of frames deep, so 200 is a wide safety margin below it: a real tool output * never nests this far, while a hostile 200k-deep array (or a self-referential * cycle) would otherwise blow the stack as an UNHANDLED async rejection — the * output then escapes sanitization entirely (fail-open DoS). Past this depth the * subtree is replaced with a placeholder and a warning is recorded, so the * caller still emits a sanitized, flagged result instead of crashing. */ export const MAX_DEPTH: 200; /** * Layer-4 result: the redacted text, the category labels redacted, and an * optional caller-supplied annotation appended to the warning. */ export type RedactResult = { text: string; found: string[]; note?: string; }; /** * A {@link FILTER_WARNING} enum code — the closed vocabulary the Layer-5 seam * may return in `warning`. See FILTER_WARNING for the meanings. */ export type FilterWarningCode = "spans-removed" | "filter-flagged" | "filter-error"; /** * Layer-5 result: verbatim spans to delete (the only mutation a filter may * request) and/or a warning CODE (never free text — the library owns the * message). Null means the filter made no finding. */ export type Layer5Result = { removeSpans?: string[]; warning?: FilterWarningCode; }; export type SanitizeTextOptions = { html?: boolean; exfilScan?: boolean; redact?: (text: string) => Promise | (RedactResult | null); filterInjection?: (text: string) => Promise | (Layer5Result | null); sgrCarveOut?: boolean; };