@@ -232,15 +232,26 @@ export const aiChat = chat
232232 turn,
233233 count : messages . length ,
234234 } ) ;
235- // Cast: `chatTools` has executes (output types are real), but
236- // `ChatUiMessage` is derived from the schema-only set in
237- // `chat-tools-schemas.ts` so its tools have `output: never`.
238- // `validateUIMessages` only reads `inputSchema` at runtime, so
239- // the type narrowing is safely sidestepped.
240- return validateUIMessages ( {
241- messages,
242- tools : chatTools as unknown as Parameters < typeof validateUIMessages > [ 0 ] [ "tools" ] ,
243- } ) ;
235+ // HITL continuations (`addToolOutput` / `addToolApproveResponse`)
236+ // ship a slim assistant on the wire — `state` + `output` /
237+ // `errorText` / `approval` only, no `input` or other parts.
238+ // `validateUIMessages` rejects that shape (the AI SDK schema
239+ // requires `input` on resolved tool parts), so filter to user
240+ // messages first. The agent's per-turn merge restores the
241+ // hydrated entry's `input` before `toModelMessages`.
242+ const userMessages = messages . filter ( ( m ) => m . role === "user" ) ;
243+ if ( userMessages . length > 0 ) {
244+ await validateUIMessages ( {
245+ messages : userMessages ,
246+ // Cast: `chatTools` has executes (output types are real), but
247+ // `ChatUiMessage` is derived from the schema-only set in
248+ // `chat-tools-schemas.ts` so its tools have `output: never`.
249+ // `validateUIMessages` only reads `inputSchema` at runtime, so
250+ // the type narrowing is safely sidestepped.
251+ tools : chatTools as unknown as Parameters < typeof validateUIMessages > [ 0 ] [ "tools" ] ,
252+ } ) ;
253+ }
254+ return messages ;
244255 } ,
245256 // #endregion
246257
0 commit comments