fix(sdk): preserve rich blocks in realtime message events

Merged
WI
3d ago
+514-15

Realtime message events currently lose rich blocks that REST reads preserve. Node delivery transforms omit the field when reconstructing messages, and the shared workspace event schema strips it during parsing. Preserve optional/nullable blocks through both paths so realtime consumers receive the same structured content as REST consumers.

The fix restores channel creation/update events and thread replies through both engine workspace transforms and SDK node transforms. DM/group-DM transforms also preserve blocks if supplied, but this does not add DM send/storage support. Nested blocks remain authoritative, including explicit null and empty arrays. Messages without blocks keep their existing shape.

Realtime blocks are opaque unknown[], matching the engine's accepted input, so custom properties and arbitrary block kinds are retained. WebSocket-only opaque casing preserves nested custom keys. Existing HTTP response casing and outgoing typed actionId behavior stay unchanged, with strict compatibility tests.

Companion to https://github.com/AgentWorkforce/relay/pull/2106; addresses section 1 of https://github.com/AgentWorkforce/relay/issues/1359. The Relay PR fixes client typing and immediate listener guidance. Keep that issue open until a human-published SDK version is integrated into Relay; this PR performs no release.

Validation

  • Original regression tests: 8 failures before the fix.
  • TypeScript SDK suite: 532 passed, including strict type checks.
  • Shared types suite: 281 passed.
  • Engine workspace transforms: 43 passed.
  • SDK and engine builds, package lint, and whitespace checks: passed.
  • Transport regressions cover server-produced workspace frames through SDK parsing, node delivery, a 49 KB payload, arbitrary/custom block values, flat/nested DM-shaped payloads, and absent/null/empty blocks.
  • Local cross-repository fixture using built SDKs: real Relaycast node WebSocket transport -> Relay adapter -> public addListener preserves the 49 KB blocks payload and matches REST readback. All I/O uses fixtures; no live credentials or services.
  • Two independent agent reviews: PASS, no remaining correctness findings after autofix.

Validation limits

Database-backed engine tests were attempted but could not execute because the native SQLite addon is unavailable for this Windows ARM64 / Node 24 setup. The engine transform suite passes; this is not a full engine-suite pass. GitHub build jobs remain queued.

No dependency versions changed and no production, deployment, or release operations performed.