Optimistic UI for Agent Actions
Optimistic UI is a well-understood pattern: update the interface as if the action succeeded, reconcile when the server responds, roll back the rare failure. It works because form submissions succeed almost every time and fail in predictable ways.
Agent actions are different. They can partially succeed. They can succeed at something other than what was requested. They can take thirty seconds and then need approval. Applying naive optimism to them shows the user a done state that isn't true, and the correction, when it arrives, destroys trust.
Proposed and partial: the two states agents add
A normal action is pending or done. An agent action needs two more. Proposed: the agent has decided what to do and shown it, but has not done it yet. Partial: some steps ran and some didn't. The proposed state is where The Approval Gate lives.
type ActionState = | { kind: "proposed"; action: ProposedAction; approvable: boolean } | { kind: "executing"; action: ProposedAction; startedAt: number } | { kind: "done"; result: ActionResult; undoable: boolean; undoUntil?: number } | { kind: "failed"; action: ProposedAction; reason: string; retryable: boolean } | { kind: "partial"; completed: Step[]; remaining: Step[]; reason: string };The interface shows the proposed state clearly: "The agent is about to update three records. Here they are." The user can watch, intervene, or let it proceed. It's no slower, and it tells the user the truth about what the agent is doing.
Partial completion is a first-class state
A five-step action that fails at step three isn't "failed." It's partially done, and the UI has to say which parts. Showing a red error over the whole thing hides that two records were already changed. Showing a green checkmark hides that three were not.
Render the steps. Mark each as done, failed, or pending. Offer the two sensible next moves: retry the remaining steps, or undo the completed ones. Knowing which steps completed is what Durable Agents checkpoints for.
Undo is the key feature
For reversible actions, optimism is safe because undo exists. Show the done state immediately, keep the undo affordance visible for a defined window, and make undo one click.
For irreversible actions, there is no undo, so there is no optimism. Those go through the proposed state and an explicit confirmation. The interface should make the distinction visible: reversible actions feel light, irreversible ones feel deliberate.
Progress that means something
"Working..." with a spinner tells the user nothing for thirty seconds. Agents produce intermediate signal: which tool is being called, what step this is, what has been decided so far. Surface it. Users who can see the agent doing reasonable things ("Reading the ticket. Looking up the customer. Drafting a reply.") start to trust it.
Cancellation has to work
If a user can see progress, they will want to stop it. Cancel must propagate to the agent runtime, halt at the next safe point, and report what was completed before the halt. A cancel button that only hides the spinner while the agent keeps going is a lie the user will discover later. Never show a done state the system can't back up.
Working on something like this?
Start a Conversation