/** * The single place an unlisted tool's fate is decided: covered by a field list, * exempt with a stated reason, or undeclared — nobody has classified it. * * `undeclared` is NOT a runtime alarm; every arm leaves the tool unfolded, which * is what an unlisted tool already got. The signal is the partition test, which * reads this function. * @param {string} tool * @param {Record} [fields] * @returns {{ kind: "covered", fields: string[] } | { kind: "exempt", reason: string } | { kind: "undeclared" }} */ export function scopeFor(tool: string, fields?: Record): { kind: "covered"; fields: string[]; } | { kind: "exempt"; reason: string; } | { kind: "undeclared"; }; /** * True iff any UTF-16 code unit is outside ASCII (> 0x7F). Surrogates (astral * chars) are >= 0xD800 so they count; ASCII control chars (tab, newline) stay * ASCII. A plain loop, not a regex, to avoid a control char in the pattern. * @param {string} value * @returns {boolean} */ export function hasNonAscii(value: string): boolean; /** * Model-facing note naming the fields whose confusables were folded. * @param {string[]} normalized * @returns {string} */ export function normalizeContext(normalized: string[]): string; /** * Keep only the findings whose TOKEN is foldable — see the module header for the * two conditions (folds to pure ASCII; is not a lone glyph) and THREAT-MODEL.md * for why declining the rest forfeits no enforcement. * * Every finding is validated before it is judged, so an adversarial scanner's * bogus finding throws rather than being quietly dropped by the gate. * @param {string} text * @param {Array<{ index: number, char: string, latinEquivalent: string }>} findings * @returns {Array<{ index: number, char: string, latinEquivalent: string }>} */ export function selectFoldableFindings(text: string, findings: Array<{ index: number; char: string; latinEquivalent: string; }>): Array<{ index: number; char: string; latinEquivalent: string; }>; /** * Replace every scan-flagged confusable with its ASCII (latin) equivalent. * `index` is a UTF-16 offset into `text` and `char` is the matched glyph (which * may be an astral, 2-unit char); splice highest-index first so a * length-changing fold never shifts the offsets of earlier findings. * @param {string} text * @param {Array<{ index: number, char: string, latinEquivalent: string }>} findings * @returns {string} */ export function foldConfusables(text: string, findings: Array<{ index: number; char: string; latinEquivalent: string; }>): string; /** * Normalize confusable/homoglyph chars in the path/command fields of a tool * call. Returns the updated input plus the fields touched, or null when nothing * changed. Throws if the scanner fails (the caller fails closed: an * un-normalized confusable could slip past a deny rule). * * `scan` overrides the confusable engine: `scan(text)` → `{ findings }` (an * empty `findings` means no confusables). Omit it to use namespace-guard, which * is resolved lazily. `fields` maps a tool name to the input keys to fold; * defaults to {@link DEFAULT_FIELDS}. * @param {string} tool * @param {any} toolInput * @param {{ scan?: (text: string) => { findings: Array<{ index: number, char: string, latinEquivalent: string }> }, fields?: Record }} [options] * @returns {{ updatedInput: any, normalized: string[] } | null} */ export function normalizeConfusables(tool: string, toolInput: any, options?: { scan?: (text: string) => { findings: Array<{ index: number; char: string; latinEquivalent: string; }>; }; fields?: Record; }): { updatedInput: any; normalized: string[]; } | null; /** * Default path/command fields to fold per tool. Agent-agnostic: the keys are * the conventional Claude/Anthropic tool names, but a caller with a different * tool surface passes its own `fields` map. * @type {Record} */ export const DEFAULT_FIELDS: Record; /** @type {Record} */ export const EXEMPT_TOOLS: Record; /** @type {ReadonlyArray<{ pattern: RegExp, reason: string }>} */ export const EXEMPT_TOOL_PATTERNS: ReadonlyArray<{ pattern: RegExp; reason: string; }>;