refactor: consolidate duplicate types and file I/O utilities

- Remove 4 duplicate file I/O functions from audit/utils.ts, re-export from utils/file-io.ts
- Consolidate AgentEndResult interface into new types/audit.ts
- Use exported AgentDefinition from types/agents.ts in session-manager.ts
- Rename AgentMetrics to AgentAuditMetrics to disambiguate from temporal/shared.ts
This commit is contained in:
ajmallesh
2026-02-16 12:08:51 -08:00
parent 8e4fafba99
commit ae69478541
6 changed files with 36 additions and 94 deletions
+1 -11
View File
@@ -17,21 +17,11 @@ import { MetricsTracker } from './metrics-tracker.js';
import { initializeAuditStructure, type SessionMetadata } from './utils.js';
import { formatTimestamp } from '../utils/formatting.js';
import { SessionMutex } from '../utils/concurrency.js';
import type { AgentEndResult } from '../types/index.js';
// Global mutex instance
const sessionMutex = new SessionMutex();
interface AgentEndResult {
attemptNumber: number;
duration_ms: number;
cost_usd: number;
success: boolean;
model?: string | undefined;
error?: string | undefined;
checkpoint?: string | undefined;
isFinalAttempt?: boolean | undefined;
}
/**
* AuditSession - Main audit system facade
*/
+5 -16
View File
@@ -18,7 +18,7 @@ import {
import { atomicWrite, readJson, fileExists } from '../utils/file-io.js';
import { formatTimestamp, calculatePercentage } from '../utils/formatting.js';
import { AGENT_PHASE_MAP, type PhaseName } from '../session-manager.js';
import type { AgentName } from '../types/index.js';
import type { AgentName, AgentEndResult } from '../types/index.js';
interface AttemptData {
attempt_number: number;
@@ -30,7 +30,7 @@ interface AttemptData {
error?: string | undefined;
}
interface AgentMetrics {
interface AgentAuditMetrics {
status: 'in-progress' | 'success' | 'failed';
attempts: AttemptData[];
final_duration_ms: number;
@@ -68,21 +68,10 @@ interface SessionData {
total_duration_ms: number;
total_cost_usd: number;
phases: Record<string, PhaseMetrics>;
agents: Record<string, AgentMetrics>;
agents: Record<string, AgentAuditMetrics>;
};
}
interface AgentEndResult {
attemptNumber: number;
duration_ms: number;
cost_usd: number;
success: boolean;
model?: string | undefined;
error?: string | undefined;
checkpoint?: string | undefined;
isFinalAttempt?: boolean | undefined;
}
interface ActiveTimer {
startTime: number;
attemptNumber: number;
@@ -326,9 +315,9 @@ export class MetricsTracker {
* Calculate phase-level metrics
*/
private calculatePhaseMetrics(
successfulAgents: Array<[string, AgentMetrics]>
successfulAgents: Array<[string, AgentAuditMetrics]>
): Record<string, PhaseMetrics> {
const phases: Record<PhaseName, AgentMetrics[]> = {
const phases: Record<PhaseName, AgentAuditMetrics[]> = {
'pre-recon': [],
'recon': [],
'vulnerability-analysis': [],
+4 -59
View File
@@ -15,6 +15,10 @@ import fs from 'fs/promises';
import path from 'path';
import { fileURLToPath } from 'url';
// Import and re-export file I/O utilities from canonical location
import { ensureDirectory, atomicWrite, readJson, fileExists } from '../utils/file-io.js';
export { ensureDirectory, atomicWrite, readJson, fileExists };
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
@@ -93,65 +97,6 @@ export function generateWorkflowLogPath(sessionMetadata: SessionMetadata): strin
return path.join(auditPath, 'workflow.log');
}
/**
* Ensure directory exists (idempotent, race-safe)
*/
export async function ensureDirectory(dirPath: string): Promise<void> {
try {
await fs.mkdir(dirPath, { recursive: true });
} catch (error) {
// Ignore EEXIST errors (race condition safe)
if ((error as NodeJS.ErrnoException).code !== 'EEXIST') {
throw error;
}
}
}
/**
* Atomic write using temp file + rename pattern
* Guarantees no partial writes or corruption on crash
*/
export async function atomicWrite(filePath: string, data: object | string): Promise<void> {
const tempPath = `${filePath}.tmp`;
const content = typeof data === 'string' ? data : JSON.stringify(data, null, 2);
try {
// Write to temp file
await fs.writeFile(tempPath, content, 'utf8');
// Atomic rename (POSIX guarantee: atomic on same filesystem)
await fs.rename(tempPath, filePath);
} catch (error) {
// Clean up temp file on failure
try {
await fs.unlink(tempPath);
} catch {
// Ignore cleanup errors
}
throw error;
}
}
/**
* Read and parse JSON file
*/
export async function readJson<T = unknown>(filePath: string): Promise<T> {
const content = await fs.readFile(filePath, 'utf8');
return JSON.parse(content) as T;
}
/**
* Check if file exists
*/
export async function fileExists(filePath: string): Promise<boolean> {
try {
await fs.access(filePath);
return true;
} catch {
return false;
}
}
/**
* Initialize audit directory structure for a session
* Creates: audit-logs/{sessionId}/, agents/, prompts/, deliverables/
+1 -8
View File
@@ -4,14 +4,7 @@
// it under the terms of the GNU Affero General Public License version 3
// as published by the Free Software Foundation.
import type { AgentName } from './types/index.js';
// Agent definition interface
interface AgentDefinition {
name: AgentName;
displayName: string;
prerequisites: AgentName[];
}
import type { AgentName, AgentDefinition } from './types/index.js';
// Agent definitions according to PRD
export const AGENTS: Readonly<Record<AgentName, AgentDefinition>> = Object.freeze({
+24
View File
@@ -0,0 +1,24 @@
// Copyright (C) 2025 Keygraph, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License version 3
// as published by the Free Software Foundation.
/**
* Audit system type definitions
*/
/**
* Result data passed to audit system when an agent execution ends.
* Used by both AuditSession and MetricsTracker.
*/
export interface AgentEndResult {
attemptNumber: number;
duration_ms: number;
cost_usd: number;
success: boolean;
model?: string | undefined;
error?: string | undefined;
checkpoint?: string | undefined;
isFinalAttempt?: boolean | undefined;
}
+1
View File
@@ -11,3 +11,4 @@
export * from './errors.js';
export * from './config.js';
export * from './agents.js';
export * from './audit.js';