State¶
A run can carry a state: one JSON document (usually a dict) that tools read and change, and that your app sees change as it happens. Think of a todo list, a shopping cart or a document the agent and the user edit together.
from pyox_agents import Agent, RunContext, StateChanged, tool
@tool
def add_todo(ctx: RunContext, text: str) -> str:
"""Add a todo to the list."""
ctx.state["todos"].append(text)
return f"Added {text!r}"
agent = Agent(model="gpt-5-mini", tools=[add_todo])
async for event in agent.stream("Plan my trip", state={"todos": []}):
if isinstance(event, StateChanged):
print(event.delta) # [{"op": "add", "path": "/todos/0", "value": "Book the flight"}]
- Give it with
stream(state=...)(orrun(state=...)). Anything JSON works. - Inside a tool,
ctx.statereads it. Change it in place, or replace it:ctx.state = {...}. - When the tool finishes, its changes are applied and sent as a
StateChangedevent, with the whole newstateand adelta: what changed, as a JSON Patch. RunFinished.stateis the state at the end of the run.
Nothing is stored: your app keeps the state between runs and gives it again. Your own dict is never changed.
Rules¶
- Changes apply when a tool finishes. A tool that fails, or stops to wait for a person, changes nothing: it runs again from its start once answered, so it would make its changes twice otherwise.
- Tools running at the same time keep each other's changes. Each tool applies only what it changed, so two tools changing different keys never overwrite each other.
- Hooks read a copy. Only tools change the state.
- The model does not see the state unless a tool returns it (or a hook adds it to the prompt).
Subagents¶
A subagent sees the state only if its tool allows it: as_tool(..., state="read") or
state="write". Without it, ctx.state raises PermissionError in the subagent's tools, and a read-only
subagent's tool that changes the state gets an error result instead.
Web frontends¶
AG-UI clients (CopilotKit) and AI SDK frontends share the state with the agent: ag_ui.stream and
ai_sdk.stream start the run with the client's state, and send each change as it happens (a STATE_DELTA
event, or a data-state part). See web frontends.