---
name: agentbyte-multi-turn-context
description: Guide for managing Agentbyte multi-turn conversations using AgentContext and session stores. Use this when implementing stateful chats, persistent sessions, session switching, or threading history across agents, orchestrators, and workflow steps.
---

Use this skill when building conversational agents that need to remember previous turns, or when sessions must persist across process restarts.

## Core Concept

`AgentContext` IS the session — it carries `messages`, `session_id`, `metadata`, and
`shared_state`. There is no separate `Session` domain model. The caller owns the context
and passes it in; the agent or orchestrator reads and writes it in place.

## Pattern: Basic Multi-Turn Agent

```python
from agentbyte.context import AgentContext
from agentbyte.agents import Agent

session_context = AgentContext(session_id="user-123")
agent = Agent(name="chat-bot", ...)

# First turn
response1 = await agent.run("Hi, I am Alex", context=session_context)

# Second turn — re-use the same context; history is already updated
response2 = await agent.run("What is my name?", context=session_context)
print(response2.final_content)  # "Your name is Alex"
```

## Pattern: Persistent Sessions with Session Stores

Session stores persist `AgentContext` across process restarts. Three built-in options:

| Store | Backend | Use when |
|-------|---------|----------|
| `InMemorySessionStore` | Python dict (volatile) | Tests, single-process demos |
| `FileSessionStore` | One JSON file per session | Simple persistence, low concurrency |
| `CachedFileSessionStore` | In-memory cache + file | Production: fast reads, durable writes |

```python
from agentbyte.context import AgentContext
from agentbyte.session_store import CachedFileSessionStore

store = CachedFileSessionStore(".sessions")
session_id = "user-123"

# Load or create
ctx = await store.get(session_id) or AgentContext(session_id=session_id)

response = await agent.run("Remember: my budget is $500", context=ctx)

# Persist after each turn
await store.save(session_id, ctx)
```

All three stores share the same `SessionStore` interface:
```python
await store.get(session_id)        # AgentContext | None
await store.save(session_id, ctx)  # None
await store.list()                 # Iterable[tuple[str, AgentContext]]
await store.delete(session_id)     # bool
await store.clear_all()            # int (count deleted)
```

Import from any of these paths:
```python
from agentbyte import CachedFileSessionStore       # package root
from agentbyte.session_store import FileSessionStore  # module
from agentbyte.webui import InMemorySessionStore   # webui re-export
```

## Pattern: Orchestrator Sessions

`BaseOrchestrator.run()` and `run_stream()` accept an optional `context` parameter.
The orchestrator seeds its `shared_messages` from `context.messages` at the start of
each run, then writes the full conversation back before returning.

```python
from agentbyte.context import AgentContext
from agentbyte.session_store import CachedFileSessionStore
from agentbyte.presets.orchestration import get_orchestrator

store = CachedFileSessionStore(".sessions")
session_id = "orch-session-1"
orchestrator = get_orchestrator("round_robin", agents=[agent_a, agent_b])

# Turn 1
ctx = await store.get(session_id) or AgentContext(session_id=session_id)
response = await orchestrator.run(task="Draft an intro paragraph.", context=ctx)
await store.save(session_id, ctx)

# Turn 2 — prior conversation is loaded from the store
ctx = await store.get(session_id)
response = await orchestrator.run(task="Now make it more concise.", context=ctx)
await store.save(session_id, ctx)
```

## Pattern: Workflow Step Sessions (AgentbyteAgentStep)

Workflows are pipelines — they do not thread conversation history automatically.
Pass `AgentContext` via `agent_context` on `AgentbyteAgentInput` when a step agent
needs session continuity.

```python
from agentbyte.context import AgentContext
from agentbyte.session_store import CachedFileSessionStore
from agentbyte.workflow.steps import AgentbyteAgentInput

store = CachedFileSessionStore(".sessions")
ctx = await store.get("step-session") or AgentContext(session_id="step-session")

execution = await runner.run(
    workflow,
    AgentbyteAgentInput(task="Summarise the report", agent_context=ctx),
)
await store.save("step-session", ctx)
```

`agent_context` is always optional — omit it for a stateless step.

## Key Attributes

- **`context.messages`**: Conversation history (list of `Message`).
- **`context.session_id`**: Identifier set at creation; used as store key.
- **`context.metadata`**: Dict of custom data carried across turns.
- **`context.shared_state`**: Cross-agent shared state (orchestrators).

## Guardrails

- Never store conversation history on the `Agent` object — always on `AgentContext`.
- The caller is responsible for persisting the context (call `store.save()` after each turn).
- Do not pass the same `AgentContext` to two concurrent agent runs — it is not thread-safe.
- For workflow pipelines, use `AgentbyteAgentInput.agent_context`; do not embed `AgentContext` in the workflow `Context` dict.
- Add `.sessions/` to `.gitignore` to avoid committing session JSON files.
