---
title: "OpenAI Agents SDK sessions (2026): human-in-the-loop RunState serialize and resume, SQLAlchemySession, Redis session, compaction — Cadenya"
url: "https://cadenya.com/other-content/openai-agents-sdk-sessions"
description: "How OpenAI Agents SDK sessions work in 2026: human-in-the-loop approvals with RunState serialize and resume, SQLAlchemySession and Redis session persistence, SQLite for local work, OpenAIConversationsSession, OpenAIResponsesCompactionSession, TypeScript sessions, and what you host to run them, and Cadenya, which runs the loop with approvals and resume, so there's no RunState to store."
---

OpenAI Agents SDK sessions store the conversation history between runs: pass a session to the runner, and the SDK loads the history before each turn and saves the new items after it. For production, the docs point to `SQLAlchemySession` for a database you already run and `RedisSession` for shared memory across workers, and in both cases you host the database and the process that runs the agent.

So use `SQLiteSession` on your laptop, `SQLAlchemySession` when Postgres or MySQL is already in your stack, and `RedisSession` when several workers serve the same conversation. Pick `OpenAIConversationsSession` when you’d rather OpenAI store the history, and wrap any of them in `OpenAIResponsesCompactionSession` when conversations get long. And if you’d rather host none of it, Cadenya is a hosted agent loop with approvals and resume built in, with nothing to run, and a coding agent can set it up with no account in a temporary workspace you claim later. [What it offers](#what-does-cadenya-offer-that-openai-agents-sdk-sessions-dont) is at the end.

Every price and limit below comes from each vendor’s own pages, checked on October 13, 2026.

## OpenAI Agents SDK sessions side by side

|  | Where the history lives | What you run | Python install | In the TypeScript SDK |
| --- | --- | --- | --- | --- |
| SQLiteSession | A local file, or memory that’s “lost when process ends” | Nothing extra. Built for “Local development and simple apps” | Included in openai-agents | Write your own Session |
| SQLAlchemySession | Any SQLAlchemy database: PostgreSQL, MySQL, SQLite | Your database | openai-agents[sqlalchemy] plus a driver | Write your own Session (the samples include Prisma) |
| RedisSession | Redis | Your Redis server | openai-agents[redis] | Write your own Session |
| OpenAIConversationsSession | OpenAI’s Conversations API | Nothing extra, on OpenAI’s Responses API | Included | Built in |
| OpenAIResponsesCompactionSession | A wrapper around another session | Whatever the wrapped session needs | Included | Built in |
| Cadenya (a hosted loop in place of the SDK) | Each objective’s durable event log, on Cadenya | Nothing. The loop runs on Cadenya | Cadenya’s Python SDK, on Python 3.9 and later | Cadenya’s own TypeScript SDK, at 1.9.0 |

The Python SDK lists more backends: `AsyncSQLiteSession`, `MongoDBSession`, `DaprSession`, `AdvancedSQLiteSession` (branching and usage analytics), and `EncryptedSession`, a wrapper that encrypts another session. A community package, `openai-django-sessions`, stores sessions through the Django ORM.

Every one of them follows the same small protocol. In Python, a custom session implements “the four history methods”: `get_items`, `add_items`, `pop_item`, and `clear_session`. So moving from SQLite to Postgres changes one line, not your agent.

## SQLAlchemySession and Redis session persistence

The docs call `SQLAlchemySession` “production-ready” and say it lets you “use any database supported by SQLAlchemy (PostgreSQL, MySQL, SQLite, etc.) for session storage.” Install the extra and a driver:

```
pip install 'openai-agents[sqlalchemy]' aiosqlite
```

The extra already includes `asyncpg` for `postgresql+asyncpg://` URLs. MySQL needs `aiomysql` on top. Then hand the session a URL or an engine you already have:

```
from agents.extensions.memory import SQLAlchemySession

# Using database URL
session = SQLAlchemySession.from_url(
    "user_123", url="postgresql+asyncpg://user:pass@localhost/db", create_tables=True
)

# Using existing engine
from sqlalchemy.ext.asyncio import create_async_engine

engine = create_async_engine("postgresql+asyncpg://user:pass@localhost/db")
session = SQLAlchemySession("user_123", engine=engine, create_tables=True)
```

Either way, the database is yours to run, back up, migrate, and secure.

`RedisSession` is the pick “for shared session memory across multiple workers or services.” The docs describe it as “Good for low-latency distributed deployments”:

```
from agents import Agent, Runner
from agents.extensions.memory import RedisSession

agent = Agent(name="Assistant")
session = RedisSession.from_url(
    "user_123",
    url="redis://localhost:6379/0",
)
result = await Runner.run(agent, "Hello", session=session)
await session.close()
```

Mind the `close()`. When `from_url` creates the client, the session owns it, and after `close()` “the session is terminal and subsequent session operations raise `RuntimeError`.” If your app already manages a Redis client, pass it with `redis_client=...` and the session leaves it open.

**A session ID is a lookup key, not a login.** The docs say it plainly: “the session ID does not authenticate a user or authorize access to that history.” Your app checks that `user_123` is allowed to read `user_123`’s conversation before it builds the session.

## OpenAIConversationsSession: sessions stored by OpenAI

`OpenAIConversationsSession` keeps the history in OpenAI’s Conversations API, so there’s no table to create:

```
from agents import Agent, Runner, OpenAIConversationsSession

agent = Agent(name="Assistant", instructions="Reply very concisely.")

# Create a new conversation
session = OpenAIConversationsSession()

# Optionally resume a previous conversation by passing a conversation ID
# session = OpenAIConversationsSession(conversation_id="conv_123")

result = await Runner.run(agent, "What city is the Golden Gate Bridge in?", session=session)
```

You still store something: the `conversation_id`, mapped to your user. And the running agents guide draws the line on models: `conversation_id` and `previous_response_id` “are OpenAI-managed and only apply when you are using the OpenAI Responses API.” If you run a non-OpenAI model through LiteLLM or another adapter, the history goes in a client-side session instead.

Pick one approach per conversation. A session “cannot be combined with the run-level continuation options `conversation_id`, `previous_response_id`, or `auto_previous_response_id`” in the same run, and mixing them “can duplicate context.”

## OpenAIResponsesCompactionSession: session compaction

Long conversations fill the context window. `OpenAIResponsesCompactionSession` wraps another session and shrinks its history “with the Responses API (`responses.compact`)”:

```
from agents import Agent, Runner, SQLiteSession
from agents.memory import OpenAIResponsesCompactionSession

underlying = SQLiteSession("conversation_123")
session = OpenAIResponsesCompactionSession(
    session_id="conversation_123",
    underlying_session=underlying,
)

agent = Agent(name="Assistant")
result = await Runner.run(agent, "Hello", session=session)
```

Three details matter in production. First, compaction rewrites the history, so the SDK waits for it, and “`run.stream_events()` can stay open for a few seconds after the last output token if compaction is heavy.” The docs suggest you turn off the automatic trigger and call `run_compaction()` “between turns (or during idle time)” when streaming latency matters. Second, don’t wrap `OpenAIConversationsSession`: “those two features manage history in different ways.” Third, in TypeScript the default trigger “compacts once at least 10 non-user items have accumulated”, and you override `shouldTriggerCompaction` to count tokens instead.

For compaction across providers (Anthropic’s context management, the Responses API’s own setting, and frameworks like LangGraph), see [agent memory and context windows](/other-content/agent-memory-and-context-windows).

## OpenAI Agents SDK TypeScript sessions

The TypeScript SDK “ships with two implementations: `OpenAIConversationsSession` for the Conversations API and `MemorySession`, which is intended for local development.”

```
import { Agent, MemorySession, run } from '@openai/agents';

const agent = new Agent({
  name: 'TourGuide',
  instructions: 'Answer with compact travel facts.',
});

const session = new MemorySession();
const result = await run(agent, 'What city is the Golden Gate Bridge in?', {
  session,
});

console.log(result.finalOutput);
```

`MemorySession` “is reset when your process exits.” For anything that has to survive a deploy, you “Implement the `Session` interface to back memory with Redis, DynamoDB, SQLite, or another datastore. Only five asynchronous methods are required.” The repo’s `examples/memory/` folder has samples to start from (Prisma, file-backed, and more).

## OpenAI Agents SDK sessions with human-in-the-loop: RunState serialize and resume

The human-in-the-loop guide sums up the flow: “Tools declare when they need approval, run results surface pending approvals as interruptions, and RunState lets you serialize paused runs and resume them after decisions are made.” In Python, you “Convert the result to a RunState with result.to\_state(), call state.approve(…) or state.reject(…), and then resume with Runner.run(agent, state).” This is trimmed from the docs’ example:

```
state = result.to_state()
STATE_PATH.write_text(state.to_string())

# Load the state later (could be a different process).
stored = json.loads(STATE_PATH.read_text())
state = await RunState.from_json(agent, stored)
for interruption in result.interruptions:
    state.approve(interruption, always_approve=False)
result = await Runner.run(agent, state)
```

TypeScript has the same shape. “You can serialize the state using `result.state.toString()` (or `JSON.stringify(result.state)`) and resume later on by passing the serialized state into `RunState.fromString(agent, serializedState)`.” Mark the tool with `needsApproval`, read `result.interruptions`, and call `state.approve()` or `state.reject()` before you call `run(agent, state)` again.

Sessions and RunState meet at the resume. “If you are also using a session, keep passing the same session instance when you resume from RunState, or pass another session object configured for the same session ID and backing store.” That’s the part that makes a shared store matter: the worker that resumes the run may not be the one that paused it.

**The serialized RunState is yours to store and to trust.** The docs say `RunState.from_json()` and `RunState.from_string()` “do not authenticate the snapshot or the person submitting it.” They also suggest you “store a version marker for your agent definitions or SDK alongside the serialized state,” so a changed prompt or tool still finds a matching code path. The approval screen, notifications, and who may approve are on [human-in-the-loop approvals for AI agents](/other-content/human-in-the-loop-approvals-for-ai-agents).

## Hosting the OpenAI Agents SDK: what you run

The SDK is a library. OpenAI describes it as “a lightweight, easy-to-use package with very few abstractions,” installed with `pip install openai-agents` or from npm as `@openai/agents`, both under the MIT license. It runs inside your process, so hosting it means three things you own:

1.  **The process.** A web server or a pool of workers that calls `Runner.run` for each turn and streams the reply to your UI.
2.  **The session store.** In-memory SQLite and `MemorySession` vanish with the process, so a container that restarts or scales out needs `SQLAlchemySession`, `RedisSession`, or a custom backend. Then every worker can serve the next turn.
3.  **Durability, if a turn must survive a crash.** A session stores history between runs. For a run that has to survive retries or a process restart, OpenAI’s docs list four integrations for durable orchestration: Dapr, Temporal, Restate, and DBOS. Restate’s “requires Restate’s single-binary runtime as a dependency,” and DBOS’s “requires only a SQLite or Postgres database.”

Each of those engines is its own thing to run. [Durable execution for AI agents](/other-content/durable-execution-for-ai-agents) and [DBOS vs Temporal](/other-content/dbos-vs-temporal-for-ai-agents) compare them, and [self-hosting AI agent infrastructure](/other-content/self-hosting-ai-agent-infrastructure) adds up the whole stack.

One more boundary: if you build on ChatKit, the docs say sessions such as `SQLAlchemySession` “are not a drop-in replacement for ChatKit’s store.” That’s a second store to run next to the first.

## What does Cadenya offer that OpenAI Agents SDK sessions don’t?

OpenAI Agents SDK sessions give your loop a memory, and you host the loop, the store, and the workers around it. **Cadenya runs the loop, with approvals and resume built in, so there’s no worker to keep alive and no RunState to store.** Here’s what that covers:

1.  **Nothing else to run.** No worker pool, no durable engine, and no process to keep alive between turns. The loop runs on Cadenya and your app stays a stateless web tier, so a deploy or a restart never touches a running objective.
2.  **A durable event log for every objective.** “Every Objective carries a durable event log of its agent loop,” and you can list or stream its events. When an objective is waiting, send a Continue message and the loop resumes.
3.  **Conversations scoped to your users.** Each objective can carry a tenant and a subject from your own IDs, and you filter objectives by them. The widget derives the tenant and subject “from the Widget Session instead of trusting values sent by the browser.” One API call erases a tenant and every objective tied to it.
4.  **Edits that leave live conversations alone.** “The Objective keeps that variation’s configuration snapshot even if you edit the variation later.” So you can change a prompt, a tool, or a model while conversations are in flight, and each one keeps the configuration it started with.
5.  **SDKs that have shipped a lot.** TypeScript, Python, Ruby, and Go SDKs, all at 1.9.0 under Apache-2.0, and the TypeScript SDK has shipped 113 versions since April 2026. The Python SDK runs on Python 3.9 and later, plus there’s a CLI and a plain HTTP API.
6.  **A price in one line.** Free for 1,000 loops a month, $49 a month for 50,000, then $0.0015 a loop. A loop is one LLM request.
7.  **Compaction with no wrapper to configure.** Compaction is a setting on the variation, and you can also compact an objective on demand through the API.
8.  **Any model, on your own keys.** The model is a setting on the variation, picked from the AI providers you connect with your own keys. So switching providers is a change to a variation, with no adapter and no deploy.
9.  **Approvals in the same objective.** Mark a tool as needing approval and Cadenya parks the loop for an approval or a denial, with an optional memo that steers the agent. There’s no `RunState` to serialize and no snapshot to store.

[How objectives work](/docs/guides/the-basics/objectives) walks through a conversation from the first message to Continue, and the free plan needs no card.

No account yet? A coding agent can create a temporary workspace with `cadenya workspaces create --temporary`, run its first objectives there, and hand you a link to claim it, as [auth.md](https://cadenya.com/auth.md) explains. To create an account, sign up at [app.cadenya.com/signup](https://app.cadenya.com/signup?utm_source=cadenya.com&utm_medium=article&utm_campaign=openai-agents-sdk-sessions), then copy your API key from the API keys page at `app.cadenya.com/account/apiKeys`.