import { createHash } from 'node:crypto'; import type { NewAgentLog } from './agent-logs.js'; export type RuntimeAuditOperation = | 'session.list' | 'session.tree' | 'session.stream' | 'session.send' | 'session.attach' | 'session.terminate' | 'runtime.capabilities' | 'runtime.health' | 'runtime.transitional-capabilities'; export type RuntimeAuditOutcome = 'requested' | 'succeeded' | 'denied' | 'failed'; export type RuntimeAuditErrorCode = 'policy_denied' | 'provider_error'; /** * Deliberately metadata-only runtime audit record. It has no fields for message * content, credentials, approval references, tool arguments, or tool output. */ export interface RuntimeAuditEvent { providerId: string; operation: RuntimeAuditOperation; outcome: RuntimeAuditOutcome; actorId: string; tenantId: string; channelId: string; correlationId: string; resourceId?: string; durationMs?: number; errorCode?: RuntimeAuditErrorCode; } const SAFE_IDENTIFIER = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/; function safeIdentifier(value: string): string { if (SAFE_IDENTIFIER.test(value)) return value; return hashIdentifier(value); } function hashIdentifier(value: string): string { return `sha256:${createHash('sha256').update(value).digest('hex')}`; } /** * Converts a typed audit event into a durable log entry using an explicit * allowlist. Values that could carry credentials or untrusted content are * rejected before persistence or structured log emission. */ export function createRuntimeAuditLogEntry(event: RuntimeAuditEvent): NewAgentLog { const providerId = safeIdentifier(event.providerId); const actorId = safeIdentifier(event.actorId); const tenantId = safeIdentifier(event.tenantId); const channelId = safeIdentifier(event.channelId); const correlationId = safeIdentifier(event.correlationId); // Provider resource identifiers may be opaque or user-derived, so never persist them raw. const resourceId = event.resourceId ? hashIdentifier(event.resourceId) : undefined; const persistedUserId = SAFE_IDENTIFIER.test(event.actorId) ? event.actorId : null; if ( event.durationMs !== undefined && (!Number.isInteger(event.durationMs) || event.durationMs < 0) ) { throw new Error('Runtime audit duration must be a non-negative integer'); } return { sessionId: `runtime:${providerId}`, userId: persistedUserId, level: event.outcome === 'failed' ? 'error' : event.outcome === 'denied' ? 'warn' : 'info', category: event.operation.startsWith('session.') ? 'tool_use' : 'general', content: 'runtime.provider.audit', metadata: { providerId, operation: event.operation, outcome: event.outcome, actorId, tenantId, channelId, correlationId, ...(resourceId ? { resourceId } : {}), ...(event.durationMs !== undefined ? { durationMs: event.durationMs } : {}), ...(event.errorCode ? { errorCode: event.errorCode } : {}), }, }; }