Shared State
Create a two-way connection between your UI and agent state.
What is shared state?#
Agentic Copilots maintain a shared state that seamlessly connects your UI with the agent's execution. This shared state system allows you to:
- Display the agent's current progress and intermediate results
- Update the agent's state through UI interactions
- React to state changes in real-time across your application
When should I use this?#
Use shared state when you want to facilitate collaboration between your agent and the user. Updates flow both ways — the agent's outputs are automatically reflected in the UI, and any inputs the user updates in the UI are automatically reflected in the agent's execution.
Reading agent state#
Subscribe a component to the agent's state with useAgent. Any time the agent
mutates its state — for example via a tool call — the hook fires and your UI
re-renders with the new values.
// Subscribe to agent state changes. The custom AGUI router for this // agent (see agent_server.py) emits a STATE_SNAPSHOT event after every // run, which fires this hook and re-renders the panels below. const { agent } = useAgent({ agentId: "shared-state-read-write", updates: [UseAgentUpdate.OnStateChanged], });The returned agent.state is just a plain object. Read it like any other
piece of React state and render the parts you care about — agent-written
notes, structured outputs, progress indicators, anything the agent has put
there.
Writing agent state#
The same agent object exposes a setState setter. Calling it from a UI
event handler pushes the new value into shared state, and the agent reads it
back on its next turn — so the UI's writes visibly steer the model.
// WRITE: every edit in the sidebar goes straight into agent state. // On the agent's next turn, the dynamic instructions function reads // this back out of session_state and adds it to the system prompt — // so the UI's writes visibly steer the model. const handlePreferencesChange = (next: Preferences) => { agent.setState({ preferences: next, notes, // preserve what the agent has written } as RWAgentState); };This is what makes the channel two-way: the UI doesn't just observe the agent, it can hand the agent fresh inputs (preferences, selections, partial work) without going through the chat thread.
Rendering shared state in the UI#
Because agent.state is plain React data, the UI layer is whatever you'd
normally build. The demo on this page wires the agent's outputs into a
small card component and feeds user edits back through setState.
// Read-side render: this card reflects the agent-authored `notes` slice// of shared state. The parent page passes `state.notes` in; we never// touch agent state ourselves — we just render it. The Clear button is// a small write-back, exposed as an `onClear` prop.export function NotesCard({ notes, onClear }: NotesCardProps) { return ( <div data-testid="notes-card" className="w-full max-w-md p-6 bg-white rounded-2xl shadow-sm border border-[#DBDBE5] space-y-4" > <div className="flex items-start justify-between gap-3"> <div> <h2 className="text-xl font-semibold text-[#010507]">Agent notes</h2> <p className="text-xs text-[#57575B] mt-1"> The agent writes here via its{" "} <code className="font-mono text-[11px] text-[#010507]"> set_notes </code>{" "} tool. The UI re-renders from shared state. </p> </div> {notes.length > 0 && ( <button type="button" onClick={onClear} data-testid="notes-clear-button" className="text-[10px] uppercase tracking-[0.14em] font-medium text-[#57575B] hover:text-[#FA5F67] border border-[#DBDBE5] hover:border-[#FA5F67] rounded-full px-2.5 py-1 transition-colors" > Clear </button> )} </div> {notes.length === 0 ? ( <div data-testid="notes-empty" className="text-sm text-[#838389] italic pt-1" > No notes yet. Ask the agent to remember something. </div> ) : ( <ul data-testid="notes-list" className="list-disc list-inside space-y-1 text-sm text-[#010507]" > {notes.map((note, i) => ( <li key={i} data-testid="note-item"> {note} </li> ))} </ul> )} </div> );}Streaming partial state updates#
By default, agent state only updates between node transitions, so a long-running tool call appears as one big burst at the end. State streaming forwards a specific tool argument straight into a state key as it's being generated, so the UI can watch the answer assemble token-by-token.
See State streaming for the full walkthrough,
including the corresponding useAgent subscription on the frontend.
Read-only context#
When the value is UI-owned and the agent should read it but never write
it back — current user, selected record, scroll position — reach for
useAgentContext instead of full shared state. It publishes values as a
one-way UI → agent channel that auto-unregisters on unmount.
See Agent read-only context for the full pattern.
