Sessions
Persistent, resumable, and forkable agent conversations
Sessions
Sessions give agents persistent memory of a conversation that survives restarts, can be resumed later, forked into parallel explorations, and rewound when things go wrong.
Why Sessions?
Without sessions, every agent run is one-shot — the conversation starts and ends in a single process. Sessions unlock:
- Resume — User closes the app, comes back tomorrow, and continues where they left off
- Fork — Try two different approaches from the same point, keep the one that works
- Rewind — Agent went down a wrong path? Roll back to a specific message and try again
- Audit — Full conversation history is queryable and inspectable
Creating a Session
import { Runner, SessionStore } from 'assistme-agent-sdk'
const result = await Runner.run(agent, {
messages: [{ role: 'user', content: 'Help me refactor the auth module' }],
session: {
persist: true,
store: SessionStore.sqlite('./sessions.db'),
},
})
// Save the session ID for later
const sessionId = result.sessionId
console.log(`Session: ${sessionId}`) // "session_abc123"Resuming a Session
// Later... even in a different process
const result = await Runner.run(agent, {
messages: [{ role: 'user', content: 'Now update the tests for the refactored code' }],
session: {
resume: 'session_abc123',
store: SessionStore.sqlite('./sessions.db'),
},
})
// Agent has full context from the previous conversationForking a Session
Create a branch from any point in the conversation:
// Fork from a specific message
const fork = await SessionStore.fork('session_abc123', {
fromMessage: 'msg_xyz', // Fork point
store: SessionStore.sqlite('./sessions.db'),
})
// Run the fork with a different approach
const resultA = await Runner.run(agent, {
messages: [{ role: 'user', content: 'Try approach A: use JWT tokens' }],
session: { resume: fork.sessionId, store },
})
// Run the original with another approach
const resultB = await Runner.run(agent, {
messages: [{ role: 'user', content: 'Try approach B: use session cookies' }],
session: { resume: 'session_abc123', store },
})
// Compare results and keep the better oneRewinding a Session
Undo agent actions by rewinding to a previous state:
const session = await SessionStore.get('session_abc123', store)
// See the full message timeline
for (const msg of session.messages) {
console.log(`${msg.id}: [${msg.role}] ${msg.content.substring(0, 80)}...`)
}
// Rewind to before the agent's mistake
await SessionStore.rewind('session_abc123', {
toMessage: 'msg_before_mistake',
store,
// Also revert file changes made after this point
rewindFiles: true,
})
// Continue from the rewound state
const result = await Runner.run(agent, {
messages: [{ role: 'user', content: 'Try a different approach instead' }],
session: { resume: 'session_abc123', store },
})File Checkpointing
When rewindFiles: true, the SDK tracks all file modifications and can revert them:
const result = await Runner.run(agent, {
messages: [{ role: 'user', content: 'Refactor the auth module' }],
session: {
persist: true,
store,
checkpointFiles: true, // Track file changes
},
})
// Agent modified 5 files... but the changes are wrong
// Revert all file changes back to a specific point
await SessionStore.rewind(result.sessionId, {
toMessage: 'msg_before_refactor',
store,
rewindFiles: true, // Reverts files to their state at that message
})Session Stores
// SQLite (single-process, local)
SessionStore.sqlite('./sessions.db')
// Redis (distributed, multi-process)
SessionStore.redis({ url: 'redis://localhost:6379' })
// PostgreSQL (persistent, scalable)
SessionStore.postgres({ connectionString: process.env.DATABASE_URL })
// In-memory (testing)
SessionStore.inMemory()Querying Sessions
// List all sessions
const sessions = await SessionStore.list(store, {
userId: 'user-123',
limit: 20,
orderBy: 'updatedAt',
})
// Get a specific session
const session = await SessionStore.get('session_abc123', store)
console.log(session.messages)
console.log(session.createdAt)
console.log(session.updatedAt)
console.log(session.metadata)
// Get messages from a session
const messages = await SessionStore.getMessages('session_abc123', store, {
limit: 50,
after: 'msg_xyz', // Pagination
})Session Metadata
Attach custom metadata for querying and filtering:
const result = await Runner.run(agent, {
messages,
session: {
persist: true,
store,
metadata: {
userId: 'user-123',
project: 'auth-refactor',
tags: ['refactoring', 'authentication'],
},
},
})
// Query by metadata
const sessions = await SessionStore.list(store, {
filter: { project: 'auth-refactor' },
})Multi-Turn Conversations
Sessions make multi-turn agent conversations natural:
async function chat(sessionId: string | null, userMessage: string) {
const result = await Runner.run(agent, {
messages: [{ role: 'user', content: userMessage }],
session: {
...(sessionId ? { resume: sessionId } : { persist: true }),
store: SessionStore.postgres({ connectionString }),
},
})
return {
sessionId: result.sessionId,
response: result.output,
}
}
// Usage
let session = await chat(null, 'Help me build a REST API')
// session.sessionId = "session_abc"
session = await chat(session.sessionId, 'Add authentication')
session = await chat(session.sessionId, 'Now add rate limiting')
// Each call builds on the full conversation historyBest Practices
-
Always persist in production — Ephemeral sessions lose context on crashes. Use a durable store.
-
Enable file checkpointing for code agents — If your agent modifies files, checkpointing lets you rewind safely.
-
Set session TTLs — Old sessions consume storage. Set expiration policies appropriate for your use case.
-
Use metadata for organization — Tag sessions with user IDs, project names, and categories for easy retrieval.
-
Fork for experiments — When trying different approaches, fork instead of starting from scratch. This preserves context.