/** * Numeric verbosity from _AGENT_SANITIZER_TRACE: 0 off, 1 info, 2 debug. * Unknown, empty, or "off" → 0. * @param {NodeJS.ProcessEnv} [env] * @returns {number} */ export function traceThreshold(env?: NodeJS.ProcessEnv): number; /** * Emit one JSON trace line for `event` at `level` (default "info") carrying the * metadata `fields`. No-op when the channel is below `level`; best-effort on write. * @param {string} event * @param {Record} [fields] * @param {"info"|"debug"} [level] * @returns {void} */ export function trace(event: string, fields?: Record, level?: "info" | "debug"): void; /** * `sink` with {@link trace}'s best-effort posture forced onto it: a throw is * swallowed, so an announcement can never break the hook making it. * * This is what makes the sink safely injectable. The announcement call sites were * placed under the guarantee that emitting cannot fail, and one of them relies on * it outright: scan-invisible-chars announces BEFORE it auto-cleans the * contaminated instruction files and arms the PreToolUse gate, with no catch * above it, so a throwing sink there would abort the scan — leaving the payload on * disk, the gate un-armed, and NO announcement on any channel. The loss the * announcement exists to make loud would itself be silent. * * Swallowing is right here and is not licence to swallow elsewhere in this tree: * a dropped announcement is already loud in the host's own detector — that is what * a trace channel is — whereas a killed hook is loud nowhere. * @param {TraceFn} sink * @returns {TraceFn} */ export function bestEffortTrace(sink: TraceFn): TraceFn; /** * The sink a hook emits through, given a host's or none: a HOST sink is made * best-effort and charged to the slow-hook notice's host-extension window; the * package's own {@link trace} is handed back untouched. * * A composer's sink may write over a socket or spawn a subprocess whose cost * this process cannot see, so an uncharged one leaves its wait in the notice's * unattributed remainder and its in-process CPU billed to the sanitizer. The * default sink's file write IS the sanitizer's own work, so charging it would * move a real per-call cost out of the figure that names it. * * Both properties are bound HERE, not at each hook, so a sixth caller can * neither drop one nor nest them wrong — and the nesting is load-bearing twice * over. The charge is booked in a `finally` inside the best-effort bracket, so * a sink that spends its wait and THEN throws is measured before the throw is * swallowed. And the exemption above reads the host's own sink, which a * best-effort wrapper applied first would have replaced with a truthy one. * @param {TraceFn} [sink] a host's sink; absent asks for the package channel * @returns {TraceFn} */ export function hookTrace(sink?: TraceFn): TraceFn; /** * The sink shape a hook emits through: the event name, its metadata fields, and * the level. A host implementation receives the same {@link TraceEvent} names the * default emits, so it can remap them onto its own channel's vocabulary. * * A sink is NOT required to be total — throw freely. Every hook binds the one it * was given through {@link hookTrace}, which is what upholds the channel's * never-breaks-a-hook posture on host code that cannot promise it. * @typedef {(event: string, fields?: Record, level?: "info"|"debug") => void} TraceFn */ /** Trace-channel event names. */ export const TraceEvent: Readonly<{ HOOK_RAN: "hook_ran"; SCAN_INVISIBLE_CHARS_RAN: "scan_invisible_chars_ran"; SCAN_LOADED_INSTRUCTIONS_RAN: "scan_loaded_instructions_ran"; }>; /** * The sink shape a hook emits through: the event name, its metadata fields, and * the level. A host implementation receives the same {@link TraceEvent} names the * default emits, so it can remap them onto its own channel's vocabulary. * * A sink is NOT required to be total — throw freely. Every hook binds the one it * was given through {@link hookTrace}, which is what upholds the channel's * never-breaks-a-hook posture on host code that cannot promise it. */ export type TraceFn = (event: string, fields?: Record, level?: "info" | "debug") => void;