Agent SDK · Tools and sessions

Session.

Stateful multi-turn conversations with an auto-managed conversation ID and client-side transcript replay.

A Session wraps an Agent with auto-managed conversation_id and accumulates turn results. Agent runs send persist: false, so the server does not retain turn content — instead, the session accumulates each turn's exchange client-side and replays the transcript with every subsequent turn, giving the model full conversation context.

create_session(agent, **options)

Python
import asyncio

from matilda_agent_sdk import create_session


async def main():
    session = create_session(
        {"name": "tutor", "instructions": "You are a patient programming tutor. Explain concepts simply."}
    )

    print("Conversation ID:", session.conversation_id)

    # Turn 1
    r1 = await session.run("What is a closure in Python?")
    print("Turn 1:", r1.final_output[:100], "...")

    # Turn 2 — the session replays the accumulated transcript, so the model
    # sees the previous exchange
    r2 = await session.run("Can you show me a simple example?")
    print("Turn 2:", r2.final_output[:100], "...")

    # The session accumulates all turn results
    print("Total turns:", len(session.turns))
    print("Last turn stream ID:", session.last_turn.stream_id if session.last_turn else None)


asyncio.run(main())

Session class

session.run(prompt, **options)

Runs a turn and accumulates the result in session.turns. Options are the run() options (minus conversation_id); per-turn options merge with session defaults. Returns AgentRunResult.

session.stream(prompt, **options)

Streams a turn, yielding AgentEvent as they arrive. The result is accumulated in session.turns when the stream completes.

Python
async for event in session.stream("Explain decorators in one paragraph."):
    if event.type == "response.output_text.delta":
        print(event.delta, end="", flush=True)

print("\n[Turns accumulated]:", len(session.turns))
print("[Final output cached]:", (session.last_turn.final_output[:60] + "...") if session.last_turn else "")

session.conversation_id

The auto-generated (or provided) conversation ID. Reused across all turns.

session.turns

A list[AgentRunResult] — one per completed turn.

session.last_turn

The most recent AgentRunResult, or None if no turns have run.

Session options

Fieldtypedescription
runnerRunner | NoneCustom runner instance. Defaults to the shared default runner.
clientMatildaClient | NoneExplicit client (wrapped in a fresh Runner). Ignored when runner is set.
conversation_idstr | NoneExplicit conversation ID. Auto-generated when omitted.
purposeAgentPurpose | NoneFallback purpose when agent is a dict.
response_modestr | NoneSession-level response mode override.
metadatadict | NoneSession-level metadata merged into every turn. Defaults to {}.
tool_handlersToolHandlers | NoneSession-level tool handlers.
max_tool_roundtripsintSession-level roundtrip cap. Defaults to 25.

Metadata passthrough

Metadata can be set at multiple levels: Agent construction, Session construction, or per-call. Per-call metadata merges with (and overrides) session defaults.

Python
from matilda_agent_sdk import create_session


def tutor_instructions(ctx):
    env = ctx["metadata"].get("env", "unknown")
    user = ctx["metadata"].get("user", "anonymous")
    return f"Environment: {env}. User: {user}."


async def main():
    session = create_session(
        {"name": "helper", "instructions": tutor_instructions},
        metadata={"env": "staging", "user": "demo-user"},
    )

    # Session-level metadata is used by default
    await session.run("Who am I?")

    # Per-call metadata overrides session defaults
    await session.run("Who am I now?", metadata={"user": "admin"})
    # → env=staging (from session), user=admin (overridden per-call)

Custom runner

A Session can use a custom Runner for dependency injection in tests or isolated configuration:

Python
import asyncio

from matilda_agent_sdk import Runner, Session


async def main():
    my_runner = Runner()
    session = Session(
        {"name": "custom-runner-agent", "instructions": "Be brief."},
        runner=my_runner,
    )

    result = await session.run("What is 2 + 2?")


asyncio.run(main())