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>
This commit is contained in:
kappa
2026-02-05 11:27:25 +09:00
parent 36fdc4fe3d
commit 1160719a78

View File

@@ -0,0 +1,255 @@
# 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)
```typescript
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)
```typescript
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.
```typescript
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.
```typescript
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
```typescript
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.
```typescript
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`.
```typescript
async save(db: D1Database, session: T): Promise<void>
```
#### delete(db, userId)
Delete session from D1.
```typescript
async delete(db: D1Database, userId: string): Promise<void>
```
#### has(db, userId)
Check if active session exists (lightweight query).
```typescript
async has(db: D1Database, userId: string): Promise<boolean>
```
#### create(userId, status, additionalFields?)
Create a new session object (not saved to DB yet).
```typescript
create(userId: string, status: string, additionalFields?: Partial<T>): T
```
#### isExpired(session)
Check if session is expired.
```typescript
isExpired(session: T): boolean
```
#### addMessage(session, role, content)
Add message to session with automatic trimming.
```typescript
addMessage(session: T, role: 'user' | 'assistant', content: string): void
```
#### getOrCreate(db, userId, initialStatus)
Get existing session or create new one (convenience method).
```typescript
async getOrCreate(db: D1Database, userId: string, initialStatus: string): Promise<T>
```
## Migration Guide
### Before (domain-agent.ts)
```typescript
// 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)
```typescript
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`)