/** Seer feed event from WS `seer_events` or GET /api/v1/seer/stream. */ export interface SeerEventRecord { id?: number; event_type: string; agent_id?: string; payload?: Record; ts?: string; } export function isPathTraceTimelineEvent(event: SeerEventRecord): boolean { return event.event_type === 'pathtrace_timeline'; } export function isOnionMinerLogEvent(event: SeerEventRecord): boolean { return event.event_type === 'onion_miner_log'; } export function onionMinerLogSummary(event: SeerEventRecord): string { if (!isOnionMinerLogEvent(event)) return ''; const p = event.payload ?? {}; const method = typeof p.method === 'string' ? p.method : 'branch'; const outcome = typeof p.outcome === 'string' ? p.outcome : 'update'; const depth = typeof p.contingency_depth === 'number' ? p.contingency_depth : undefined; const ghost = p.is_ghost === true ? ' · ghost' : ''; const hr = typeof p.hashrate === 'number' && p.hashrate > 0 ? ` · ${Math.round(p.hashrate)} H/s` : ''; if (outcome === 'exhausted') { return `Contingency exhausted${depth != null ? ` · depth ${depth}` : ''} — awaiting court branch params`; } if (outcome === 'strain_retired') { return 'Contingency hospice — strain retired after max exhaustion cycles'; } if (outcome === 'won' || outcome === 'ghost_won') { return `Contingency ${method} linked${hr}${ghost}${depth != null ? ` · depth ${depth}` : ''}`; } return `Contingency ${method} ${outcome}${ghost}${depth != null ? ` · depth ${depth}` : ''}`; } export function pathTraceTimelineSummary(event: SeerEventRecord): string { if (!isPathTraceTimelineEvent(event)) return ''; const p = event.payload ?? {}; const ev = typeof p.event === 'string' ? p.event : 'update'; const branch = p.branch as { persona?: string; status?: string; hashrate?: number } | undefined; if (ev === 'fork') return 'Path Tracer fork — ghost branches spawned'; if (ev === 'branch_won') { return `Path Tracer branch won (${branch?.persona ?? 'unknown'})${branch?.hashrate ? ` · ${Math.round(branch.hashrate)} H/s` : ''}`; } if (ev === 'merge') return `Path Tracer merged ${branch?.persona ?? 'winner'} into canonical timeline`; return `Path Tracer timeline ${ev}`; } export function isSurgicalReplayEvent(event: SeerEventRecord): boolean { return event.event_type === 'surgical_replay'; } export { isMiningSelfSurgeryEvent, miningSelfSurgerySummary } from './miningSelfSurgery'; export function surgicalReplaySummary(event: SeerEventRecord): string { if (!isSurgicalReplayEvent(event)) { return ''; } const p = event.payload ?? {}; const tier = typeof p.failed_tier === 'string' ? p.failed_tier : 'tier'; const fix = typeof p.fix_type === 'string' ? p.fix_type : 'fix'; const outcome = typeof p.outcome === 'string' ? p.outcome : ''; return `Surgical replay ${tier} → ${fix}${outcome ? ` (${outcome})` : ''}`; } export function mergeSeerEvents( existing: SeerEventRecord[], incoming: SeerEventRecord | SeerEventRecord[], ): SeerEventRecord[] { const batch = Array.isArray(incoming) ? incoming : [incoming]; const seen = new Set(existing.map((e) => `${e.id ?? ''}:${e.event_type}:${e.ts ?? ''}`)); const merged = [...existing]; for (const ev of batch) { const key = `${ev.id ?? ''}:${ev.event_type}:${ev.ts ?? ''}`; if (seen.has(key)) continue; seen.add(key); merged.unshift(ev); } return merged.slice(0, 200); }