@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
ai6.0.0–6.0.230 every chunk schema is az.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 chunk | Before | After |
|---|---|---|
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-response | emitted on every approval decision | not 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
aiversions 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 ofAISDKUIEvent), andReplayToolCall.denialMessage. AISDKStartStepEvent: now{ type: 'start-step' }.AISDKFinishStepEvent: now{ type: 'finish-step' }.stepId,usageandfinishReasonare removed.AISDKFileEvent:filenameis replaced byproviderMetadata?: { helix: { filename: string } }.AISDKToolApprovalRequestEvent:isAutomaticremoved.AISDKToolOutputDeniedEvent:messageanddynamicremoved.AISDKAbortEvent:reasonremoved. The event is not emitted today.
If you read the removed data
- Per-step usage / finish reason: read it server-side.
- The core
step_endchunk on the Helix stream (StreamChunk) still carriesstepId,usageandfinishReason. - Run/session usage APIs and step hooks also have it.
- The core
stepIdon the client: it is still ondata-step-committed/data-step-discarded.- Approval decisions: in
aiv6 the decision is client-side part state (addToolApprovalResponse). The coretool_approval_responsechunk still carriesapproved/reasonon the Helix stream. - Custom servers emitting their own UI events: keep them to the shapes
ai6.0.0 declares. Put Helix extras ondata-*events or inproviderMetadata.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.