* feat(api): deterministic request-body serialization via stableStringify
Add `stableStringify` helper that emits JSON with object keys sorted
lexicographically at every depth (arrays preserved). Adopt it in the
OpenAI-compatible shim and the Codex Responses-API shim for the outgoing
request body.
WHY: OpenAI / Kimi / DeepSeek / Codex use implicit prefix caching keyed
on exact request bytes. Spurious insertion-order differences in
spread-merged body objects otherwise invalidate the cache on every turn.
Also a pre-requisite for Anthropic `cache_control` breakpoint hits.
Byte-equivalent to `JSON.stringify` when keys already happen to be in
lexical insertion order, so strictly additive across providers.
* fix(api): preserve circular-ref TypeError in stableStringify + cover GitHub fallback
Replace two-pass sortingReplacer approach with a single-pass deepSort that
tracks ancestor objects via WeakSet, throwing TypeError on cycles (same
contract as native JSON.stringify) and correctly handling DAGs via
try/finally cleanup. Switch the GitHub Copilot /responses fallback in
openaiShim.ts from JSON.stringify to stableStringify so that path is also
byte-stable for prefix caching.
Regression coverage added: top-level cycle, deep nested cycle, DAG safety.
* fix(api): align stableStringify with native JSON.stringify pre-processing
Replicate native JSON.stringify pre-processing inside deepSort so
serialization output matches native behavior beyond key ordering:
- invoke toJSON(key) when present (Date, URL, user classes); pass ''
at top-level, property name for nested values, index string for
array elements
- unbox Number/String/Boolean wrappers via valueOf() so new Boolean(false)
doesn't get truthy-coerced
- run cycle detection on the post-toJSON value so a toJSON returning
an ancestor still throws TypeError; DAGs continue to not throw
- drop properties whose toJSON returns undefined, matching native
Add focused stableStringify.test.ts (21 cases) asserting equality with
JSON.stringify across toJSON paths, wrapper unboxing, cycle/DAG handling,
and sortKeysDeep parity.