- MCP Server ToolsUpdated
- Managed MCP with chat()Updated
- Manual MCPUpdated
- MCP Type GenerationUpdated
- MCP Client InputUpdated
- MCP Server Tools: AdvancedNew
- MCP EventsNew
- MCP AppsUpdated
- Serve Tools over HTTPUpdated
- Serve Resources and PromptsUpdated
- Ask for InputUpdated
- Draft Text from a ToolUpdated
- MCP Server TasksUpdated
- MCP Server AuthUpdated
- MCP Server SessionsUpdated
- MCP Server on stdioUpdated
Persistence (Advanced)
- GenerationsUpdated
- Realtime Voice ChatUpdated
- Text-to-SpeechUpdated
- TranscriptionUpdated
- Transcription: AdvancedNew
- Voice CreationUpdated
- Audio RecordingUpdated
- Audio GenerationUpdated
- Image GenerationUpdated
- Image Generation: AdvancedNew
- Video GenerationUpdated
- Video Generation: AdvancedNew
- Live GenerationUpdated
- World GenerationUpdated
- Generation HooksUpdated
- OverviewUpdated
- Quick StartUpdated
- ProvidersUpdated
- HarnessesUpdated
- Harness AuthUpdated
- WorkspaceUpdated
- ToolsUpdated
- PolicyUpdated
- Lifecycle & SnapshotsUpdated
- Portable SnapshotsUpdated
- Keep Files After ReloadUpdated
- Save a Named VersionUpdated
- Branch From a VersionUpdated
- Send a Frozen FileUpdated
- Let the Agent Save and ForkUpdated
- Pick Which Files to KeepUpdated
- What a Snapshot StoresUpdated
- Instance DurabilityUpdated
- Durable RunsUpdated
- EventsUpdated
Sandboxes (Advanced)
- Runtime ContextUpdated
- Bring Your Own KeyUpdated
- Debug LoggingUpdated
- Multimodal ContentUpdated
- Files APIUpdated
- Per-Model Type SafetyUpdated
- Runtime Adapter SwitchingUpdated
- Tree-ShakingUpdated
- Extend AdapterUpdated
- Extend Adapter: AdvancedNew
- Typed Pre-Configured OptionsUpdated
- Test with a Fake ModelUpdated
- Prompt CachingUpdated
- Mid-Conversation ChangesUpdated
- Approval Flow ProcessingUpdated
Migration
In 1.0, tool approval, a generic pause, and client-tool execution are AG-UI interrupts. Resolve each one on the item. addToolApprovalResponse and pendingInterrupts are removed.
There is no codemod. Upgrade the server and the client together. For the full list of 1.0 changes, see Upgrade to 1.0.
Quick migration#
- Rename pendingInterrupts to interrupts.
- Replace each addToolApprovalResponse call with interrupt.resolveInterrupt(approved) on the matching item.
- Replace approval loops with resolveInterrupts(...).
- On the server, forward parentRunId and resume into chat().
- Finish or drop the runs that paused on 0.x. 1.0 cannot resume them.
The sections below show each step.
API mapping#
| 0.x (removed in 1.0) | 1.0 |
|---|---|
| pendingInterrupts | interrupts |
| ChatClient.getPendingInterrupts() | ChatClient.getInterrupts() |
| addToolApprovalResponse({ id, approved }) | Find the bound tool-approval item and call interrupt.resolveInterrupt(approved). Removed from every framework and from ChatClient. |
| Raw resumeInterrupts(entries, state) | Item methods, or root resolveInterrupts(...). Keep resumeInterruptsUnsafe for validated recovery tools. |
| interrupt.error | interrupt.errors[0] |
| approval-requested custom event | RUN_FINISHED interrupt descriptor, reason tool_call |
| tool-input-available custom event | RUN_FINISHED interrupt descriptor, reason tanstack:client_tool_execution |
| Boolean denial treated as cancellation | resolveInterrupt(false) for a denial. cancel() for a cancellation with no payload. |
These stay the same:
- addToolResult still sends client-tool results. It hands them to the matching native item.
- needsApproval still turns on approval in the tool definition.
Single approval#
// 0.x
await addToolApprovalResponse({ id: approval.id, approved: true })
// 1.0
const interrupt = interrupts.find(
(item) => item.kind === 'tool-approval' && item.toolName === 'transfer',
)
if (interrupt?.kind === 'tool-approval' && interrupt.toolName === 'transfer') {
interrupt.resolveInterrupt(true)
}A single valid item submits at once. For the full component, see Tool Approval.
Payloads and edits#
In 0.x, a boolean approval had no typed data. In 1.0, add an approvalSchema to the tool, and send data under payload:
interrupt.resolveInterrupt(true, {
editedArgs: { amount: 12, recipient: 'Ada' }, // optional, approval only, full replacement
payload: { note: 'Reviewed' },
})
interrupt.resolveInterrupt(false, { payload: { reason: 'Policy limit' } })- Rejection never accepts edits.
- Custom fields at the top level are not valid.
- A single approvalSchema (not an { approve, reject } map) applies to the selected decision.
- With no schema, resolveInterrupt(true) and resolveInterrupt(false) stay valid.
Denial and cancellation#
- resolveInterrupt(false, ...) continues the model with an explicit rejected decision.
- cancel() emits AG-UI status: 'cancelled'. It never validates or selects the reject branch.
If you called addToolApprovalResponse({ approved: false }), use resolveInterrupt(false). It is a denial, not a cancellation.
Batches#
Native batches are all or nothing. Replace loops over approval ids with one of these:
- Stage each item. The last valid item submits the batch.
- Answer every item from one synchronous root callback:
resolveInterrupts((interrupt) => {
if (interrupt.kind === 'tool-approval') {
interrupt.resolveInterrupt(true, { payload: { note: 'Batch review' } })
return
}
interrupt.cancel()
})Other root helpers:
- resolveInterrupts(true) or resolveInterrupts(false): only for batches of approvals with no payload and no edits.
- cancelInterrupts(): cancel every item, with no payload.
- clearResolution(): drop the draft of one item.
- retryInterrupts(): only when every item is still validly staged and the root error is retryable.
See Multiple Interrupts.
Generic responses#
Do not derive a static type from a responseSchema that you received. Parse the value as unknown, convert the schema with z.fromJSONSchema, and then resolve with the validated value. For typed generic interrupts, use defineInterrupt. See Generic Interrupts.
Server events#
A native server emits these events, in this order:
- MESSAGES_SNAPSHOT
- STATE_SNAPSHOT (optional)
- RUN_FINISHED with a nonempty interrupt outcome
A continuation has:
- a new runId
- the same threadId
- the interrupted run as parentRunId
- every pending id, exactly once
Interrupts are ephemeral. The server rebuilds and validates the expected batch from the submitted history and its current tool definitions. So a stateless route needs no persistence. But the batch comes from client input, so this mode does not give authoritative recovery, exactly-once delivery, replay protection, or restart recovery.
resumeInterruptsUnsafe is a low-level escape hatch. It sends validated raw resume entries directly. Do not use it for approval UI.
Old streams#
1.0 does not read the approval-requested and tool-input-available custom events. An interrupt descriptor without a binding (only metadata.kind) shows as kind: 'unbound', and you cannot resume it. Upgrade the server and the client together, and finish or drop the runs that paused on 0.x.
Before you ship#
The quick migration covers the client. Also do these:
- If your server wrote the 0.x custom events itself, replace those writers with the interrupt terminal.
- Test expired items and a failed transport.
When this is done, your approvals run on 1.0 interrupts. Start from the Overview for the current flow.