0.5.2

Caller-owned threadId for the Claude facet

A single-fix cohort, shipped for the first downstream consumer that hit it in production forensics. The wire vocabulary is untouched — AGJSON_VERSION stays 1.0.0-draft.2.

createClaudeNormalizer({ threadId })

The Claude Agent SDK wire has no thread concept — only session_id — so the claude facet has always relabeled that id as the AgJSON threadId, a placeholder in the same spirit as the openai/vercel facets’ fixed labels. That placeholder is fine for self-contained streams, but it leaks: a consumer that persists events verbatim under its own thread identity ends up with mid-stream rows keyed by the SDK’s session id while its session records carry the real thread id (guuey#415, where the mismatch confounded an identity investigation).

The factory now accepts an options object with threadId: the runtime that owns the real partition root stamps it at construction, and every emitted entity — messages, synthesized turn.starts, notice rows, denial records — carries one id everywhere. Per SPEC §3, threadId is the partition root a key-value persistence layer writes by; the layer that owns that identity is the caller, not the wire.

Absent the option, the legacy session_id relabeling stands unchanged — recorded cassettes and existing consumers are byte-identical. Synthesized turn ids continue to embed the session id as opaque identifiers; that is deliberate and not part of this change.