/**
 * design/80 D-1 §2 (slice 1a.2) — the engine-only **vetted canonical serializer** used to mint a stable,
 * opaque {@link boundInputHashOf | boundInputHash} over a pending tool call's arguments.
 *
 * ## Why a bespoke serializer (and why engine-ONLY)
 * The package's only runtime deps are typebox + the MCP SDK — there is no canonical/stable JSON serializer,
 * and `JSON.stringify` is **not** canonical (object key order is insertion order, `-0`/`NaN`/`Infinity` are
 * mishandled, and it offers no type-tagging). The review r3 ruling (design/80 §7) is the load-bearing
 * constraint: **never let two runtimes each compute a canonical form and compare** — a `JSON.stringify`
 * divergence (or a serializer-version drift across a worker upgrade) would produce a false mismatch and
 * fail-closed a *legitimate* approval. So the boundInputHash is **server-minted opaque**: the engine computes
 * it ONCE at suspend-mint with THIS serializer, persists it on the checkpoint, and a resume verifies by
 * **opaque string equality** against the persisted value (it never re-serializes the resume-side args). This
 * serializer therefore only needs to be *internally deterministic* (same value → same bytes within one
 * runtime); it is deliberately NOT exported from the package index, so no cross-wire consumer (the SDK /
 * service) can be tempted to recompute it — they treat the hash as opaque and echo it verbatim.
 *
 * ## Canonical form
 * A total function (never throws) that emits a **type-tagged, length-prefixed** string so distinct values of
 * distinct types can never collide (`number 1` ≠ `string "1"` ≠ `boolean`, and `{"a":"b"}` ≠ `{"ab":""}`):
 *  - `null` → `N`, `undefined` → `U` (encoded distinctly — `{a:undefined}` must not collide with `{}`)
 *  - boolean → `b1` / `b0`
 *  - number → `n:<canonical>`; finite via `String(x)` (shortest round-trippable, deterministic per value);
 *    `-0` normalized to `0`; `NaN`/`±Infinity` get explicit distinct tokens (never collapsed to `null`)
 *  - bigint → `i:<value>`
 *  - string → `s<byteLen>:<raw>` — **length-prefixed, NOT normalized**: equal inputs map to equal output and
 *    distinct inputs (e.g. a non-NFC string vs its NFC form) map to distinct output. The serializer never
 *    silently Unicode-normalizes (that would let two different argument payloads share one hash).
 *  - array → `a<len>:[<el>…]` — order preserved (semantic)
 *  - object → `o<count>:{<keyLen>:<key>=<val>…}` — **own enumerable keys sorted** by UTF-16 code unit, so
 *    `{a,b}` and `{b,a}` canonicalize identically. (Duplicate keys cannot exist on a live JS object —
 *    last-wins already happened at parse/construction — so they need no special handling here.)
 *  - function / symbol → tagged tokens (`fn` / `y:<desc>`) so the serializer stays total; these never appear
 *    in design/37 post-hook tool args (which are JSON values), but encoding rather than throwing keeps a
 *    weird arg from silently degrading a durable suspend to a non-durable fallback.
 *  - non-JSON OBJECT brands (Date/Map/Set/RegExp/typed arrays/boxed primitives/…) → a **type-discriminator
 *    token carrying their state** (see {@link encodeBranded}). Built-ins keep their state in internal slots,
 *    so encoding them by own enumerable keys alone folded every one of them onto the SAME empty-object form:
 *    `new Date(0)`, `new Date(9e12)`, a `Map`, a `Set` and `{}` all minted one hash, and an equality check
 *    over that hash accepted a substituted payload (false-accept — the direction a binding cannot afford,
 *    since a mismatch is visible and a match is not). Reachable only when a DEPLOYMENT supplies non-JSON
 *    values (model-produced args are JSON; a hook / policy rewrite is the way in).
 *
 * ## Compatibility boundary of the brand tokens
 * JSON values (null/boolean/number/string/array/plain object) hash **byte-identically** to before the
 * discriminator existed — the brand is read from `Object.prototype.toString`, and everything that reports
 * `[object Object]` (including a class instance with no `Symbol.toStringTag`, and a cross-realm plain object
 * whose `Object.prototype` differs) keeps the own-enumerable-keys encoding. Only NEWLY minted hashes over
 * non-JSON values move. That is safe by construction for the persisted ones: the hash is opaque and
 * server-minted, a resume compares the persisted string and never re-serializes.
 */
export declare function canonicalize(value: unknown): string;
/** What {@link canonicalize} returns when even its top level cannot be encoded — a reader that must never accept a
 *  constant (the checkpoint row digest) refuses on it. */
export declare const UNCANONICALIZABLE = "<uncanonicalizable>";
/**
 * design/80 D-1 §2 — the server-minted **opaque** boundInputHash: a SHA-256 (hex) over {@link canonicalize}.
 * Bound at suspend-mint to the pending tool call's input (`pendingAction.args`) and persisted on the
 * checkpoint; a resume verifies the echoed value by string equality (never re-hashing the resume-side args).
 * The hex digest is fixed-length and opaque — exactly what the operator sees / the D-G signer signs.
 */
export declare function boundInputHashOf(value: unknown): string;
