Follow-up to the event-time proxy snapshot: every upstream attempt now
carries attribution, the direct/unknown contract is enforced everywhere,
and the queued upstream_errors array has hard bounds again.
Attribution coverage
- compact fallback retry helper and the streaming compact failover event
now stamp proxy_id/proxy_name; the non-streaming SSE compact retry
previously dropped its first attempt on `continue` and now records it
- raw chat-completions stream truncation events (new on main) attributed
- Anthropic/Bedrock transport errors get attribution inside the shared
handleUpstreamTransportError helper, so all five callers inherit it
- OpenAI first-output timeout takes attribution from the caller: HTTP
callers pass the HTTP rule, the WS passthrough relay passes the WS rule
(no managed proxy => unknown, never direct, because http.DefaultClient
may honor environment proxies)
Contract hardening
- single opsUpstreamProxyAttribution returns (id, name) from one decision;
a hydrated Proxy without a durable ID is unknown, not id=null+name
- both normalizers (struct at append time, JSON on detail reads) enforce
proxy_id==null <=> sentinel name; blank managed name => "proxy"
- detail-read normalization edits only the attribution keys via sjson, so
keys from older struct versions and stored key order are preserved
Queue bounds
- keep at most 256 attempts and ~512 KiB serialized per entry, newest
first; the oldest retained event records dropped_earlier_attempts
- attempts outside the 16-event body window trim url/message to 512 bytes
- restore the empty-event drop rule, judged before the body window clears
detail so older real attempts survive
Tests cover each site above (managed proxy, direct, WS unknown), the
normalizer invariants, key/order preservation, and the count/byte bounds.