// AGENT ACTION LOGGER
// Logs every action taken by agents for rollback/undo capability
// Creates snapshots and maintains action history
import { promises as fs } from 'fs';
import path from 'path';
import crypto from 'crypto';
export class AgentActionLogger {
constructor() {
this.logPath = 'C:/MCP/logs/agent-actions';
this.rollbackPath = 'C:/MCP/logs/rollback';
this.currentSession = null;
this.sessionActions = [];
}
// Start a new logging session
async startSession(agentName, taskDescription) {
const sessionId = this.generateSessionId();
this.currentSession = {
id: sessionId,
agentName: agentName,
taskDescription: taskDescription,
startTime: new Date().toISOString(),
actions: [],
status: 'active'
};
console.log(`📝 Session started: ${sessionId} - ${agentName}`);
return sessionId;
}
// Log an action with before/after state
async logAction(actionType, details, beforeState = null, afterState = null) {
if (!this.currentSession) {
throw new Error('No active session - call startSession() first');
}
const action = {
id: this.generateActionId(),
timestamp: new Date().toISOString(),
type: actionType,
details: details,
beforeState: beforeState,
afterState: afterState,
rollbackable: this.isRollbackable(actionType)
};
this.currentSession.actions.push(action);
this.sessionActions.push(action);
// Save incremental log
await this.saveIncrementalLog(action);
console.log(` ✓ Action logged: ${actionType}`);
return action.id;
}
// Log file creation
async logFileCreation(filePath, content) {
return await this.logAction('file_create', {
description: `Created file: ${filePath}`,
filePath: filePath,
fileSize: content.length
}, null, {
filePath: filePath,
content: content,
hash: this.hashContent(content)
});
}
// Log file modification
async logFileModification(filePath, oldContent, newContent) {
return await this.logAction('file_modify', {
description: `Modified file: ${filePath}`,
filePath: filePath,
changeSize: newContent.length - oldContent.length
}, {
filePath: filePath,
content: oldContent,
hash: this.hashContent(oldContent)
}, {
filePath: filePath,
content: newContent,
hash: this.hashContent(newContent)
});
}
// End session and save complete log
async endSession(status = 'completed') {
if (!this.currentSession) {
throw new Error('No active session to end');
}
this.currentSession.endTime = new Date().toISOString();
this.currentSession.status = status;
this.currentSession.totalActions = this.currentSession.actions.length;
const start = new Date(this.currentSession.startTime);
const end = new Date(this.currentSession.endTime);
this.currentSession.duration = (end - start) / 1000;
await this.saveCompleteSessionLog();
if (this.hasRollbackableActions()) {
await this.createRollbackSnapshot();
}
console.log(`✅ Session ended: ${this.currentSession.id}`);
const completedSession = this.currentSession;
this.currentSession = null;
this.sessionActions = [];
return completedSession;
}
// Create rollback snapshot
async createRollbackSnapshot() {
const snapshotId = `snapshot_${Date.now()}`;
const snapshot = {
id: snapshotId,
sessionId: this.currentSession.id,
createdAt: new Date().toISOString(),
agentName: this.currentSession.agentName,
rollbackableActions: this.currentSession.actions.filter(a => a.rollbackable)
};
const snapshotPath = path.join(this.rollbackPath, `${snapshotId}.json`);
await fs.writeFile(snapshotPath, JSON.stringify(snapshot, null, 2));
console.log(`📸 Rollback snapshot: ${snapshotId}`);
return snapshotId;
}
// Helper methods
isRollbackable(actionType) {
return ['file_create', 'file_modify', 'file_delete', 'dir_create'].includes(actionType);
}
hasRollbackableActions() {
return this.currentSession.actions.some(a => a.rollbackable);
}
generateSessionId() {
return `${Date.now()}_${Math.random().toString(36).substring(7)}`;
}
generateActionId() {
return crypto.randomBytes(8).toString('hex');
}
hashContent(content) {
return crypto.createHash('sha256').update(content).digest('hex');
}
getDailyLogFileName() {
const date = new Date().toISOString().split('T')[0];
return `actions_${date}.log`;
}
async saveIncrementalLog(action) {
const dailyLogFile = this.getDailyLogFileName();
const logPath = path.join(this.logPath, dailyLogFile);
const logEntry = JSON.stringify(action) + '\n';
await fs.appendFile(logPath, logEntry).catch(async () => {
await fs.writeFile(logPath, logEntry);
});
}
async saveCompleteSessionLog() {
const sessionLogPath = path.join(
this.logPath,
`session_${this.currentSession.id}.json`
);
await fs.writeFile(
sessionLogPath,
JSON.stringify(this.currentSession, null, 2)
);
}
}
export const actionLogger = new AgentActionLogger();