---
title: "Sessions"
url: "/docs/harness/sessions"
canonical_url: "https://docs.litellm.ai/docs/harness/sessions"
type: "docs"
last_updated: "2026-10-01"
summary: "A session keeps the runtime's working directory and native history alive between turns. litellm.agent() creates a session for one turn and closes it afterwards. Use litellm.agent_session() when you need more than one turn."
related:
  - "/docs/harness/models"
  - "/docs/harness/events"
---
# Sessions

> Index of all LiteLLM docs: https://docs.litellm.ai/llms.txt


A session keeps the runtime's working directory and native history alive between turns. `litellm.agent()` creates a session for one turn and closes it afterwards. Use `litellm.agent_session()` when you need more than one turn.

## Multi-turn

```python
with litellm.agent_session(Harness.CODEX, sandbox=box, model="litellm_proxy/coder") as s:
    s.run("Install dev deps.")
    s.run("Run the router unit tests and summarize failures.")
    r = s.run("Fix the first failure. Keep the diff small.")
    print(s.cost)  # running total across all turns
```

Options you pass to `agent_session()` apply to every turn.

## Ending a session

| Method | Runtime | Sandbox | Resumable |
|---|---|---|---|
| `s.close()` | stopped | closed if the session created it | no |
| `s.detach()` returns `State` | parked | left running | yes |
| `s.stop()` returns `State` | stopped | closed if the session created it | yes |

Leaving a `with` block calls `close()` unless you already called `detach()` or `stop()`. If you passed in the sandbox, it belongs to you.

## Across processes

`State` holds the harness, the runtime's own session id, the working directory and the model. It never holds credentials. `state.dumps()` gives you bytes to store anywhere.

```python title="app.py"
from litellm import Harness
from litellm.harness import State

@app.post("/chat/{chat_id}")
async def chat(chat_id: str, msg: str):
    raw = await redis.get(f"harness:{chat_id}")
    box = sandbox.docker("my-agents:latest", name=f"chat-{chat_id}")

    if raw:
        s = await litellm.aagent_resume(State.loads(raw), sandbox=box)
    else:
        s = await litellm.aagent_session(Harness.CLAUDE_CODE, sandbox=box, model="litellm_proxy/coder")

    async with s:
        r = await s.arun(msg)
        await redis.set(f"harness:{chat_id}", (await s.adetach()).dumps())
    return {"text": r.text, "cost": r.cost}
```

`agent_resume()` raises `StateIncompatible` when the state came from a different harness or can't be read.

## History

```python
messages = s.history()  # OpenAI-format messages
```

Only Deep Agents supports history in this release. On the other harnesses `history()` raises `CapabilityUnsupported`.

## Related pages

- [Models and routing](https://docs.litellm.ai/docs/harness/models.md)
- [Events](https://docs.litellm.ai/docs/harness/events.md)
