Files
telegram-bot-workers/docs/session-manager-usage.md
kappa 1160719a78 docs: add SessionManager usage guide
- Comprehensive API documentation
- Migration examples (before/after)
- Specialized manager patterns
- Next steps for agent refactoring

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 12:47:34 +09:00

6.8 KiB

SessionManager Usage Guide

Overview

The SessionManager<T> class provides a generic, reusable way to manage agent sessions with D1 database. It eliminates duplicated CRUD code across all 4 agents (Domain, Deposit, Server, Troubleshoot).

Features

  • CRUD Operations: get, save, delete, has
  • Expiry Management: Automatic TTL handling
  • Message Management: Add messages with automatic trimming (max limit)
  • Type Safety: Full TypeScript support with generics
  • Extensible: Specialized managers for agents with extra fields

Basic Usage

1. For Simple Sessions (Deposit, Troubleshoot)

import { SessionManager } from '../utils/session-manager';
import type { DepositSession } from '../types';

// Create manager instance
const depositSessionManager = new SessionManager<DepositSession>({
  tableName: 'deposit_sessions',
  ttlMs: 30 * 60 * 1000, // 30 minutes
  maxMessages: 20
});

// Get or create session
let session = await depositSessionManager.get(db, userId);
if (!session) {
  session = depositSessionManager.create(userId, 'collecting_amount');
}

// Add message
depositSessionManager.addMessage(session, 'user', userMessage);

// Save session
await depositSessionManager.save(db, session);

// Delete session
await depositSessionManager.delete(db, userId);

// Check if session exists
const hasSession = await depositSessionManager.has(db, userId);

2. For Sessions with Extra Fields (Domain, Server)

import { DomainSessionManager } from '../utils/session-manager';
import type { DomainSession } from '../types';

// Use specialized manager
const domainSessionManager = new DomainSessionManager({
  tableName: 'domain_sessions',
  ttlMs: 60 * 60 * 1000, // 1 hour
  maxMessages: 20
});

// Create with additional fields
const session = domainSessionManager.create(userId, 'gathering', {
  target_domain: 'example.com'
});

// The manager handles target_domain serialization automatically
await domainSessionManager.save(db, session);

Specialized Managers

DomainSessionManager

Handles target_domain field for Domain Agent.

export class DomainSessionManager extends SessionManager<DomainSession> {
  protected parseAdditionalFields(result: Record<string, unknown>): Partial<DomainSession> {
    return {
      target_domain: result.target_domain as string | undefined,
    };
  }

  protected getAdditionalColumns(session: DomainSession): Record<string, unknown> {
    return {
      target_domain: session.target_domain || null,
    };
  }
}

ServerSessionManager

Handles last_recommendation field for Server Agent.

export class ServerSessionManager extends SessionManager<ServerSession> {
  protected parseAdditionalFields(result: Record<string, unknown>): Partial<ServerSession> {
    return {
      last_recommendation: result.last_recommendation
        ? JSON.parse(result.last_recommendation as string)
        : undefined,
    };
  }

  protected getAdditionalColumns(session: ServerSession): Record<string, unknown> {
    return {
      last_recommendation: session.last_recommendation
        ? JSON.stringify(session.last_recommendation)
        : null,
    };
  }
}

API Reference

Constructor

constructor(config: SessionManagerConfig)

Parameters:

  • config.tableName: D1 table name (e.g., 'domain_sessions')
  • config.ttlMs: Session TTL in milliseconds
  • config.maxMessages: Maximum messages to keep in session

Methods

get(db, userId)

Get session from D1. Returns null if not found or expired.

async get(db: D1Database, userId: string): Promise<T | null>

save(db, session)

Save session to D1 (insert or replace). Automatically updates updated_at and expires_at.

async save(db: D1Database, session: T): Promise<void>

delete(db, userId)

Delete session from D1.

async delete(db: D1Database, userId: string): Promise<void>

has(db, userId)

Check if active session exists (lightweight query).

async has(db: D1Database, userId: string): Promise<boolean>

create(userId, status, additionalFields?)

Create a new session object (not saved to DB yet).

create(userId: string, status: string, additionalFields?: Partial<T>): T

isExpired(session)

Check if session is expired.

isExpired(session: T): boolean

addMessage(session, role, content)

Add message to session with automatic trimming.

addMessage(session: T, role: 'user' | 'assistant', content: string): void

getOrCreate(db, userId, initialStatus)

Get existing session or create new one (convenience method).

async getOrCreate(db: D1Database, userId: string, initialStatus: string): Promise<T>

Migration Guide

Before (domain-agent.ts)

// 160 lines of duplicated CRUD code
async function getDomainSession(db: D1Database, userId: string): Promise<DomainSession | null> {
  // ... 40 lines
}

async function saveDomainSession(db: D1Database, session: DomainSession): Promise<void> {
  // ... 30 lines
}

async function deleteDomainSession(db: D1Database, userId: string): Promise<void> {
  // ... 10 lines
}

function createDomainSession(userId: string, status: DomainSessionStatus): DomainSession {
  // ... 10 lines
}

function isSessionExpired(session: DomainSession): boolean {
  // ... 5 lines
}

function addMessageToSession(session: DomainSession, role: 'user' | 'assistant', content: string): void {
  // ... 15 lines
}

async function hasDomainSession(db: D1Database, userId: string): Promise<boolean> {
  // ... 10 lines
}

After (with SessionManager)

import { DomainSessionManager } from '../utils/session-manager';

const sessionManager = new DomainSessionManager({
  tableName: 'domain_sessions',
  ttlMs: 60 * 60 * 1000,
  maxMessages: 20
});

// All CRUD operations now use the manager
const session = await sessionManager.get(db, userId);
sessionManager.addMessage(session, 'user', message);
await sessionManager.save(db, session);

Result: ~160 lines → ~10 lines per agent = ~600 lines saved across 4 agents!

Next Steps

  1. Task 19: Refactor domain-agent.ts to use DomainSessionManager
  2. Task 20: Refactor server-agent.ts to use ServerSessionManager
  3. Task 21: Refactor deposit-agent.ts to use SessionManager<DepositSession>
  4. Task 22: Refactor troubleshoot-agent.ts to use SessionManager<TroubleshootSession>

Notes

  • The base SessionManager<T> works for simple sessions (Deposit, Troubleshoot)
  • Specialized managers (DomainSessionManager, ServerSessionManager) handle extra fields
  • All session operations are logged with structured logging
  • Expired sessions are automatically filtered out in get() method
  • Message trimming prevents memory bloat (configurable maxMessages)