'use strict'; const fs = require('fs'); const path = require('path'); const { randomUUID } = require('crypto'); const auth = require('./auth'); const identity = require('./identity'); const CHECKPOINTS_DIR = path.join(auth.getAgentIndexDir('dummy'), '..', '..', 'checkpoints'); function getAgentCheckpointsDir(agentId) { const dir = path.join(CHECKPOINTS_DIR, agentId); fs.mkdirSync(dir, { recursive: true }); return dir; } // Create a checkpoint at session end function createCheckpoint(agentId, sessionData) { const agentDir = getAgentCheckpointsDir(agentId); const sessionId = randomUUID(); const now = new Date().toISOString(); const checkpoint = { metadata: { agent_id: agentId, session_id: sessionId, started_at: sessionData.started_at || now, ended_at: now, duration_seconds: sessionData.duration_seconds || 0, checkpoint_version: '1.0' }, context: { current_focus: sessionData.current_focus || null, active_project_ids: sessionData.active_project_ids || [], conversation_summary: sessionData.conversation_summary || '', pending_items: sessionData.pending_items || [] }, recent_memories: { memory_ids: sessionData.memory_ids || [], last_recall_query: sessionData.last_recall_query || null }, identity_updates: { new_beliefs: sessionData.new_beliefs || [], new_skills: sessionData.new_skills || [], new_relationships: sessionData.new_relationships || [], drift_checks: sessionData.drift_checks || { passed: 0, failed: 0 } }, working_memory: { scratchpad: sessionData.scratchpad || '', open_files: sessionData.open_files || [], context_stack: sessionData.context_stack || [] } }; const filename = `checkpoint-${checkpoint.metadata.ended_at.replace(/:/g, '-')}.json`; const filepath = path.join(agentDir, filename); fs.writeFileSync(filepath, JSON.stringify(checkpoint, null, 2), 'utf8'); return checkpoint; } // Get the most recent checkpoint for an agent function getLatestCheckpoint(agentId) { const agentDir = getAgentCheckpointsDir(agentId); try { const files = fs.readdirSync(agentDir) .filter(f => f.startsWith('checkpoint-') && f.endsWith('.json')) .sort() .reverse(); if (files.length === 0) { return null; } const latestFile = path.join(agentDir, files[0]); const content = fs.readFileSync(latestFile, 'utf8'); return JSON.parse(content); } catch (err) { return null; } } // List all checkpoints for an agent function listCheckpoints(agentId) { const agentDir = getAgentCheckpointsDir(agentId); try { const files = fs.readdirSync(agentDir) .filter(f => f.startsWith('checkpoint-') && f.endsWith('.json')) .sort() .reverse(); return files.map(f => { const content = fs.readFileSync(path.join(agentDir, f), 'utf8'); const cp = JSON.parse(content); return { session_id: cp.metadata.session_id, ended_at: cp.metadata.ended_at, duration_seconds: cp.metadata.duration_seconds, summary: cp.context.conversation_summary?.substring(0, 100) || 'No summary' }; }); } catch (err) { return []; } } // Restore session (mark as session start) function restoreSession(agentId) { const id = identity.loadIdentity(agentId); if (!id) { throw new Error(`Identity not found for agent: ${agentId}`); } const checkpoint = getLatestCheckpoint(agentId); // Update identity state const now = new Date().toISOString(); identity.updateMutableState(agentId, { state: { last_session_at: now, accumulated_experience: (id.mutable.state?.accumulated_experience || 0) + 1 } }); return { restored: !!checkpoint, checkpoint, identity_updated: true }; } // Get session statistics for an agent function getSessionStats(agentId) { const checkpoints = listCheckpoints(agentId); const id = identity.loadIdentity(agentId); const totalSessions = checkpoints.length; const totalDuration = checkpoints.reduce((sum, cp) => sum + (cp.duration_seconds || 0), 0); const avgDuration = totalSessions > 0 ? totalDuration / totalSessions : 0; return { agent_id: agentId, total_sessions: totalSessions, total_duration_seconds: totalDuration, avg_session_duration_seconds: Math.round(avgDuration), accumulated_experience: id?.mutable.state?.accumulated_experience || 0, last_session_at: id?.mutable.state?.last_session_at || null, drift_checks: { passed: id?.mutable.state?.drift_checks_passed || 0, failed: id?.mutable.state?.drift_checks_failed || 0 } }; } module.exports = { createCheckpoint, getLatestCheckpoint, listCheckpoints, restoreSession, getSessionStats };