Skip to main content
The ContextWindow class is a core component of the qckfx Agent SDK that manages conversation context, message history, and file access tracking. It provides a structured way to handle the conversation flow between users and agents while maintaining important metadata.

Overview

The ContextWindow manages:
  • Conversation History - All messages exchanged between user and agent
  • File Access Tracking - Records which files have been read during the conversation
  • Checkpoint Management - Tracks checkpoint IDs for rollback functionality
  • Message Validation - Ensures proper message ordering and structure

Core Concepts

Message Types

The ContextWindow handles several types of messages:
type Message = {
  role: 'user' | 'assistant';
  content: string | ContentBlock[];
}

interface ConversationMessage {
  id: string;                    // Unique message identifier
  anthropic: Message;            // The actual message content
  createdAt: number;            // Timestamp when message was created
  lastCheckpointId?: string;    // Associated checkpoint for rollback
}

Content Blocks

Messages can contain different types of content blocks:
type ContentBlock = 
  | { type: 'text'; text: string }
  | { type: 'tool_use'; id: string; name: string; input: Record<string, unknown> }
  | { type: 'tool_result'; tool_use_id: string; content: string }

Creating and Using ContextWindow

Basic Creation

import { createContextWindow } from '@qckfx/agent';

const context = createContextWindow();

Adding Messages

The ContextWindow provides several methods for adding different types of messages:
// Add a simple user message
const messageId = context.pushUser('What files are in this directory?');

// The message ID can be used for rollback operations
console.log('Message ID:', messageId);

File Access Tracking

The ContextWindow automatically tracks which files have been accessed during the conversation:
// Record that a file was read
context.recordFileRead('/path/to/file.ts');
context.recordFileRead('package.json');

// Check if a file has been read
if (context.hasReadFile('package.json')) {
  console.log('Package.json was already read in this context');
}

// Get all files that have been read
const readFiles = context.getReadFiles();
console.log('Files read:', readFiles);

Checkpoint Management

Checkpoints enable rollback functionality by tracking the state at specific points:
// Set the current checkpoint ID
context.setLastCheckpointId('checkpoint_abc123');

// Get the current checkpoint ID
const checkpointId = context.getLastCheckpointId();

// All new messages will inherit this checkpoint ID
const messageId = context.pushUser('New message');

Message Retrieval

// Get raw Anthropic messages (for API calls)
const messages = context.getMessages();

// Get full conversation messages with metadata
const conversationMessages = context.getConversationMessages();

// Get the last message
const lastMessage = context.peek();
if (lastMessage) {
  console.log('Last message:', lastMessage.anthropic.content);
}

// Get conversation length
const length = context.getLength();
console.log(`Conversation has ${length} messages`);

Context Management

// Clear all messages
context.clear();

// Set new messages (replaces all existing messages)
const newMessages = [
  { role: 'user', content: 'Starting fresh conversation' }
];
context.setMessages(newMessages);

Message Validation

The ContextWindow automatically validates message structure in development and test environments:
// This validation happens automatically when adding messages
try {
  context.pushToolUse({
    id: 'tool_123',
    name: 'FileReadTool',
    input: { path: 'file.txt' }
  });
  
  // This MUST be followed by a matching tool_result
  context.pushToolResult('tool_123', { ok: true, data: 'file content' });
} catch (error) {
  console.error('Message validation failed:', error.message);
}
Validation Rules:
  • Every tool_use message must be immediately followed by a matching tool_result
  • Tool result tool_use_id must match the tool use id
  • Validation only runs in development and test environments for performance

Advanced Usage Patterns

Context Window with Agent Integration

import { Agent, createContextWindow } from '@qckfx/agent';

class ConversationManager {
  private context: ContextWindow;
  private agent: Agent;

  constructor(agent: Agent) {
    this.agent = agent;
    this.context = createContextWindow();
  }

  async processQuery(query: string): Promise<string> {
    // Add user message to context
    this.context.pushUser(query);
    
    // Process with agent
    const result = await this.agent.processQuery(query);
    
    // Add assistant response to context
    this.context.pushAssistant(result.response);
    
    return result.response;
  }

  getConversationSummary() {
    return {
      messageCount: this.context.getLength(),
      filesAccessed: this.context.getReadFiles(),
      lastCheckpoint: this.context.getLastCheckpointId()
    };
  }

  rollbackToCheckpoint(checkpointId: string) {
    const messages = this.context.getConversationMessages();
    const checkpointMessage = messages.find(m => m.lastCheckpointId === checkpointId);
    
    if (checkpointMessage) {
      this.context.rollbackToMessage(checkpointMessage.id);
    }
  }
}

File Access Monitoring

class FileAccessMonitor {
  private context: ContextWindow;
  private accessLog: Array<{ file: string; timestamp: number }> = [];

  constructor(context: ContextWindow) {
    this.context = context;
  }

  recordAccess(filePath: string) {
    this.context.recordFileRead(filePath);
    this.accessLog.push({
      file: filePath,
      timestamp: Date.now()
    });
  }

  getAccessPattern() {
    const readFiles = this.context.getReadFiles();
    return {
      uniqueFiles: readFiles.length,
      totalAccesses: this.accessLog.length,
      mostRecentAccess: this.accessLog[this.accessLog.length - 1],
      accessHistory: this.accessLog
    };
  }

  hasAccessedRecently(filePath: string, withinMs: number = 60000): boolean {
    const recentAccess = this.accessLog
      .filter(log => log.file === filePath)
      .find(log => Date.now() - log.timestamp < withinMs);
    
    return !!recentAccess;
  }
}

Best Practices

Memory Management

class ContextManager {
  private context: ContextWindow;
  private maxMessages: number;

  constructor(maxMessages: number = 100) {
    this.context = createContextWindow();
    this.maxMessages = maxMessages;
  }

  addMessage(message: Message) {
    this.context.push(message);
    
    // Keep context size manageable
    if (this.context.getLength() > this.maxMessages) {
      this.trimOldMessages();
    }
  }

  private trimOldMessages() {
    const messages = this.context.getConversationMessages();
    const excess = messages.length - this.maxMessages;
    
    if (excess > 0) {
      // Remove oldest messages but preserve recent checkpoint
      const oldestToKeep = messages[excess];
      this.context.rollbackToMessage(oldestToKeep.id);
    }
  }
}

Error Handling

class SafeContextWindow {
  private context: ContextWindow;

  constructor() {
    this.context = createContextWindow();
  }

  safeAddMessage(message: Message): string | null {
    try {
      return this.context.push(message);
    } catch (error) {
      console.error('Failed to add message to context:', error);
      return null;
    }
  }

  safeRollback(messageId: string): boolean {
    try {
      const removed = this.context.rollbackToMessage(messageId);
      return removed > 0;
    } catch (error) {
      console.error('Failed to rollback context:', error);
      return false;
    }
  }

  getContextHealth(): { isValid: boolean; issues: string[] } {
    const issues: string[] = [];
    
    try {
      const messages = this.context.getConversationMessages();
      
      // Check for orphaned tool_use messages
      for (let i = 0; i < messages.length - 1; i++) {
        const current = messages[i].anthropic;
        const next = messages[i + 1].anthropic;
        
        if (Array.isArray(current.content) && 
            current.content[0]?.type === 'tool_use') {
          if (!Array.isArray(next.content) || 
              next.content[0]?.type !== 'tool_result') {
            issues.push(`Tool use at index ${i} missing matching tool result`);
          }
        }
      }
      
      return { isValid: issues.length === 0, issues };
    } catch (error) {
      return { 
        isValid: false, 
        issues: [`Context validation failed: ${error.message}`] 
      };
    }
  }
}
Use the ContextWindow’s file tracking features to avoid redundant file reads and optimize agent performance.
Message validation only runs in development and test environments. Ensure your production code follows proper message ordering to avoid runtime issues.
I