Skip to content

@helix-agents/ai-sdk: UI chunks match ai's schema (DI-20) ​

This note covers the @helix-agents/ai-sdk minor release that makes every UI chunk it emits parse with ai's own uiMessageChunkSchema. Stock useChat / Chat apps need no code change. The change only affects code that reads the removed fields from raw UI chunks, or that uses the removed/changed AISDK*Event types.

Why ​

A stock ai client (DefaultChatTransport) validates every SSE chunk with uiMessageChunkSchema and fails the whole response on the first one that doesn't parse: the chat ends in status: 'error' ("Type validation failed"), and the assistant message is left empty or partial.

  • In ai 6.0.0–6.0.230 every chunk schema is a z.strictObject, so one extra key is fatal.
  • 6.0.231+ tolerates extra keys, but still rejects unknown chunk types.

With transformerOptions.includeStepEvents: true and a step-reporting LLM adapter, Helix emitted:

  • start-step { stepId };
  • finish-step { stepId, usage, finishReason }.

ai's schema declares both as bare { type }, so every such response failed on strict versions. Step-reporting adapters include MockLLMAdapter and custom adapters; @helix-agents/llm-vercel does not report steps today (RM-48).

What changed on the wire ​

UI chunkBeforeAfter
start-step{ type, stepId }{ type }
finish-step{ type, stepId, usage, finishReason }{ type }
file{ type, url, mediaType, filename }{ type, url, mediaType, providerMetadata?: { helix: { filename } } }
tool-approval-request{ type, approvalId, toolCallId, isAutomatic? }{ type, approvalId, toolCallId }
tool-approval-responseemitted on every approval decisionnot emitted; a denial still emits tool-output-available with the deny string
replayed tool-output-denied{ type, toolCallId, message, dynamic }{ type, toolCallId }

None of the removed fields ever reached a stock client:

  • strict ai versions crashed on them;
  • loose versions dropped them.

For file.filename, clients from ai 6.0.230 on copy providerMetadata onto the file part (part.providerMetadata.helix.filename). Older clients keep only url + mediaType.

Public type changes ​

  • Removed: AISDKToolApprovalResponseEvent (and its member of AISDKUIEvent), and ReplayToolCall.denialMessage.
  • AISDKStartStepEvent: now { type: 'start-step' }.
  • AISDKFinishStepEvent: now { type: 'finish-step' }. stepId, usage and finishReason are removed.
  • AISDKFileEvent: filename is replaced by providerMetadata?: { helix: { filename: string } }.
  • AISDKToolApprovalRequestEvent: isAutomatic removed.
  • AISDKToolOutputDeniedEvent: message and dynamic removed.
  • AISDKAbortEvent: reason removed. The event is not emitted today.

If you read the removed data ​

  • Per-step usage / finish reason: read it server-side.
    • The core step_end chunk on the Helix stream (StreamChunk) still carries stepId, usage and finishReason.
    • Run/session usage APIs and step hooks also have it.
  • stepId on the client: it is still on data-step-committed / data-step-discarded.
  • Approval decisions: in ai v6 the decision is client-side part state (addToolApprovalResponse). The core tool_approval_response chunk still carries approved / reason on the Helix stream.
  • Custom servers emitting their own UI events: keep them to the shapes ai 6.0.0 declares. Put Helix extras on data-* events or in providerMetadata.helix. See the UI-chunk wire contract.

Deploy notes ​

The client and server can be deployed in either order: the new chunks are a strict subset of the old ones.

Released under the MIT License.