Run an agent in your process

A local session runs inside your Python program: the SDK runs the agent loop, your tools and the session's record in your process. A local session lives as long as your program: run it, continue it, and cancel it once you no longer need it. To keep it after the program exits, save it to a file (experimental).

To start, you need the SDK installed and MISTRAL_API_KEY set.

Run a session

Run a session

Build the agent and run it. model is the only required argument of agents.Agent(...): see Configuring the agent for every argument and its default. agent.run() answers one message; a session keeps the conversation across messages:

import asyncio

from mistralai import agents

agent = agents.Agent(
    model="mistral-large-4",
    name="trip-planner",
    instructions="Answer in one short sentence.",
)

async def main() -> None:
    state = await agent.run("Name one museum in Lisbon.")
    print(state.text)

    session = await agent.start_session()
    await session.run("My name is Ada.")
    state = await session.run("What is my name?")
    if isinstance(state, agents.Completed):
        print(state.text)
    await session.cancel()  # ends the session

if __name__ == "__main__":
    asyncio.run(main())
Output
The Calouste Gulbenkian Museum is one of Lisbon's most famous museums.
Your name is Ada.

There are two ways to run it:

  • As a script: uv run python main.py runs main(), which sends the agent its messages from code.
  • Interactively: uv run agents chat, run from the location of main.py, loads the same agent and lets you type each message in your terminal. It imports main.py, so the if __name__ == "__main__": line keeps main() from running on import.

agent.run() answers approvals only through an approval handler, passed as on_approval=. Without one, a tool call that needs an approval makes the run return agents.Blocked. The turn then waits for an answer on a session handle, up to the approval timeout, 1 hour by default. After that, the model gets a timeout result that tells it not to retry the call right away, and the turn goes on.

Continue a session

Continue a session

To continue the conversation, use a session. The session that agent.run() starts closes once its turn is done, and you can then only read it. To follow up on an agent.run(), run it inside an async with agents.local_runtime() block, and open its session by id there:

# Inside main() of the script above.
async with agents.local_runtime():
    state = await agent.run("Name three hotels in Lisbon, on one line.")
    print(state.text)
    session = agent.session(state.session_id)
    state = await session.run("Which of them is usually the cheapest?")
    if isinstance(state, agents.Completed):
        print(state.text)
    await session.cancel()
Output
Hotel Avenida Palace, Pestana Palace Lisbon, and Four Seasons Hotel Ritz Lisbon.
Pestana Palace Lisbon is usually the cheapest of the three, though rates vary by season.
Save a session

Save a session

By default, local sessions stay in memory, and end with your process. To keep them on disk, use the experimental JsonFileSessionJournal.

Ending turns and sessions

Ending turns and sessions

A local session ends when you cancel it, when its agent.run() turn is done, when a local_runtime() block exits, or when its event loop stops:

EventEffect
You stop waiting: timeout=, asyncio.wait_for, a cancelled taskThe turn keeps running until you stop it with turn.interrupt(), or end the whole session with session.cancel().
agent.run() returns a finished turnIts own turn has ended, and any background process it started has stopped. Its subagents keep running. Once they are done, its session closes, and you can still read it. Inside an async with agents.local_runtime() block, or with idempotency_key=, the session stays open until you cancel it.
await session.cancel() returnsThe session and its subagents have ended, and its sandbox is free.
The async with agents.local_runtime() block exitsEvery local session of the event loop ends. You can still read it until the event loop stops. A session saved with the experimental journal is only unloaded, and reopens.
The event loop stops, or the process exitsEvery local session stops, and is gone: no worker continues it.

Here the run returns after one second, with the turn still running, and session.cancel() stops it:

# Inside main() of the script above.
state = await agent.run("Write a 2,000-word story about a lighthouse.", timeout=1)
print(type(state).__name__)
await agent.session(state.session_id).cancel()  # stops the turn and ends the session
Output
Running

A command on a session that has ended raises agents.SessionTerminatedError:

Error
✕ SessionTerminatedError: The local Session has ended

Cancel each session once you no longer need it. A session still open when the event loop stops makes the SDK log the warning agents_sdk_local_runtime_closed_open_sessions, with the ids of the sessions.

A session that ended stays in memory until the event loop stops, so you can still read its turns. A server that runs many sessions on one event loop grows with each one.

Stop every local session

Stop every local session

Each event loop has one local runtime: the part of the SDK that runs the local sessions of that loop, and their subagents. agent.run(), agent.start_session() and agent.session(id) all use it. Two asyncio.run calls in one script have two runtimes, and do not share sessions.

agents.local_runtime() returns the event loop's local runtime. Getting it starts nothing. Use it as an async with block to stop all the local work of the event loop when the block ends:

# Inside main() of the script above.
async with agents.local_runtime():
    session = await agent.start_session()
    await session.start_turn("Plan a weekend in Lisbon.")
# Every local session of this event loop has stopped: running turns, subagents and sandboxes.

The block's exit also stops the sessions started before the block, and the turns and subagents that a run you stopped waiting for left running. In nested blocks, only the outermost block's exit stops the sessions.

When the block exits, every local session it ran ends, and the exit frees it. After that, a read or a command on one, such as turn.get() or session.send_message(...), raises agents.SessionTerminatedError, and session.cancel() returns. To continue, start a new session. A session saved with the experimental journal is only unloaded, and reopens.

Other settings

Other settings

Raise the program time budget

Raise the program time budget

The model can call your tools from a program: TypeScript code the model writes and runs with the run_typescript tool. Each program has a time budget of 60 seconds. The budget applies to the whole program, not to each tool call. A running tool call always finishes, but once the budget is used up, the program stops before its next step and the model gets a ProgramBudgetExceeded error. Time spent waiting for an approval or a hook does not count.

If a program that calls your tools can take longer than 60 seconds, raise the budget for every local session of the event loop:

# Wrap the body of main() in the block, before any other local session.
async with agents.local_runtime(program_timeout_seconds=300):
    await agent.run("Build the report")

The budget is fixed when the runtime starts, so open the block before the event loop's first local session and run your sessions inside it.

Common issues

Common issues

  • SessionTerminatedError: The local Session has ended. The session was cancelled, or an async with agents.local_runtime() block it ran in has exited. Start a new session.
  • SdkError: This event loop's local runtime already runs with .... A local session started the runtime before the block. Open the block first, at the top of main().
  • The warning agents_sdk_local_runtime_closed_open_sessions. Sessions were still open when the event loop stopped. Call await session.cancel() on each one once you no longer need it.
Next steps

Next steps