# OpenAI Agents SDK Session Storage and Memory | DialogueDB

> Managed session storage and long-term memory for OpenAI Agents SDK apps. Runs where MemorySession stops. Multi-user isolation and semantic search included.

Canonical: https://dialoguedb.com/integrations/openai-agents-sdk

OpenAI Agents SDK Integration

# Managed session storage and memory for the OpenAI Agents SDK

DialogueDB is the session backend for OpenAI Agents SDK apps in production. One integration replaces the storage layer, memory service, user isolation logic, and vector search you would otherwise assemble yourself.

[Get Your Free API Key](https://app.dialoguedb.com/signup) [See the Working Example](https://github.com/dialoguedb/examples/tree/main/openai-agents-sdk)

Your agent app

OpenAI Agents SDK

run()

Agent

Tools

Handoffs

a few lines around run()

Session storage layer

DialogueDB

Messages

persist across deploys

Memory

carries between sessions

Namespaces

isolate every user

Search

by meaning

Where MemorySession stops

## MemorySession works until
the first restart

The default Session backend keeps the transcript inside the process that created it.
Here is exactly where that design starts pushing back.

MemorySession's design

- In-process memory, gone when the process exits
- One session ID per conversation, no user model
- Message history only, no memory abstraction
- Retrieval is by ID, no semantic query

Where it hits the wall

- Serverless and multi-instance deploys each get their own copy, never shared
- No namespace or tenant model to isolate one user from another
- Nothing to carry facts about a user across sessions
- Recalling "what did we discuss about X" needs a separate search stack

What replaces it, and what it adds

## DialogueDB is the session backend when MemorySession runs out

And it delivers three things MemorySession was never trying to do.

### Session storage that survives production

A managed database backing every run, not an in-process array. Every message survives redeploys, cold starts, and multi-instance workers.

### Namespaces for multi-user agent runs

Pass a namespace on every save and every read. No cross-user leakage, no WHERE clauses in your agent code.

### Memory that carries across sessions and handoffs

Structured Memory objects live independently of message history. Agents in the same namespace share the same memory during handoffs.

### Semantic search over every past run

Query past messages, memories, and tool calls by meaning. No vector database to run, no embeddings pipeline to build.

The integration pattern

## Five steps around the run. No adapter, no protocol implementation.

Load history, optionally search memory, run the agent as normal, save the result. A few calls around `run()`, and nothing about your agent code has to change.

1 Load past messages db.getOrCreateDialogue({ id, namespace }) and loadMessages() rehydrate dialogue.messages, creating the dialogue on the first turn. 2 Search memory (optional) db.searchMemories(userInput, { namespace }) surfaces relevant facts about this specific user. 3 Inject into agent instructions Add the matched memories to the agent's instructions or context on this turn. 4 Run the agent run(agent, input) executes as normal. Nothing about your agent code changes. 5 Persist the new turn dialogue.saveMessages writes the items this turn added, mapped from result.history, with tool calls preserved.

The pattern agent-run.ts

```
// 1. Load past messages, rebuild the run items
const dialogue = await db.getOrCreateDialogue({ id, namespace })
await dialogue.loadMessages({ order: "asc" })
const history = fromStoredMessages(dialogue.messages)

// 2. Optionally recall relevant memory
const { results } = await db.searchMemories(userInput, { namespace, limit: 3 })
const memories = results.map((r) => String(r.item.value))

// 3. Inject into instructions
const agent = buildAgent(memories)

// 4. Run the agent, seeded with that history
const result = await run(agent, [
  ...history,
  { type: "message", role: "user", content: userInput },
])

// 5. Persist what this turn added
await dialogue.saveMessages(
  toStoredMessages(result.history.slice(history.length))
)
```

DialogueDB does not implement OpenAI's `Session` protocol as a drop-in class. The pattern above is the whole integration.

## What this unlocks in your agent app

Six use cases the integration makes shorter to build.

### Support agents that carry ticket history

Every past conversation about a customer is a load call away. The agent picks up mid-thread without you re-hydrating context.

`db.getOrCreateDialogue()`

### Onboarding assistants that build a user profile

As the assistant learns preferences and answers, save them as Memory objects. The next session starts with a real profile, not a blank slate.

`db.createMemory()`

### Multi-agent teams with clean handoffs

Two or more agents pointing at the same namespace share the same message history and memory. The handoff carries context by default.

`namespace: shared`

### Long-running research agents

Agents that spend hours or days on a task can crash, redeploy, and pick up exactly where they stopped. No lost work.

`dialogue.messages`

### Personal assistants with real memory

Recall what the user said last week or last month by meaning, not by keyword. Answer as if the assistant had been listening the whole time.

`db.searchMessages()`

### Multi-tenant SaaS with agent isolation

One codebase, many customers, each with their own namespace. No cross-tenant leakage, no WHERE clauses in agent code.

`namespace: userId`

## Install in 3 steps

From npm install to the first persisted run, in under five minutes.

1

### Install the client

```
npm install dialogue-db
```

2

### Set the API key

```
DIALOGUE_DB_API_KEY=...
```

3

### Save after the run

```
// result, history, dialogue, and your
// toStoredMessages mapper from the
// pattern above
const added = result.history
  .slice(history.length)
await dialogue.saveMessages(
  toStoredMessages(added)
)
```

[Get Your Free API Key](https://app.dialoguedb.com/signup)

No credit card. Free tier to start. Starter is $29/month when usage grows.

## The reference example

A TypeScript sample repo that persists an agent conversation to DialogueDB and reloads it on the next turn, with tool calls intact. Clone it, plug in your API key, and see it work locally.

What `turn()` does, in the order it runs

1

### Loads the conversation from scratch

db.getOrCreateDialogue and loadMessages fetch every prior turn back out of DialogueDB. A fresh process after a redeploy or a cold start reads the same history.

2

### Recalls memory for this user

db.searchMemories returns an envelope; the values in results\[\].item.value go into the agent this turn.

3-4

### Runs the agent, seeded with that history

run() is handed the rebuilt items plus the new user input, so the agent picks up exactly where it left off.

5

### Persists only what this turn added

saveMessages writes the items result.history gained this turn onto the same dialogue, tool calls and tool results included.

[Clone the reference example](https://github.com/dialoguedb/examples/tree/main/openai-agents-sdk)

Reference code src/index.ts

```
import { run } from "@openai/agents"
import { DialogueDB } from "dialogue-db"
import { buildAgent } from "./agent"
// agent.ts builds the agent with model: "gpt-4o" and two tools
import { fromStoredMessages, toStoredMessages } from "./persist"

const db = new DialogueDB({ apiKey: process.env.DIALOGUE_DB_API_KEY! })
const namespace = "user_123"  // namespace is the user id

async function turn(id: string, userInput: string) {
  // 1. Load prior history and rebuild the run items
  const dialogue = await db.getOrCreateDialogue({ id, namespace })
  await dialogue.loadMessages({ order: "asc" })
  const history = fromStoredMessages(dialogue.messages)

  // 2. Recall memory the caller chose to store
  const { results } = await db.searchMemories(userInput, { namespace, limit: 3 })
  const recalled = results.map((r) => String(r.item.value))

  // 3-4. Run the agent, seeded with that history
  const result = await run(buildAgent(recalled), [
    ...history,
    { type: "message", role: "user", content: userInput },
  ])

  // 5. Persist only what this turn added
  await dialogue.saveMessages(
    toStoredMessages(result.history.slice(history.length)),
  )

  return result.finalOutput
}
```

The mapper, both directions src/persist.ts

```
import { protocol, type AgentInputItem } from "@openai/agents"

export const toStoredMessages = (items: AgentInputItem[]) =>
  items.map((item) => ({
    role: "role" in item ? item.role : "assistant",  // storage label only; replay never reads it
    content: item,  // stored verbatim, so tool calls survive
  }))

export const fromStoredMessages = (rows: readonly { content: unknown }[]) =>
  rows.map((row) => protocol.ModelItem.parse(row.content))
```

Uses the OpenAI Agents SDK. Loads prior history, seeds the run with it, and persists the items the run added, with tool calls preserved.

## Where DialogueDB fits

Session and memory options for OpenAI Agents SDK apps, side by side.

| |  MemorySession SDK default     | Persistent sessions OpenAIConversationsSession, or bring your own | Standalone memory Mem0, Zep | DialogueDB          |                          |
| -------------------------------- | ----------------------------------------------------------------- | --------------------------- | ------------------- | ------------------------ |
| Persists messages in production  | No, in-process only                                               | Yes                         | Facts only          | Yes, every message       |
| Multi-user isolation             | By session ID only                                                | By session ID               | By user ID          | Namespaces, first-class  |
| Long-term memory across sessions | No                                                                | No                          | Yes                 | Yes, Memory objects      |
| Semantic search over past runs   | No                                                                | Build it yourself           | Facts only          | Every message and memory |
| Same store for messages + memory | N/A                                                               | Messages only               | Two services to run | Yes, one API             |

## If you know the OpenAI Agents SDK, you already know DialogueDB

The concepts map one-to-one to what the Agents SDK already exposes.

| OpenAI Agents SDK               | DialogueDB                 |
| ------------------------------- | -------------------------- |
| Session items / RunResult items | dialogue                   |
| Message item                    | message                    |
| Agent user (from your auth)     | namespace                  |
| Tool call                       | message with tool metadata |
| Handoff between agents          | shared namespace           |
| User context to remember        | memory                     |

DialogueDB does not ship as a drop-in `Session` class. The pattern is `dialogue.saveMessages()` after each `run()`, then the mapped items back in on the next run.

What DialogueDB sees

## Two independent calls, two different payloads

Model provider calls run directly from your server to whichever LLM you have configured. DialogueDB sits alongside that request path, not inside it. Only what you explicitly hand it ever reaches it.

The model call run() → LLM

Fires from inside `run()`. Goes straight to OpenAI, Anthropic, xAI, or whatever provider your agent uses.

What the model provider gets

- Your model provider API key
- System prompt and tool definitions
- Full conversation input for this turn

DialogueDB never sees any of this

The save call saveMessages → DialogueDB

Fires after `run()` resolves. Sends only what you explicitly pass to the SDK.

What DialogueDB gets

- Message content, role, and tool metadata
- Dialogue ID and the namespace scoping this user
- Memory values and labels (when you call createMemory)

Never: API keys, user auth tokens, model prompts, unrelated app data

## Frequently asked questions

Does DialogueDB work with run() and streaming runs?

Yes, DialogueDB works with both a normal run and a streaming run in the OpenAI Agents SDK. Streaming is run(agent, input, { stream: true }). After the run finishes, map the items it added from result.history and call dialogue.saveMessages to persist them. Streaming works identically because you persist once the run resolves.

Does DialogueDB implement OpenAI's Session protocol as a drop-in class?

No, DialogueDB does not implement OpenAI's Session protocol as a drop-in class today. The integration pattern is explicit: load messages before the run, call run normally, and persist the items it added after. It is a few lines of wiring, not a magic adapter.

How do handoffs between agents share memory in DialogueDB?

Handoffs between agents share memory in DialogueDB through a shared namespace. When two agents write to and read from the same namespace, they see the same dialogue history and can search the same Memory objects. There is no first-class handoff API in DialogueDB; the shared context comes from the namespace model itself.

Does DialogueDB work with the Python version of the OpenAI Agents SDK?

No, DialogueDB does not yet ship a Python client. The current SDK is TypeScript only, so the integration works with the OpenAI Agents SDK for JavaScript and TypeScript. A Python client is on the roadmap.

Can I migrate to DialogueDB from an existing SQLite or Postgres session store?

Yes, you can migrate to DialogueDB from an existing SQLite or Postgres session store. Read your existing conversation table, group rows by session ID (which becomes the dialogue ID), and call dialogue.saveMessages(rows) for each dialogue. Namespaces map to your user identifier column.

## One API for messages, memory, and search

Add it to your OpenAI Agents SDK app in minutes.

[Get Your Free API Key](https://app.dialoguedb.com/signup) [See the Working Example](https://github.com/dialoguedb/examples/tree/main/openai-agents-sdk)
