/** * Trace Polling Service * * Manages polling for trace availability after a trace-mode run completes. * Traces take ~5 minutes to propagate to OpenSearch after agent execution. */ import { Span, EvaluationReport, AgentConfig } from '../../types/index.js'; export interface PollState { reportId: string; /** * Connector runId (Strategy B). OPTIONAL — REST-connector reports never * get one; correlation then relies on the sessionId/service-window hints * derived from the report (Strategies C/D). */ runId?: string; attempts: number; maxAttempts: number; intervalMs: number; lastAttempt: string | null; running: boolean; timerId?: ReturnType; agentConfig?: AgentConfig; } export interface PollCallbacks { onTracesFound: (spans: Span[], report: EvaluationReport) => Promise; onAttempt?: (attempt: number, maxAttempts: number) => void; onError: (error: Error) => void; /** * Fired when polling stops WITHOUT a verdict from this poller — e.g. the * report reached a terminal metricsStatus through another path (eager * judge, sibling server). Callers that wrap `startPolling` in a promise * (evaluationRunner.waitForTracesAndJudge) MUST resolve here or they hang. */ onStopped?: () => void; } /** * Trace Polling Manager * * Singleton that manages active polling for trace availability. * State is in-memory only - polling is short-lived (~10 min max). * * Polling runs in two places for redundancy: * - Server (experimentRunner.ts): Primary - starts immediately after agent execution * - Browser (RunDetailsContent.tsx): Recovery - starts when viewing a pending report */ declare class TracePollingManager { private polls; private callbacks; private completionPromises; /** * Start polling for traces for a specific report */ startPolling(reportId: string, runId: string | undefined, callbacks: PollCallbacks, options?: { intervalMs?: number; maxAttempts?: number; agentConfig?: AgentConfig; }): void; /** * Stop polling for a specific report. * If a completion promise exists (from startPollingAsync), it is rejected * so callers awaiting it are unblocked. */ stopPolling(reportId: string): void; /** * Get the state for a specific poll */ getState(reportId: string): PollState | undefined; /** * Get all active polls */ getAllActivePolls(): Map; /** * Start polling and return a Promise that resolves when polling completes. * This allows callers (e.g., benchmark runner) to await trace availability * instead of firing and forgetting. * * If polling is already active for this reportId, returns the existing * completion promise (no duplicate poll started). */ startPollingAsync(reportId: string, runId: string | undefined, callbacks: PollCallbacks, options?: { intervalMs?: number; maxAttempts?: number; agentConfig?: AgentConfig; }): Promise; /** * Fetch a report defensively: storage failures and missing docs both * resolve to null (callers treat null as "unknown — proceed"). */ private safeGetReport; /** * Write an evaluator-error patch ONLY if the report is still pending / * calculating. A report judged through another path (eager judge, another * server's poller) must never have its verdict clobbered by this poller's * timeout/error bookkeeping. */ private patchErrorIfStillPending; /** * Execute a single poll attempt */ private poll; /** * Build trajectory from spans with proper error handling */ private buildTrajectory; } export declare const tracePollingManager: TracePollingManager; export {}; //# sourceMappingURL=tracePoller.d.ts.map