Initial commit
This commit is contained in:
419
references/session-management.md
Normal file
419
references/session-management.md
Normal file
@@ -0,0 +1,419 @@
|
||||
# Session Management Guide
|
||||
|
||||
Complete guide to sessions, resuming, and forking in Claude Agent SDK.
|
||||
|
||||
---
|
||||
|
||||
## What Are Sessions?
|
||||
|
||||
Sessions enable:
|
||||
- **Persistent conversations** - Resume where you left off
|
||||
- **Context preservation** - Agent remembers everything
|
||||
- **Alternative paths** - Fork to explore different approaches
|
||||
|
||||
---
|
||||
|
||||
## Session Lifecycle
|
||||
|
||||
```
|
||||
Start → Capture Session ID → Resume → Resume → ... → End
|
||||
↓
|
||||
Fork (alternative path)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Starting a Session
|
||||
|
||||
Every `query()` call creates a session.
|
||||
|
||||
```typescript
|
||||
let sessionId: string | undefined;
|
||||
|
||||
const response = query({
|
||||
prompt: "Build a REST API",
|
||||
options: { model: "sonnet" }
|
||||
});
|
||||
|
||||
for await (const message of response) {
|
||||
if (message.type === 'system' && message.subtype === 'init') {
|
||||
sessionId = message.session_id;
|
||||
console.log(`Session started: ${sessionId}`);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**CRITICAL**: Capture `session_id` from `system` init message.
|
||||
|
||||
---
|
||||
|
||||
## Resuming a Session
|
||||
|
||||
Continue a previous conversation.
|
||||
|
||||
```typescript
|
||||
const resumed = query({
|
||||
prompt: "Now add authentication",
|
||||
options: {
|
||||
resume: sessionId, // Resume previous session
|
||||
model: "sonnet"
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
**What's preserved**:
|
||||
- All previous messages
|
||||
- Agent's understanding of context
|
||||
- Files created/modified
|
||||
- Decisions made
|
||||
|
||||
**What's NOT preserved**:
|
||||
- Environment variables
|
||||
- Tool availability (specify again)
|
||||
- Permission settings (specify again)
|
||||
|
||||
---
|
||||
|
||||
## Forking a Session
|
||||
|
||||
Create alternative path without modifying original.
|
||||
|
||||
```typescript
|
||||
const forked = query({
|
||||
prompt: "Actually, make it GraphQL instead",
|
||||
options: {
|
||||
resume: sessionId,
|
||||
forkSession: true, // Creates new branch
|
||||
model: "sonnet"
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
**Result**:
|
||||
- New session created
|
||||
- Starts from same point as original
|
||||
- Original session unchanged
|
||||
- Can compare approaches
|
||||
|
||||
---
|
||||
|
||||
## Use Case Patterns
|
||||
|
||||
### Pattern 1: Sequential Development
|
||||
|
||||
Step-by-step feature building.
|
||||
|
||||
```typescript
|
||||
// Step 1: Initial implementation
|
||||
let session = await startSession("Create user authentication");
|
||||
|
||||
// Step 2: Add feature
|
||||
session = await resumeSession(session, "Add OAuth support");
|
||||
|
||||
// Step 3: Add tests
|
||||
session = await resumeSession(session, "Write integration tests");
|
||||
|
||||
// Step 4: Deploy
|
||||
session = await resumeSession(session, "Deploy to production");
|
||||
```
|
||||
|
||||
### Pattern 2: Exploration & Decision
|
||||
|
||||
Try multiple approaches, choose best.
|
||||
|
||||
```typescript
|
||||
// Start main conversation
|
||||
let mainSession = await startSession("Design payment system");
|
||||
|
||||
// Explore option A
|
||||
let optionA = await forkSession(mainSession, "Use Stripe");
|
||||
|
||||
// Explore option B
|
||||
let optionB = await forkSession(mainSession, "Use PayPal");
|
||||
|
||||
// Explore option C
|
||||
let optionC = await forkSession(mainSession, "Use Square");
|
||||
|
||||
// Choose winner
|
||||
let chosenSession = optionA; // Decision made
|
||||
await resumeSession(chosenSession, "Implement chosen approach");
|
||||
```
|
||||
|
||||
### Pattern 3: Multi-User Collaboration
|
||||
|
||||
Multiple developers, independent work.
|
||||
|
||||
```typescript
|
||||
// Developer A starts work
|
||||
let sessionA = await startSession("Implement user profile page");
|
||||
|
||||
// Developer B forks for different feature
|
||||
let sessionB = await forkSession(sessionA, "Add avatar upload");
|
||||
|
||||
// Developer C forks for another feature
|
||||
let sessionC = await forkSession(sessionA, "Implement search");
|
||||
|
||||
// All can work independently
|
||||
```
|
||||
|
||||
### Pattern 4: Error Recovery
|
||||
|
||||
Backup and restore points.
|
||||
|
||||
```typescript
|
||||
// Save checkpoint before risky operation
|
||||
let checkpoint = sessionId;
|
||||
|
||||
try {
|
||||
await resumeSession(checkpoint, "Refactor entire auth system");
|
||||
} catch (error) {
|
||||
console.log("Refactor failed, restoring from checkpoint");
|
||||
await forkSession(checkpoint, "Try safer incremental refactor");
|
||||
}
|
||||
```
|
||||
|
||||
### Pattern 5: A/B Testing
|
||||
|
||||
Test different implementations.
|
||||
|
||||
```typescript
|
||||
let baseline = await startSession("Implement feature X");
|
||||
|
||||
// Approach A
|
||||
let approachA = await forkSession(baseline, "Use algorithm A");
|
||||
const metricsA = await measurePerformance(approachA);
|
||||
|
||||
// Approach B
|
||||
let approachB = await forkSession(baseline, "Use algorithm B");
|
||||
const metricsB = await measurePerformance(approachB);
|
||||
|
||||
// Compare and choose
|
||||
const winner = metricsA.better(metricsB) ? approachA : approachB;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Session Best Practices
|
||||
|
||||
### ✅ Do
|
||||
|
||||
- Always capture `session_id` from init message
|
||||
- Store session IDs for later use
|
||||
- Use descriptive prompts when resuming
|
||||
- Fork for alternative approaches
|
||||
- Test resuming before deploying
|
||||
- Consider session lifetime limits
|
||||
|
||||
### ❌ Don't
|
||||
|
||||
- Forget to capture session ID
|
||||
- Assume sessions last forever
|
||||
- Resume with completely unrelated prompts
|
||||
- Fork excessively (creates many branches)
|
||||
- Rely on sessions for critical state
|
||||
- Skip testing resume functionality
|
||||
|
||||
---
|
||||
|
||||
## Helper Functions
|
||||
|
||||
```typescript
|
||||
async function startSession(prompt: string): Promise<string> {
|
||||
let sessionId: string | undefined;
|
||||
|
||||
const response = query({ prompt, options: { model: "sonnet" } });
|
||||
|
||||
for await (const message of response) {
|
||||
if (message.type === 'system' && message.subtype === 'init') {
|
||||
sessionId = message.session_id;
|
||||
}
|
||||
}
|
||||
|
||||
if (!sessionId) throw new Error('Failed to start session');
|
||||
return sessionId;
|
||||
}
|
||||
|
||||
async function resumeSession(
|
||||
sessionId: string,
|
||||
prompt: string
|
||||
): Promise<void> {
|
||||
const response = query({
|
||||
prompt,
|
||||
options: { resume: sessionId, model: "sonnet" }
|
||||
});
|
||||
|
||||
for await (const message of response) {
|
||||
if (message.type === 'assistant') {
|
||||
console.log(message.content);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function forkSession(
|
||||
sessionId: string,
|
||||
prompt: string
|
||||
): Promise<string> {
|
||||
let newSessionId: string | undefined;
|
||||
|
||||
const response = query({
|
||||
prompt,
|
||||
options: {
|
||||
resume: sessionId,
|
||||
forkSession: true,
|
||||
model: "sonnet"
|
||||
}
|
||||
});
|
||||
|
||||
for await (const message of response) {
|
||||
if (message.type === 'system' && message.subtype === 'init') {
|
||||
newSessionId = message.session_id;
|
||||
} else if (message.type === 'assistant') {
|
||||
console.log(message.content);
|
||||
}
|
||||
}
|
||||
|
||||
if (!newSessionId) throw new Error('Failed to fork session');
|
||||
return newSessionId;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Session Storage
|
||||
|
||||
Store sessions for later use:
|
||||
|
||||
```typescript
|
||||
// In-memory storage
|
||||
const sessions = new Map<string, { id: string; created: Date }>();
|
||||
|
||||
async function saveSession(prompt: string) {
|
||||
const id = await startSession(prompt);
|
||||
sessions.set(id, { id, created: new Date() });
|
||||
return id;
|
||||
}
|
||||
|
||||
function getSession(id: string) {
|
||||
return sessions.get(id);
|
||||
}
|
||||
|
||||
// Database storage
|
||||
async function saveSessionToDb(sessionId: string, metadata: any) {
|
||||
await db.insert('sessions', {
|
||||
id: sessionId,
|
||||
created_at: new Date(),
|
||||
metadata
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Session Limits
|
||||
|
||||
### Context Window
|
||||
|
||||
Sessions have context window limits (200k tokens for Sonnet).
|
||||
|
||||
**Strategies**:
|
||||
- SDK auto-compacts context
|
||||
- Fork to start fresh from a point
|
||||
- Summarize and start new session
|
||||
|
||||
### Lifetime
|
||||
|
||||
Sessions may expire after inactivity.
|
||||
|
||||
**Strategies**:
|
||||
- Don't rely on sessions lasting indefinitely
|
||||
- Store important state separately
|
||||
- Test resume functionality
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Session Not Found
|
||||
|
||||
**Problem**: `"Invalid session ID"`
|
||||
|
||||
**Causes**:
|
||||
- Session expired
|
||||
- Invalid session ID
|
||||
- Session from different CLI instance
|
||||
|
||||
**Solution**: Start new session
|
||||
|
||||
### Context Preserved Incorrectly
|
||||
|
||||
**Problem**: Agent doesn't remember previous work
|
||||
|
||||
**Causes**:
|
||||
- Different settings/tools specified
|
||||
- Context window exceeded
|
||||
- Fork instead of resume
|
||||
|
||||
**Solution**: Verify using `resume` not `forkSession`
|
||||
|
||||
### Too Many Forks
|
||||
|
||||
**Problem**: Hard to track branches
|
||||
|
||||
**Solution**: Limit forking, clean up unused branches
|
||||
|
||||
---
|
||||
|
||||
## Complete Example
|
||||
|
||||
```typescript
|
||||
class SessionManager {
|
||||
private sessions = new Map<string, string>();
|
||||
|
||||
async start(name: string, prompt: string): Promise<string> {
|
||||
const id = await startSession(prompt);
|
||||
this.sessions.set(name, id);
|
||||
console.log(`✨ Started: ${name}`);
|
||||
return id;
|
||||
}
|
||||
|
||||
async resume(name: string, prompt: string): Promise<void> {
|
||||
const id = this.sessions.get(name);
|
||||
if (!id) throw new Error(`Session ${name} not found`);
|
||||
console.log(`↪️ Resuming: ${name}`);
|
||||
await resumeSession(id, prompt);
|
||||
}
|
||||
|
||||
async fork(
|
||||
fromName: string,
|
||||
newName: string,
|
||||
prompt: string
|
||||
): Promise<string> {
|
||||
const fromId = this.sessions.get(fromName);
|
||||
if (!fromId) throw new Error(`Session ${fromName} not found`);
|
||||
console.log(`🔀 Forking: ${fromName} → ${newName}`);
|
||||
const newId = await forkSession(fromId, prompt);
|
||||
this.sessions.set(newName, newId);
|
||||
return newId;
|
||||
}
|
||||
|
||||
list(): string[] {
|
||||
return Array.from(this.sessions.keys());
|
||||
}
|
||||
}
|
||||
|
||||
// Usage
|
||||
const manager = new SessionManager();
|
||||
|
||||
await manager.start("main", "Build a web app");
|
||||
await manager.resume("main", "Add authentication");
|
||||
await manager.fork("main", "option-a", "Use JWT tokens");
|
||||
await manager.fork("main", "option-b", "Use sessions");
|
||||
|
||||
console.log("Sessions:", manager.list());
|
||||
// ["main", "option-a", "option-b"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**For more details**: See SKILL.md
|
||||
**Template**: templates/session-management.ts
|
||||
Reference in New Issue
Block a user