Agent SDK

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 conversation

Forking 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 one

Rewinding 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 history

Best Practices

  1. Always persist in production — Ephemeral sessions lose context on crashes. Use a durable store.

  2. Enable file checkpointing for code agents — If your agent modifies files, checkpointing lets you rewind safely.

  3. Set session TTLs — Old sessions consume storage. Set expiration policies appropriate for your use case.

  4. Use metadata for organization — Tag sessions with user IDs, project names, and categories for easy retrieval.

  5. Fork for experiments — When trying different approaches, fork instead of starting from scratch. This preserves context.