State Management for Long-Running Claude Code Tasks
As AI-powered workflows become more sophisticated, managing state across extended sessions becomes critical. This guide covers advanced patterns for maintaining context, implementing checkpoints, and ensuring coherent state in long-running Claude Code applications.
Table of Contents
- Understanding State in LLM Applications
- Layered Memory Architecture
- Checkpoint and Recovery Systems
- Context Window Optimization
- Memory Augmentation with RAG
- Session Management Patterns
- Production Implementation Examples
- Performance Considerations
Understanding State in LLM Applications
Unlike traditional applications, LLM state management must handle:
- Context Limitations: Even with Claude’s 100K+ token window, complex workflows can exceed limits
- Session Discontinuity: Tasks may span multiple sessions or be interrupted
- Coherence Requirements: Maintaining consistent understanding across interactions
- Cost Optimization: Balancing comprehensive context with API costs
State Types in Claude Code
interface LLMStateTypes {
// Immediate context for current interaction
activeMemory: {
currentTask: string
recentMessages: Message[]
workingVariables: Map<string, any>
}
// Essential context maintained across interactions
coreMemory: {
projectContext: string
keyDecisions: Decision[]
establishedPatterns: Pattern[]
}
// Historical data for reference
archivalMemory: {
completedTasks: Task[]
pastConversations: Conversation[]
learnedPreferences: Preference[]
}
}Layered Memory Architecture
Implement a hierarchical memory system that mirrors human cognitive processes:
Three-Layer Memory Model
class LayeredMemorySystem {
private activeMemory: ActiveMemory
private coreMemory: CoreMemory
private archivalMemory: ArchivalMemory
constructor(private maxActiveTokens: number = 50000) {
this.activeMemory = new ActiveMemory(maxActiveTokens)
this.coreMemory = new CoreMemory()
this.archivalMemory = new ArchivalMemory()
}
async processInteraction(input: string): Promise<string> {
// Load relevant memories
const context = await this.buildContext(input)
// Process with Claude
const response = await this.claudeClient.sendMessage({
messages: [{
role: 'system',
content: context.system
}, {
role: 'user',
content: context.user
}]
})
// Update memories
await this.updateMemories(input, response)
return response
}
private async buildContext(input: string): Promise<Context> {
// Retrieve relevant memories from each layer
const active = this.activeMemory.getRecent()
const core = await this.coreMemory.getRelevant(input)
const archival = await this.archivalMemory.search(input, 5)
// Intelligently combine based on relevance and token limits
return this.contextBuilder.combine(active, core, archival)
}
}Active Memory Implementation
class ActiveMemory {
private buffer: CircularBuffer<MemoryItem>
private tokenCounter: TokenCounter
constructor(private maxTokens: number) {
this.buffer = new CircularBuffer(100) // Keep last 100 items
this.tokenCounter = new TokenCounter()
}
add(item: MemoryItem): void {
this.buffer.push(item)
// Prune if exceeding token limit
while (this.getTotalTokens() > this.maxTokens) {
this.pruneOldest()
}
}
getRecent(n: number = 10): MemoryItem[] {
return this.buffer.getRecent(n)
}
private pruneOldest(): void {
// Remove oldest non-essential items
const items = this.buffer.getAll()
const prunable = items.filter(item => !item.isPinned)
if (prunable.length > 0) {
this.buffer.remove(prunable[0].id)
}
}
}Core Memory with Compression
class CoreMemory {
private facts: Map<string, Fact>
private patterns: Map<string, Pattern>
private summarizer: Summarizer
async consolidate(): Promise<void> {
// Periodically compress and consolidate memories
const recentFacts = this.getRecentFacts()
if (recentFacts.length > 10) {
const summary = await this.summarizer.summarize(recentFacts)
this.addCompressedMemory(summary)
this.removeConsolidatedFacts(recentFacts)
}
}
async getRelevant(query: string): Promise<Memory[]> {
// Use embeddings to find relevant memories
const queryEmbedding = await this.embedder.embed(query)
const relevantFacts = this.findSimilar(queryEmbedding, this.facts, 5)
const relevantPatterns = this.findSimilar(queryEmbedding, this.patterns, 3)
return [...relevantFacts, ...relevantPatterns]
}
}Checkpoint and Recovery Systems
Implement robust checkpointing for workflow resilience:
Comprehensive Checkpoint System
interface WorkflowCheckpoint {
id: string
timestamp: Date
workflowId: string
state: {
activeMemory: SerializedMemory
coreMemory: SerializedMemory
taskProgress: TaskProgress
variables: Map<string, any>
}
metadata: {
totalSteps: number
completedSteps: number
lastActivity: Date
checkpointSize: number
}
}
class CheckpointManager {
private storage: CheckpointStorage
private compression: CompressionService
async createCheckpoint(workflow: Workflow): Promise<string> {
const checkpoint: WorkflowCheckpoint = {
id: generateCheckpointId(),
timestamp: new Date(),
workflowId: workflow.id,
state: await this.serializeState(workflow),
metadata: this.gatherMetadata(workflow)
}
// Compress for efficient storage
const compressed = await this.compression.compress(checkpoint)
// Store with redundancy
await Promise.all([
this.storage.savePrimary(compressed),
this.storage.saveBackup(compressed)
])
return checkpoint.id
}
async restoreFromCheckpoint(checkpointId: string): Promise<Workflow> {
try {
const compressed = await this.storage.load(checkpointId)
const checkpoint = await this.compression.decompress(compressed)
return this.reconstructWorkflow(checkpoint)
} catch (error) {
// Attempt recovery from backup
return this.attemptBackupRecovery(checkpointId)
}
}
}Incremental Checkpointing
For large workflows, use incremental checkpoints:
class IncrementalCheckpointer {
private baseCheckpoint: Checkpoint
private deltas: Delta[]
async saveIncremental(workflow: Workflow): Promise<void> {
const currentState = await this.captureState(workflow)
const delta = this.computeDelta(this.baseCheckpoint, currentState)
if (delta.size > this.DELTA_THRESHOLD) {
// Create new base checkpoint
await this.createBaseCheckpoint(workflow)
} else {
// Save just the delta
await this.saveDelta(delta)
this.deltas.push(delta)
}
}
async restore(checkpointId: string): Promise<Workflow> {
const base = await this.loadBaseCheckpoint(checkpointId)
const deltas = await this.loadDeltas(checkpointId)
// Apply deltas in sequence
return this.applyDeltas(base, deltas)
}
}Context Window Optimization
Maximize effective use of Claude’s context window:
Intelligent Context Pruning
class ContextOptimizer {
private readonly MAX_TOKENS = 100000
private readonly BUFFER = 10000 // Safety buffer
async optimizeContext(
fullContext: ContextItem[],
currentQuery: string
): Promise<OptimizedContext> {
// Calculate relevance scores
const scoredItems = await this.scoreRelevance(fullContext, currentQuery)
// Group by priority
const grouped = this.groupByPriority(scoredItems)
// Build optimized context within token limits
const optimized = this.buildOptimizedContext(grouped)
// Add summary of pruned content
if (optimized.prunedCount > 0) {
optimized.summary = await this.summarizePruned(optimized.pruned)
}
return optimized
}
private async scoreRelevance(
items: ContextItem[],
query: string
): Promise<ScoredItem[]> {
const queryEmbedding = await this.embedder.embed(query)
return Promise.all(items.map(async item => ({
item,
score: await this.calculateScore(item, queryEmbedding),
tokens: this.tokenCounter.count(item.content)
})))
}
private calculateScore(
item: ContextItem,
queryEmbedding: number[]
): number {
const factors = {
semantic: this.semanticSimilarity(item.embedding, queryEmbedding),
recency: this.recencyScore(item.timestamp),
importance: item.importance || 0.5,
referenced: item.referenceCount / 10
}
// Weighted combination
return (
factors.semantic * 0.4 +
factors.recency * 0.2 +
factors.importance * 0.3 +
factors.referenced * 0.1
)
}
}Progressive Summarization
class ProgressiveSummarizer {
async maintainSummary(conversation: Conversation): Promise<void> {
const segments = this.segmentConversation(conversation)
for (const segment of segments) {
if (segment.needsSummarization()) {
const summary = await this.summarizeSegment(segment)
segment.setSummary(summary)
// Mark original messages as archived
segment.archiveOriginals()
}
}
}
private async summarizeSegment(segment: ConversationSegment): Promise<string> {
const prompt = `
Summarize the following conversation segment, preserving:
1. Key decisions made
2. Important facts discovered
3. Action items identified
4. Context needed for future reference
Segment:
${segment.getMessages().map(m => `${m.role}: ${m.content}`).join('\n')}
Provide a concise summary that maintains essential information:
`
const response = await this.claudeClient.sendMessage({
messages: [{ role: 'user', content: prompt }]
})
return response.content
}
}Memory Augmentation with RAG
Integrate vector databases for unlimited memory:
RAG-Enhanced Memory System
class RAGMemorySystem {
private vectorStore: VectorStore
private chunkingStrategy: ChunkingStrategy
async store(content: string, metadata: Metadata): Promise<void> {
// Use SPLICE chunking for better coherence
const chunks = await this.chunkingStrategy.chunk(content, {
method: 'splice',
overlapTokens: 50,
maxChunkSize: 512
})
// Generate embeddings
const embeddings = await Promise.all(
chunks.map(chunk => this.embedder.embed(chunk))
)
// Store with metadata
await this.vectorStore.upsert(
chunks.map((chunk, i) => ({
id: generateId(),
content: chunk,
embedding: embeddings[i],
metadata: {
...metadata,
chunkIndex: i,
totalChunks: chunks.length
}
}))
)
}
async retrieve(query: string, k: number = 5): Promise<MemoryResult[]> {
const queryEmbedding = await this.embedder.embed(query)
// Hybrid search combining semantic and keyword
const results = await this.vectorStore.hybridSearch({
vector: queryEmbedding,
text: query,
k: k * 2, // Over-retrieve for re-ranking
threshold: 0.7
})
// Re-rank using cross-encoder
const reranked = await this.reranker.rerank(query, results)
// Return top k after re-ranking
return reranked.slice(0, k)
}
}Hierarchical Memory Index
class HierarchicalMemoryIndex {
private levels: MemoryLevel[]
constructor() {
this.levels = [
new MemoryLevel('immediate', 1000), // Last 1K tokens
new MemoryLevel('recent', 10000), // Last 10K tokens
new MemoryLevel('context', 50000), // Current context
new MemoryLevel('extended', Infinity) // Vector store
]
}
async query(prompt: string): Promise<RetrievedMemories> {
const results = new RetrievedMemories()
// Query each level based on needs
for (const level of this.levels) {
const memories = await level.retrieve(prompt)
results.add(level.name, memories)
// Stop if we have enough context
if (results.totalTokens() > this.TARGET_TOKENS) {
break
}
}
return results
}
}Session Management Patterns
Handle multi-session workflows effectively:
Session State Manager
interface SessionState {
id: string
userId: string
workflowId: string
created: Date
lastActive: Date
state: {
memory: LayeredMemory
variables: Map<string, any>
progress: WorkflowProgress
}
preferences: UserPreferences
}
class SessionManager {
private activeSessions: Map<string, SessionState>
private sessionStore: SessionStore
async createSession(userId: string, workflowId: string): Promise<string> {
const session: SessionState = {
id: generateSessionId(),
userId,
workflowId,
created: new Date(),
lastActive: new Date(),
state: this.initializeState(),
preferences: await this.loadUserPreferences(userId)
}
this.activeSessions.set(session.id, session)
await this.sessionStore.save(session)
return session.id
}
async resumeSession(sessionId: string): Promise<SessionState> {
// Check active sessions first
if (this.activeSessions.has(sessionId)) {
return this.activeSessions.get(sessionId)!
}
// Load from storage
const session = await this.sessionStore.load(sessionId)
// Restore memory state
session.state.memory = await this.restoreMemory(session.state.memory)
// Add to active sessions
this.activeSessions.set(sessionId, session)
return session
}
async suspendSession(sessionId: string): Promise<void> {
const session = this.activeSessions.get(sessionId)
if (!session) return
// Persist current state
await this.sessionStore.save(session)
// Clear from active memory
this.activeSessions.delete(sessionId)
// Archive if inactive too long
if (this.isInactive(session, this.INACTIVE_THRESHOLD)) {
await this.archiveSession(session)
}
}
}Cross-Session Context Sharing
class CrossSessionContextManager {
async shareContext(
sourceSession: string,
targetSession: string,
contextType: ContextType
): Promise<void> {
const sourceState = await this.sessionManager.getSession(sourceSession)
const targetState = await this.sessionManager.getSession(targetSession)
switch (contextType) {
case 'full':
await this.shareFullContext(sourceState, targetState)
break
case 'decisions':
await this.shareDecisions(sourceState, targetState)
break
case 'patterns':
await this.sharePatterns(sourceState, targetState)
break
}
await this.sessionManager.updateSession(targetState)
}
private async sharePatterns(source: SessionState, target: SessionState): Promise<void> {
const patterns = source.state.memory.coreMemory.getPatterns()
// Filter applicable patterns
const applicable = patterns.filter(pattern =>
this.isApplicable(pattern, target.workflowId)
)
// Add to target with attribution
applicable.forEach(pattern => {
target.state.memory.coreMemory.addPattern({
...pattern,
source: source.id,
confidence: pattern.confidence * 0.8 // Reduce confidence for shared patterns
})
})
}
}Production Implementation Examples
Real-World Code Review System
class CodeReviewOrchestrator {
private stateManager: StateManager
private checkpointer: CheckpointManager
async reviewPullRequest(prUrl: string): Promise<ReviewResult> {
const session = await this.stateManager.createSession('code-review', prUrl)
try {
// Initialize with PR context
await session.memory.addContext({
type: 'pull_request',
content: await this.fetchPRDetails(prUrl),
importance: 1.0
})
// Multi-stage review process
const stages = ['security', 'performance', 'style', 'logic']
for (const stage of stages) {
// Checkpoint before each stage
await this.checkpointer.createCheckpoint(session)
const result = await this.executeReviewStage(session, stage)
// Update session memory with findings
await session.memory.addFinding({
stage,
findings: result.findings,
severity: result.severity
})
// Check if we should continue
if (result.severity === 'critical') {
break
}
}
// Synthesize all findings
return await this.synthesizeReview(session)
} catch (error) {
// Restore from last checkpoint
const lastCheckpoint = await this.checkpointer.getLatest(session.id)
await this.checkpointer.restore(lastCheckpoint)
throw error
} finally {
// Persist session state
await this.stateManager.suspendSession(session.id)
}
}
}Document Processing Pipeline
class DocumentProcessor {
private memory: RAGMemorySystem
private sessionManager: SessionManager
async processLargeDocument(
documentPath: string,
sessionId: string
): Promise<ProcessingResult> {
const session = await this.sessionManager.resumeSession(sessionId)
// Chunk document for processing
const chunks = await this.chunkDocument(documentPath)
// Process chunks with state preservation
const results = []
for (let i = 0; i < chunks.length; i++) {
// Add progress to session state
session.state.progress = {
current: i + 1,
total: chunks.length,
percentage: ((i + 1) / chunks.length) * 100
}
// Process chunk with accumulated context
const chunkResult = await this.processChunk(chunks[i], session)
results.push(chunkResult)
// Update memory with insights
await this.memory.store(
chunkResult.insights,
{
documentId: documentPath,
chunkIndex: i,
sessionId: session.id
}
)
// Periodic consolidation
if (i % 10 === 0) {
await session.state.memory.consolidate()
}
}
// Final synthesis using full context
return await this.synthesizeResults(results, session)
}
}Performance Considerations
Token Usage Optimization
class TokenOptimizer {
private cache: TokenCache
async optimizeWorkflow(workflow: Workflow): Promise<OptimizationResult> {
const analysis = await this.analyzeTokenUsage(workflow)
const optimizations = {
// Cache frequently used contexts
caching: await this.identifyCacheable(analysis),
// Compress verbose sections
compression: await this.identifyCompressible(analysis),
// Remove redundant information
deduplication: await this.findDuplicates(analysis),
// Suggest more efficient prompts
promptOptimization: await this.optimizePrompts(analysis)
}
return {
currentUsage: analysis.totalTokens,
potentialSavings: this.calculateSavings(optimizations),
recommendations: this.generateRecommendations(optimizations)
}
}
}Memory Performance Metrics
class MemoryPerformanceMonitor {
async trackPerformance(operation: string): Promise<void> {
const metrics = {
operation,
timestamp: new Date(),
memory: {
retrieval_time: 0,
storage_time: 0,
compression_ratio: 0
},
tokens: {
input: 0,
output: 0,
cached: 0
},
cost: {
api_calls: 0,
storage: 0,
compute: 0
}
}
await this.metricsStore.save(metrics)
}
}Best Practices
- Layer Your Memory: Use active, core, and archival layers appropriately
- Checkpoint Frequently: But balance with storage costs
- Compress Aggressively: Use summarization to maintain context within limits
- Monitor Token Usage: Track and optimize to control costs
- Test Recovery: Regularly test checkpoint restoration
- Version Your State: Track schema changes for backward compatibility
- Secure Sensitive Data: Encrypt state containing user information
Related Resources
- Multi-Step Task Orchestration
- Working with Large Context Windows
- Prompt Caching Strategies
- Database Integration for State Storage
- Vector Database Implementation
Next Steps
- Implement Advanced Memory Patterns
- Explore Task Decomposition Strategies
- Learn State Debugging Techniques
Last updated: January 2025. State management techniques evolve rapidly with LLM capabilities. Check for updates regularly.