Overview
Custom actions let the frontend send structured commands (undo, rollback, edit, regenerate) that modify the conversation state. Actions are not turns: they firehydrateMessages (if set) and onAction only. No turn lifecycle hooks (onTurnStart / prepareMessages / onBeforeTurnComplete / onTurnComplete), no run(), no turn-counter increment. The trace span is named chat action.
Actions wake the agent from suspension the same way a new message does, run their handler against the latest accumulator state, and emit a trigger:turn-complete chunk so the frontend’s useChat knows the action has been applied.
Defining an action handler
Define anactionSchema for validation and an onAction handler that uses chat.history to modify state:
actionSchema → hydrateMessages (if set) → onAction → apply chat.history mutations → emit trigger:turn-complete → wait for next message.
Returning a model response from an action
onAction can return a StreamTextResult, string, or UIMessage to produce a response. The returned stream is auto-piped to the frontend just like a normal turn, but the rest of the turn machinery (onTurnStart, onTurnComplete, etc.) still does not fire.
onAction itself; you have access to the streamed response object.
Gating actions on HITL state
If you have a human-in-the-loop tool waiting onaddToolOutput, you usually want to refuse competing actions like regenerate until the answer arrives. chat.history.getPendingToolCalls() gives you exactly that signal:
Sending actions from the frontend
actionSchema on the backend; invalid actions throw and surface as a stream error. The action parameter in onAction is fully typed from the schema.
For silent state changes that should never appear as a turn (e.g. injecting background context), use
chat.inject() instead. Actions are explicit user-driven mutations; injections are agent-side context updates.See also
chat.history— the imperative API actions use to mutate state- Sending actions from the frontend —
transport.sendActionergonomics hydrateMessages— fires beforeonActionwhen set- Branching conversations — pairs action handlers with backend-controlled history
- Human-in-the-loop — gating fresh actions while a tool is waiting

