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:
255
docs/session-manager-usage.md
Normal file
255
docs/session-manager-usage.md
Normal 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`)
|
||||
Reference in New Issue
Block a user