import type { VerboseLevel } from "../auto-reply/thinking.js"; export type AgentEventStream = "lifecycle" | "tool" | "assistant" | "error" | (string & {}); export type AgentEventPayload = { runId: string; seq: number; stream: AgentEventStream; ts: number; data: Record; sessionKey?: string; }; export type AgentRunContext = { sessionKey?: string; verboseLevel?: VerboseLevel; }; // Keep per-run counters so streams stay strictly monotonic per runId. const seqByRun = new Map(); const listeners = new Set<(evt: AgentEventPayload) => void>(); const runContextById = new Map(); export function registerAgentRunContext(runId: string, context: AgentRunContext) { if (!runId) return; const existing = runContextById.get(runId); if (!existing) { runContextById.set(runId, { ...context }); return; } if (context.sessionKey && existing.sessionKey !== context.sessionKey) { existing.sessionKey = context.sessionKey; } if (context.verboseLevel && existing.verboseLevel !== context.verboseLevel) { existing.verboseLevel = context.verboseLevel; } } export function getAgentRunContext(runId: string) { return runContextById.get(runId); } export function clearAgentRunContext(runId: string) { runContextById.delete(runId); } export function resetAgentRunContextForTest() { runContextById.clear(); } export function emitAgentEvent(event: Omit) { const nextSeq = (seqByRun.get(event.runId) ?? 0) + 1; seqByRun.set(event.runId, nextSeq); const enriched: AgentEventPayload = { ...event, seq: nextSeq, ts: Date.now(), }; for (const listener of listeners) { try { listener(enriched); } catch { /* ignore */ } } } export function onAgentEvent(listener: (evt: AgentEventPayload) => void) { listeners.add(listener); return () => listeners.delete(listener); }