/** * State schema for the Bandit Runner LangGraph agent */ export interface Command { command: string output: string exitCode: number timestamp: string duration: number level: number } export interface ThoughtLog { type: 'plan' | 'observation' | 'reasoning' | 'decision' content: string timestamp: string level: number metadata?: Record } export interface Checkpoint { level: number password: string timestamp: string commandCount: number state: Partial } export interface BanditAgentState { runId: string modelProvider: string modelName: string currentLevel: number targetLevel: number // Allow partial runs (e.g., 0-5 for testing) currentPassword: string nextPassword: string | null levelGoal: string commandHistory: Command[] thoughts: ThoughtLog[] status: 'planning' | 'executing' | 'validating' | 'advancing' | 'paused' | 'paused_for_user_action' | 'complete' | 'failed' retryCount: number maxRetries: number failureReasons: string[] lastCheckpoint: Checkpoint | null streamingMode: 'selective' | 'all_events' sshConnectionId: string | null startedAt: string completedAt: string | null error: string | null } export interface RunConfig { runId: string modelProvider: 'openrouter' modelName: string startLevel?: number // Always 0, optional for backwards compatibility endLevel: number maxRetries: number streamingMode: 'selective' | 'all_events' apiKey?: string } export interface AgentEvent { type: 'terminal_output' | 'agent_message' | 'level_complete' | 'run_complete' | 'error' | 'thinking' | 'tool_call' | 'user_action_required' | 'usage_update' data: { content?: string level?: number command?: string metadata?: Record reason?: 'max_retries' retryCount?: number maxRetries?: number message?: string totalTokens?: number totalCost?: number } timestamp: string } // Level goals from the system prompt export const LEVEL_GOALS: Record = { 0: "Read 'readme' file in home directory", 1: "Read '-' file (use 'cat ./-' or 'cat < -')", 2: "Find and read hidden file with spaces in name", 3: "Find file with specific permissions (non-executable, human-readable, 1033 bytes)", 4: "Find file in inhere directory that is human-readable", 5: "Find file owned by bandit7, group bandit6, 33 bytes in size", 6: "Find the only line in data.txt that occurs only once", 7: "Find password next to word 'millionth' in data.txt", 8: "Find password in one of the few human-readable strings", 9: "Extract password from file with '=' prefix", 10: "Decode base64 encoded data.txt", 11: "Decode ROT13 encoded data.txt", 12: "Decompress repeatedly compressed file (hexdump → gzip → bzip2 → tar)", 13: "Use sshkey.private to connect to bandit14 and read password", 14: "Submit current password to port 30000 on localhost", 15: "Submit current password to SSL service on port 30001", 16: "Find port with SSL and RSA private key, use key to login to bandit17", 17: "Find the one line that changed between passwords.old and passwords.new", 18: "Read readme file (shell is modified, use ssh with command)", 19: "Use setuid binary to read password", 20: "Use network daemon that echoes back password", 21: "Examine cron jobs and find password in output file", 22: "Find cron script that creates MD5 hash filename, read that file", 23: "Create script in cron-monitored directory to get password", 24: "Brute force 4-digit PIN with password on port 30002", 25: "Escape from restricted shell (more pager) to read password", 26: "Use setuid binary to execute commands as bandit27", 27: "Clone git repository and find password", 28: "Find password in git repository history/commits", 29: "Find password in git repository branches or tags", 30: "Find password in git tag", 31: "Push file to git repository, hook reveals password", 32: "Use allowed commands in restricted shell to read password", 33: "Final level - read completion message" }