@probelabs/visor 0.1.181 → 0.1.182
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/defaults/code-talk.yaml +80 -14
- package/defaults/engineer.yaml +33 -15
- package/defaults/skills/code-explorer.yaml +5 -0
- package/dist/agent-protocol/a2a-frontend.d.ts +10 -0
- package/dist/agent-protocol/a2a-frontend.d.ts.map +1 -1
- package/dist/agent-protocol/task-evaluator.d.ts +52 -0
- package/dist/agent-protocol/task-evaluator.d.ts.map +1 -0
- package/dist/agent-protocol/task-store.d.ts +5 -3
- package/dist/agent-protocol/task-store.d.ts.map +1 -1
- package/dist/agent-protocol/tasks-cli-handler.d.ts.map +1 -1
- package/dist/agent-protocol/tasks-tui.d.ts +34 -0
- package/dist/agent-protocol/tasks-tui.d.ts.map +1 -0
- package/dist/agent-protocol/trace-serializer.d.ts +90 -0
- package/dist/agent-protocol/trace-serializer.d.ts.map +1 -0
- package/dist/agent-protocol/track-execution.d.ts +2 -0
- package/dist/agent-protocol/track-execution.d.ts.map +1 -1
- package/dist/cli-main.d.ts.map +1 -1
- package/dist/defaults/code-talk.yaml +80 -14
- package/dist/defaults/engineer.yaml +33 -15
- package/dist/defaults/skills/code-explorer.yaml +5 -0
- package/dist/docs/commands.md +57 -14
- package/dist/docs/configuration.md +2 -0
- package/dist/docs/guides/graceful-restart.md +178 -0
- package/dist/docs/observability.md +69 -0
- package/dist/docs/production-deployment.md +17 -0
- package/dist/email/polling-runner.d.ts +4 -0
- package/dist/email/polling-runner.d.ts.map +1 -1
- package/dist/generated/config-schema.d.ts +70 -6
- package/dist/generated/config-schema.d.ts.map +1 -1
- package/dist/generated/config-schema.json +73 -6
- package/dist/index.js +5006 -886
- package/dist/output/traces/{run-2026-03-17T13-58-29-402Z.ndjson → run-2026-03-18T19-02-50-465Z.ndjson} +84 -84
- package/dist/{traces/run-2026-03-17T13-59-10-403Z.ndjson → output/traces/run-2026-03-18T19-03-30-428Z.ndjson} +2037 -2037
- package/dist/providers/mcp-custom-sse-server.d.ts +4 -0
- package/dist/providers/mcp-custom-sse-server.d.ts.map +1 -1
- package/dist/runners/graceful-restart.d.ts +46 -0
- package/dist/runners/graceful-restart.d.ts.map +1 -0
- package/dist/runners/mcp-server-runner.d.ts +12 -0
- package/dist/runners/mcp-server-runner.d.ts.map +1 -1
- package/dist/runners/runner-factory.d.ts.map +1 -1
- package/dist/runners/runner-host.d.ts +12 -0
- package/dist/runners/runner-host.d.ts.map +1 -1
- package/dist/runners/runner.d.ts +12 -0
- package/dist/runners/runner.d.ts.map +1 -1
- package/dist/sdk/{a2a-frontend-IWOUJOIZ.mjs → a2a-frontend-4LP3MLTS.mjs} +47 -5
- package/dist/sdk/a2a-frontend-4LP3MLTS.mjs.map +1 -0
- package/dist/sdk/a2a-frontend-5J3UNFY4.mjs +1718 -0
- package/dist/sdk/a2a-frontend-5J3UNFY4.mjs.map +1 -0
- package/dist/sdk/{a2a-frontend-BDACLGMA.mjs → a2a-frontend-MU5EO2HZ.mjs} +35 -1
- package/dist/sdk/a2a-frontend-MU5EO2HZ.mjs.map +1 -0
- package/dist/sdk/{check-provider-registry-4YKTEDKF.mjs → check-provider-registry-MHXQGUNN.mjs} +7 -7
- package/dist/sdk/{check-provider-registry-4YFVBGYU.mjs → check-provider-registry-RRWCXSTG.mjs} +3 -3
- package/dist/sdk/{check-provider-registry-67ZLGDDQ.mjs → check-provider-registry-Y33CRFVD.mjs} +7 -7
- package/dist/sdk/{chunk-DGIH6EX3.mjs → chunk-4AXAVXG5.mjs} +151 -281
- package/dist/sdk/chunk-4AXAVXG5.mjs.map +1 -0
- package/dist/sdk/{chunk-VMVIM4JB.mjs → chunk-4I3TJ7UJ.mjs} +37 -7
- package/dist/sdk/chunk-4I3TJ7UJ.mjs.map +1 -0
- package/dist/sdk/{chunk-VXC2XNQJ.mjs → chunk-5J3DNRF7.mjs} +3 -3
- package/dist/sdk/{chunk-7YZSSO4X.mjs → chunk-6DPPP7LD.mjs} +10 -10
- package/dist/sdk/chunk-7ERVRLDV.mjs +296 -0
- package/dist/sdk/chunk-7ERVRLDV.mjs.map +1 -0
- package/dist/sdk/{chunk-4DVP6KVC.mjs → chunk-7Z2WHX2J.mjs} +71 -30
- package/dist/sdk/chunk-7Z2WHX2J.mjs.map +1 -0
- package/dist/sdk/chunk-ANUT54HW.mjs +1502 -0
- package/dist/sdk/chunk-ANUT54HW.mjs.map +1 -0
- package/dist/sdk/{chunk-J73GEFPT.mjs → chunk-DHETLQIX.mjs} +2 -2
- package/dist/sdk/{chunk-QGBASDYP.mjs → chunk-JCOSKBMP.mjs} +71 -30
- package/dist/sdk/chunk-JCOSKBMP.mjs.map +1 -0
- package/dist/sdk/chunk-MK7ONH47.mjs +739 -0
- package/dist/sdk/chunk-MK7ONH47.mjs.map +1 -0
- package/dist/sdk/chunk-QXT47ZHR.mjs +390 -0
- package/dist/sdk/chunk-QXT47ZHR.mjs.map +1 -0
- package/dist/sdk/chunk-V75NEIXL.mjs +296 -0
- package/dist/sdk/chunk-V75NEIXL.mjs.map +1 -0
- package/dist/sdk/chunk-ZOF5QT6U.mjs +5943 -0
- package/dist/sdk/chunk-ZOF5QT6U.mjs.map +1 -0
- package/dist/sdk/{config-TSA5FUOM.mjs → config-2STD74CJ.mjs} +2 -2
- package/dist/sdk/config-JE4HKTWW.mjs +16 -0
- package/dist/sdk/{failure-condition-evaluator-HTPB5FYW.mjs → failure-condition-evaluator-5DZYMCGW.mjs} +4 -4
- package/dist/sdk/failure-condition-evaluator-R6DCDJAV.mjs +18 -0
- package/dist/sdk/{github-frontend-3SDFCCKI.mjs → github-frontend-3PSCKPAJ.mjs} +4 -4
- package/dist/sdk/github-frontend-L3F5JXPJ.mjs +1394 -0
- package/dist/sdk/github-frontend-L3F5JXPJ.mjs.map +1 -0
- package/dist/sdk/{host-QE4L7UXE.mjs → host-54CHV2LW.mjs} +3 -3
- package/dist/sdk/{host-VBBSLUWG.mjs → host-WAU6CT42.mjs} +3 -3
- package/dist/sdk/{host-CVH2CSHM.mjs → host-X5ZZCEWN.mjs} +2 -2
- package/dist/sdk/{routing-YVMTKFDZ.mjs → routing-CVQT4KHX.mjs} +5 -5
- package/dist/sdk/routing-EBAE5SSO.mjs +26 -0
- package/dist/sdk/{schedule-tool-Z5VG67JK.mjs → schedule-tool-POY3CDZL.mjs} +7 -7
- package/dist/sdk/{schedule-tool-ADUXTCY7.mjs → schedule-tool-R2OAATUS.mjs} +7 -7
- package/dist/sdk/{schedule-tool-ZMX3Y7LF.mjs → schedule-tool-Z6QYL2B3.mjs} +3 -3
- package/dist/sdk/{schedule-tool-handler-N7UNABOA.mjs → schedule-tool-handler-J4NUETJ6.mjs} +3 -3
- package/dist/sdk/{schedule-tool-handler-PCERK6ZZ.mjs → schedule-tool-handler-JMAKHPI7.mjs} +7 -7
- package/dist/sdk/{schedule-tool-handler-QOJVFRB4.mjs → schedule-tool-handler-MWFUIQKR.mjs} +7 -7
- package/dist/sdk/sdk.d.mts +33 -0
- package/dist/sdk/sdk.d.ts +33 -0
- package/dist/sdk/sdk.js +2058 -342
- package/dist/sdk/sdk.js.map +1 -1
- package/dist/sdk/sdk.mjs +6 -6
- package/dist/sdk/task-evaluator-HLNXKKVV.mjs +1278 -0
- package/dist/sdk/task-evaluator-HLNXKKVV.mjs.map +1 -0
- package/dist/sdk/{trace-helpers-KXDOJWBL.mjs → trace-helpers-HL5FBX65.mjs} +3 -3
- package/dist/sdk/trace-helpers-WJXYVV4S.mjs +29 -0
- package/dist/sdk/trace-helpers-WJXYVV4S.mjs.map +1 -0
- package/dist/sdk/trace-reader-ZY77OFNM.mjs +266 -0
- package/dist/sdk/trace-reader-ZY77OFNM.mjs.map +1 -0
- package/dist/sdk/track-execution-MKIQXP2C.mjs +136 -0
- package/dist/sdk/track-execution-MKIQXP2C.mjs.map +1 -0
- package/dist/sdk/track-execution-YUXQ6WQH.mjs +136 -0
- package/dist/sdk/track-execution-YUXQ6WQH.mjs.map +1 -0
- package/dist/sdk/{workflow-check-provider-NTHC5ZBF.mjs → workflow-check-provider-SE5I7EMA.mjs} +7 -7
- package/dist/sdk/workflow-check-provider-SE5I7EMA.mjs.map +1 -0
- package/dist/sdk/{workflow-check-provider-SRIMWKLQ.mjs → workflow-check-provider-VKYGI5GK.mjs} +3 -3
- package/dist/sdk/workflow-check-provider-VKYGI5GK.mjs.map +1 -0
- package/dist/sdk/{workflow-check-provider-CJXW2Z4F.mjs → workflow-check-provider-YDGZRI3Z.mjs} +7 -7
- package/dist/sdk/workflow-check-provider-YDGZRI3Z.mjs.map +1 -0
- package/dist/slack/socket-runner.d.ts +12 -0
- package/dist/slack/socket-runner.d.ts.map +1 -1
- package/dist/teams/webhook-runner.d.ts +4 -0
- package/dist/teams/webhook-runner.d.ts.map +1 -1
- package/dist/telegram/polling-runner.d.ts +2 -0
- package/dist/telegram/polling-runner.d.ts.map +1 -1
- package/dist/traces/{run-2026-03-17T13-58-29-402Z.ndjson → run-2026-03-18T19-02-50-465Z.ndjson} +84 -84
- package/dist/{output/traces/run-2026-03-17T13-59-10-403Z.ndjson → traces/run-2026-03-18T19-03-30-428Z.ndjson} +2037 -2037
- package/dist/types/config.d.ts +33 -0
- package/dist/types/config.d.ts.map +1 -1
- package/dist/whatsapp/webhook-runner.d.ts +4 -0
- package/dist/whatsapp/webhook-runner.d.ts.map +1 -1
- package/package.json +2 -2
- package/dist/sdk/a2a-frontend-BDACLGMA.mjs.map +0 -1
- package/dist/sdk/a2a-frontend-IWOUJOIZ.mjs.map +0 -1
- package/dist/sdk/chunk-4DVP6KVC.mjs.map +0 -1
- package/dist/sdk/chunk-DGIH6EX3.mjs.map +0 -1
- package/dist/sdk/chunk-QGBASDYP.mjs.map +0 -1
- package/dist/sdk/chunk-VMVIM4JB.mjs.map +0 -1
- /package/dist/sdk/{check-provider-registry-4YFVBGYU.mjs.map → check-provider-registry-MHXQGUNN.mjs.map} +0 -0
- /package/dist/sdk/{check-provider-registry-4YKTEDKF.mjs.map → check-provider-registry-RRWCXSTG.mjs.map} +0 -0
- /package/dist/sdk/{check-provider-registry-67ZLGDDQ.mjs.map → check-provider-registry-Y33CRFVD.mjs.map} +0 -0
- /package/dist/sdk/{chunk-VXC2XNQJ.mjs.map → chunk-5J3DNRF7.mjs.map} +0 -0
- /package/dist/sdk/{chunk-7YZSSO4X.mjs.map → chunk-6DPPP7LD.mjs.map} +0 -0
- /package/dist/sdk/{chunk-J73GEFPT.mjs.map → chunk-DHETLQIX.mjs.map} +0 -0
- /package/dist/sdk/{config-TSA5FUOM.mjs.map → config-2STD74CJ.mjs.map} +0 -0
- /package/dist/sdk/{failure-condition-evaluator-HTPB5FYW.mjs.map → config-JE4HKTWW.mjs.map} +0 -0
- /package/dist/sdk/{routing-YVMTKFDZ.mjs.map → failure-condition-evaluator-5DZYMCGW.mjs.map} +0 -0
- /package/dist/sdk/{schedule-tool-ADUXTCY7.mjs.map → failure-condition-evaluator-R6DCDJAV.mjs.map} +0 -0
- /package/dist/sdk/{github-frontend-3SDFCCKI.mjs.map → github-frontend-3PSCKPAJ.mjs.map} +0 -0
- /package/dist/sdk/{host-CVH2CSHM.mjs.map → host-54CHV2LW.mjs.map} +0 -0
- /package/dist/sdk/{host-QE4L7UXE.mjs.map → host-WAU6CT42.mjs.map} +0 -0
- /package/dist/sdk/{host-VBBSLUWG.mjs.map → host-X5ZZCEWN.mjs.map} +0 -0
- /package/dist/sdk/{schedule-tool-Z5VG67JK.mjs.map → routing-CVQT4KHX.mjs.map} +0 -0
- /package/dist/sdk/{schedule-tool-ZMX3Y7LF.mjs.map → routing-EBAE5SSO.mjs.map} +0 -0
- /package/dist/sdk/{schedule-tool-handler-N7UNABOA.mjs.map → schedule-tool-POY3CDZL.mjs.map} +0 -0
- /package/dist/sdk/{schedule-tool-handler-PCERK6ZZ.mjs.map → schedule-tool-R2OAATUS.mjs.map} +0 -0
- /package/dist/sdk/{schedule-tool-handler-QOJVFRB4.mjs.map → schedule-tool-Z6QYL2B3.mjs.map} +0 -0
- /package/dist/sdk/{trace-helpers-KXDOJWBL.mjs.map → schedule-tool-handler-J4NUETJ6.mjs.map} +0 -0
- /package/dist/sdk/{workflow-check-provider-CJXW2Z4F.mjs.map → schedule-tool-handler-JMAKHPI7.mjs.map} +0 -0
- /package/dist/sdk/{workflow-check-provider-NTHC5ZBF.mjs.map → schedule-tool-handler-MWFUIQKR.mjs.map} +0 -0
- /package/dist/sdk/{workflow-check-provider-SRIMWKLQ.mjs.map → trace-helpers-HL5FBX65.mjs.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/agent-protocol/trace-serializer.ts","../../src/agent-protocol/task-evaluator.ts"],"sourcesContent":["/**\n * Trace serializer for LLM evaluation prompts and CLI display.\n *\n * Supports multiple trace backends:\n * 1. Grafana Tempo (via Grafana datasource proxy or direct Tempo API)\n * 2. Jaeger (via Jaeger HTTP API)\n * 3. Local NDJSON files (Visor's file-span-exporter OTEL format)\n *\n * Auto-detects the backend from environment variables:\n * - OTEL_EXPORTER_OTLP_ENDPOINT + VISOR_TELEMETRY_SINK=otlp → probe Grafana/Jaeger\n * - VISOR_TRACE_BACKEND=grafana|jaeger|file (explicit override)\n * - GRAFANA_URL, GRAFANA_TEMPO_DATASOURCE_ID → Grafana Tempo\n * - JAEGER_URL → Jaeger\n * - Falls back to local file scan\n */\n\nimport * as fs from 'fs';\nimport * as readline from 'readline';\nimport * as path from 'path';\nimport { logger } from '../logger';\n\n// ---------------------------------------------------------------------------\n// Trace backend configuration\n// ---------------------------------------------------------------------------\n\nexport interface TraceBackendConfig {\n type: 'grafana' | 'jaeger' | 'file' | 'auto';\n /** Grafana base URL, e.g. http://localhost:3000 */\n grafanaUrl?: string;\n /** Grafana Tempo datasource ID (default: auto-detect) */\n grafanaDatasourceId?: string | number;\n /** Jaeger base URL, e.g. http://localhost:16686 */\n jaegerUrl?: string;\n /** Local trace directory (default: output/traces) */\n traceDir?: string;\n /** Auth token for remote APIs */\n authToken?: string;\n}\n\nfunction resolveBackendConfig(overrides?: Partial<TraceBackendConfig>): TraceBackendConfig {\n const explicit = process.env.VISOR_TRACE_BACKEND as TraceBackendConfig['type'] | undefined;\n\n return {\n type: overrides?.type || explicit || 'auto',\n grafanaUrl: overrides?.grafanaUrl || process.env.GRAFANA_URL,\n grafanaDatasourceId: overrides?.grafanaDatasourceId || process.env.GRAFANA_TEMPO_DATASOURCE_ID,\n jaegerUrl: overrides?.jaegerUrl || process.env.JAEGER_URL,\n traceDir: overrides?.traceDir || process.env.VISOR_TRACE_DIR || 'output/traces',\n authToken: overrides?.authToken || process.env.GRAFANA_TOKEN,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Unified span type (normalized from all backends)\n// ---------------------------------------------------------------------------\n\ninterface NormalizedSpan {\n traceId: string;\n spanId: string;\n parentSpanId?: string;\n name: string;\n startTimeMs: number;\n endTimeMs: number;\n durationMs: number;\n attributes: Record<string, string | number | boolean>;\n events: Array<{ name: string; attributes: Record<string, string | number | boolean> }>;\n status: 'ok' | 'error';\n}\n\ninterface SpanTree {\n span: NormalizedSpan;\n children: SpanTree[];\n}\n\n// ---------------------------------------------------------------------------\n// OTLP response parsing (shared by Grafana Tempo and Jaeger)\n// ---------------------------------------------------------------------------\n\n/**\n * Parse OTLP JSON trace response (Tempo/Jaeger format) into normalized spans.\n * Format: { batches: [{ scopeSpans: [{ spans: [...] }] }] }\n * Jaeger variant: { data: [{ spans: [...] }] }\n */\nfunction parseOTLPResponse(data: any): NormalizedSpan[] {\n const spans: NormalizedSpan[] = [];\n\n // Grafana Tempo OTLP format\n if (data.batches) {\n for (const batch of data.batches) {\n for (const ss of batch.scopeSpans || []) {\n for (const s of ss.spans || []) {\n spans.push(normalizeOTLPSpan(s));\n }\n }\n }\n return spans;\n }\n\n // Jaeger native format\n if (data.data && Array.isArray(data.data)) {\n for (const trace of data.data) {\n for (const s of trace.spans || []) {\n spans.push(normalizeJaegerSpan(s, trace.traceID));\n }\n }\n return spans;\n }\n\n return spans;\n}\n\nfunction normalizeOTLPSpan(s: any): NormalizedSpan {\n const startNs = parseInt(s.startTimeUnixNano || '0', 10);\n const endNs = parseInt(s.endTimeUnixNano || '0', 10);\n\n // Decode base64 span/trace IDs if needed, or use hex strings\n const traceId = decodeOTLPId(s.traceId);\n const spanId = decodeOTLPId(s.spanId);\n const parentSpanId = s.parentSpanId ? decodeOTLPId(s.parentSpanId) : undefined;\n\n const attributes: Record<string, string | number | boolean> = {};\n for (const attr of s.attributes || []) {\n const val = attr.value;\n if (val.stringValue !== undefined) attributes[attr.key] = val.stringValue;\n else if (val.intValue !== undefined) attributes[attr.key] = parseInt(val.intValue, 10);\n else if (val.boolValue !== undefined) attributes[attr.key] = val.boolValue;\n else if (val.doubleValue !== undefined) attributes[attr.key] = val.doubleValue;\n }\n\n const events: NormalizedSpan['events'] = [];\n for (const evt of s.events || []) {\n const evtAttrs: Record<string, string | number | boolean> = {};\n for (const a of evt.attributes || []) {\n const v = a.value;\n if (v.stringValue !== undefined) evtAttrs[a.key] = v.stringValue;\n else if (v.intValue !== undefined) evtAttrs[a.key] = parseInt(v.intValue, 10);\n }\n events.push({ name: evt.name, attributes: evtAttrs });\n }\n\n return {\n traceId,\n spanId,\n parentSpanId,\n name: s.name || 'unknown',\n startTimeMs: startNs / 1e6,\n endTimeMs: endNs / 1e6,\n durationMs: (endNs - startNs) / 1e6,\n attributes,\n events,\n status: s.status?.code === 2 ? 'error' : 'ok',\n };\n}\n\nfunction normalizeJaegerSpan(s: any, traceId: string): NormalizedSpan {\n const attributes: Record<string, string | number | boolean> = {};\n for (const tag of s.tags || []) {\n attributes[tag.key] = tag.value;\n }\n\n const events: NormalizedSpan['events'] = [];\n for (const log of s.logs || []) {\n const evtAttrs: Record<string, string | number | boolean> = {};\n for (const f of log.fields || []) evtAttrs[f.key] = f.value;\n events.push({ name: (evtAttrs['event'] as string) || 'log', attributes: evtAttrs });\n }\n\n // Jaeger uses microseconds\n const startUs = s.startTime || 0;\n const durationUs = s.duration || 0;\n\n return {\n traceId,\n spanId: s.spanID,\n parentSpanId: s.references?.find((r: any) => r.refType === 'CHILD_OF')?.spanID,\n name: s.operationName || 'unknown',\n startTimeMs: startUs / 1000,\n endTimeMs: (startUs + durationUs) / 1000,\n durationMs: durationUs / 1000,\n attributes,\n events,\n status:\n attributes['otel.status_code'] === 'ERROR' || attributes['error'] === true ? 'error' : 'ok',\n };\n}\n\n/**\n * OTLP trace/span IDs may be base64-encoded. Decode to hex string.\n */\nfunction decodeOTLPId(id: string): string {\n if (!id) return '';\n // Already hex? (only hex chars)\n if (/^[0-9a-f]+$/i.test(id)) return id.toLowerCase();\n // Base64 → hex\n try {\n return Buffer.from(id, 'base64').toString('hex');\n } catch {\n return id;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Local NDJSON file parsing\n// ---------------------------------------------------------------------------\n\nfunction parseLocalNDJSONSpans(spans: any[]): NormalizedSpan[] {\n return spans.map(s => {\n const startMs = timeValueToMs(s.startTime || [0, 0]);\n const endMs = timeValueToMs(s.endTime || s.startTime || [0, 0]);\n const events: NormalizedSpan['events'] = (s.events || []).map((e: any) => ({\n name: e.name,\n attributes: e.attributes || {},\n }));\n\n return {\n traceId: s.traceId || '',\n spanId: s.spanId || '',\n parentSpanId: s.parentSpanId || undefined,\n name: s.name || 'unknown',\n startTimeMs: startMs,\n endTimeMs: endMs,\n durationMs: endMs - startMs,\n attributes: s.attributes || {},\n events,\n status: s.status?.code === 2 ? 'error' : 'ok',\n };\n });\n}\n\nfunction timeValueToMs(tv: [number, number]): number {\n return tv[0] * 1000 + tv[1] / 1e6;\n}\n\n// ---------------------------------------------------------------------------\n// Trace fetching — multi-backend\n// ---------------------------------------------------------------------------\n\n/**\n * Fetch trace spans by trace ID. Tries backends in order:\n * 1. Explicit backend (config.type)\n * 2. Grafana Tempo (auto-detect)\n * 3. Jaeger (auto-detect)\n * 4. Local NDJSON files\n */\nexport async function fetchTraceSpans(\n traceId: string,\n config?: Partial<TraceBackendConfig>\n): Promise<NormalizedSpan[]> {\n const cfg = resolveBackendConfig(config);\n\n const tryGrafana = cfg.type === 'grafana' || cfg.type === 'auto';\n const tryJaeger = cfg.type === 'jaeger' || cfg.type === 'auto';\n const tryFile = cfg.type === 'file' || cfg.type === 'auto';\n\n // 1. Grafana Tempo\n if (tryGrafana) {\n const spans = await fetchFromGrafanaTempo(traceId, cfg);\n if (spans && spans.length > 0) return spans;\n }\n\n // 2. Jaeger\n if (tryJaeger) {\n const spans = await fetchFromJaeger(traceId, cfg);\n if (spans && spans.length > 0) return spans;\n }\n\n // 3. Local files\n if (tryFile) {\n const spans = await fetchFromLocalFiles(traceId, cfg);\n if (spans && spans.length > 0) return spans;\n }\n\n return [];\n}\n\nasync function fetchFromGrafanaTempo(\n traceId: string,\n cfg: TraceBackendConfig\n): Promise<NormalizedSpan[] | null> {\n // Auto-detect Grafana URL from OTLP endpoint\n let grafanaUrl = cfg.grafanaUrl;\n if (!grafanaUrl) {\n const otlpEndpoint = process.env.OTEL_EXPORTER_OTLP_ENDPOINT;\n if (otlpEndpoint) {\n // Common pattern: OTLP on 4318, Grafana on 3000 or mapped port\n // Try common Grafana ports on same host\n const url = new URL(otlpEndpoint);\n const host = url.hostname;\n for (const port of ['3000', '8001', '80']) {\n try {\n const testUrl = `http://${host}:${port}/api/health`;\n const resp = await httpGet(testUrl, cfg.authToken, 2000);\n if (resp && resp.includes('\"database\"')) {\n grafanaUrl = `http://${host}:${port}`;\n break;\n }\n } catch {}\n }\n }\n }\n\n if (!grafanaUrl) return null;\n\n try {\n // Auto-detect Tempo datasource ID\n let dsId = cfg.grafanaDatasourceId;\n if (!dsId) {\n const dsResp = await httpGet(`${grafanaUrl}/api/datasources`, cfg.authToken);\n if (dsResp) {\n const datasources = JSON.parse(dsResp);\n const tempo = datasources.find((d: any) => d.type === 'tempo');\n if (tempo) dsId = tempo.id;\n }\n }\n if (!dsId) return null;\n\n const traceUrl = `${grafanaUrl}/api/datasources/proxy/${dsId}/api/traces/${traceId}`;\n const resp = await httpGet(traceUrl, cfg.authToken);\n if (!resp) return null;\n\n const data = JSON.parse(resp);\n return parseOTLPResponse(data);\n } catch (err) {\n logger.debug(`[TraceSerializer] Grafana Tempo fetch failed: ${err}`);\n return null;\n }\n}\n\nasync function fetchFromJaeger(\n traceId: string,\n cfg: TraceBackendConfig\n): Promise<NormalizedSpan[] | null> {\n let jaegerUrl = cfg.jaegerUrl;\n if (!jaegerUrl) {\n // Auto-detect: try common Jaeger ports on localhost\n const otlpEndpoint = process.env.OTEL_EXPORTER_OTLP_ENDPOINT;\n const host = otlpEndpoint ? new URL(otlpEndpoint).hostname : 'localhost';\n for (const port of ['16686']) {\n try {\n const testUrl = `http://${host}:${port}/api/services`;\n const resp = await httpGet(testUrl, undefined, 2000);\n if (resp && resp.includes('\"data\"')) {\n jaegerUrl = `http://${host}:${port}`;\n break;\n }\n } catch {}\n }\n }\n if (!jaegerUrl) return null;\n\n try {\n const traceUrl = `${jaegerUrl}/api/traces/${traceId}`;\n const resp = await httpGet(traceUrl, cfg.authToken);\n if (!resp) return null;\n\n const data = JSON.parse(resp);\n return parseOTLPResponse(data);\n } catch (err) {\n logger.debug(`[TraceSerializer] Jaeger fetch failed: ${err}`);\n return null;\n }\n}\n\nasync function fetchFromLocalFiles(\n traceId: string,\n cfg: TraceBackendConfig\n): Promise<NormalizedSpan[] | null> {\n const traceFile = await findTraceFile(traceId, cfg.traceDir);\n if (!traceFile) return null;\n\n try {\n const { parseNDJSONTrace } = await import('../debug-visualizer/trace-reader');\n const trace = await parseNDJSONTrace(traceFile);\n return parseLocalNDJSONSpans(trace.spans as any[]);\n } catch (err) {\n logger.debug(`[TraceSerializer] Local file parse failed: ${err}`);\n return null;\n }\n}\n\n// ---------------------------------------------------------------------------\n// HTTP helper\n// ---------------------------------------------------------------------------\n\nasync function httpGet(\n url: string,\n authToken?: string,\n timeoutMs?: number\n): Promise<string | null> {\n try {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), timeoutMs || 10000);\n\n const headers: Record<string, string> = {};\n if (authToken) headers['Authorization'] = `Bearer ${authToken}`;\n\n const resp = await fetch(url, {\n signal: controller.signal,\n headers,\n });\n clearTimeout(timeout);\n if (!resp.ok) return null;\n return await resp.text();\n } catch {\n return null;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Trace file discovery (local NDJSON files)\n// ---------------------------------------------------------------------------\n\n/**\n * Find the NDJSON trace file matching a given trace ID.\n * Scans the first line of each `.ndjson` file in the trace directory.\n */\nexport async function findTraceFile(traceId: string, traceDir?: string): Promise<string | null> {\n const dir = traceDir || process.env.VISOR_TRACE_DIR || 'output/traces';\n if (!fs.existsSync(dir)) return null;\n\n const files = fs.readdirSync(dir).filter(f => f.endsWith('.ndjson'));\n\n for (const file of files) {\n const filePath = path.join(dir, file);\n try {\n const firstLine = await readFirstLine(filePath);\n if (!firstLine) continue;\n const parsed = JSON.parse(firstLine);\n if (parsed.traceId === traceId) return filePath;\n } catch {\n // skip malformed files\n }\n }\n\n return null;\n}\n\nasync function readFirstLine(filePath: string): Promise<string | null> {\n return new Promise((resolve, reject) => {\n const stream = fs.createReadStream(filePath, { encoding: 'utf-8' });\n const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });\n let resolved = false;\n rl.on('line', (line: string) => {\n if (!resolved) {\n resolved = true;\n rl.close();\n stream.destroy();\n resolve(line.trim() || null);\n }\n });\n rl.on('close', () => {\n if (!resolved) resolve(null);\n });\n rl.on('error', reject);\n });\n}\n\n// ---------------------------------------------------------------------------\n// Noise filtering\n// ---------------------------------------------------------------------------\n\n/** Span names that are pure infrastructure noise and should be filtered out */\nconst NOISE_SPAN_NAMES = new Set([\n 'engine.state.init',\n 'engine.state.waveplanning',\n 'engine.state.planready',\n 'visor.sandbox.stopAll',\n]);\n\n/** Span names that are redundant wrappers — lift their children up */\nconst WRAPPER_SPAN_NAMES = new Set([\n 'engine.state.leveldispatch',\n 'visor.ai_check',\n 'probe.delegation',\n]);\n\nfunction isNoiseSpan(span: NormalizedSpan): boolean {\n return NOISE_SPAN_NAMES.has(span.name);\n}\n\nfunction isWrapperSpan(span: NormalizedSpan): boolean {\n return WRAPPER_SPAN_NAMES.has(span.name);\n}\n\n// ---------------------------------------------------------------------------\n// Build span tree from flat list (with noise filtering)\n// ---------------------------------------------------------------------------\n\nfunction buildSpanTree(spans: NormalizedSpan[]): SpanTree {\n // First filter out pure noise spans\n const filtered = spans.filter(s => !isNoiseSpan(s));\n\n const nodeMap = new Map<string, SpanTree>();\n\n // Create all nodes\n for (const span of filtered) {\n nodeMap.set(span.spanId, { span, children: [] });\n }\n\n // Build parent-child relationships\n let root: SpanTree | undefined;\n for (const span of filtered) {\n const node = nodeMap.get(span.spanId)!;\n if (!span.parentSpanId) {\n root = node;\n } else {\n // Walk up to find nearest non-filtered ancestor\n let parentId: string | undefined = span.parentSpanId;\n while (parentId && !nodeMap.has(parentId)) {\n // Parent was filtered — find grandparent from original spans\n const parentSpan = spans.find(s => s.spanId === parentId);\n parentId = parentSpan?.parentSpanId;\n }\n if (parentId) {\n const parent = nodeMap.get(parentId);\n if (parent) parent.children.push(node);\n } else if (!root) {\n root = node;\n }\n }\n }\n\n // If no root found, pick the span with the longest duration\n if (!root) {\n const sorted = [...nodeMap.values()].sort((a, b) => b.span.durationMs - a.span.durationMs);\n root = sorted[0] || { span: filtered[0], children: [] };\n }\n\n // Sort children by start time\n const sortChildren = (node: SpanTree) => {\n node.children.sort((a, b) => a.span.startTimeMs - b.span.startTimeMs);\n node.children.forEach(sortChildren);\n };\n sortChildren(root);\n\n // Unwrap wrapper spans — lift children up and discard the wrapper\n const unwrap = (node: SpanTree): SpanTree => {\n // Recursively process children first\n node.children = node.children.map(unwrap);\n\n // Flatten: replace wrapper children with their children\n const newChildren: SpanTree[] = [];\n for (const child of node.children) {\n if (isWrapperSpan(child.span)) {\n // Lift grandchildren up\n newChildren.push(...child.children);\n } else {\n newChildren.push(child);\n }\n }\n node.children = newChildren;\n return node;\n };\n unwrap(root);\n\n // Remove redundant probe.event.tool.result spans that are siblings of\n // search.delegate spans — these are aggregate results echoed back to the\n // parent AI and duplicate info already shown inside the delegate subtree\n const removeDelegateEchos = (node: SpanTree): void => {\n const hasDelegateChild = node.children.some(c => c.span.name === 'search.delegate');\n if (hasDelegateChild) {\n node.children = node.children.filter(c => {\n if (c.span.name !== 'probe.event.tool.result') return true;\n const toolName = c.span.attributes['tool.name'];\n // Remove search echoes — their details are inside search.delegate\n return toolName !== 'search';\n });\n }\n node.children.forEach(removeDelegateEchos);\n };\n removeDelegateEchos(root);\n\n return root;\n}\n\n// ---------------------------------------------------------------------------\n// Compact tree rendering\n// ---------------------------------------------------------------------------\n\nexport interface RenderTreeOptions {\n maxDepth?: number;\n maxChars?: number;\n /** Fallback intent text for AI spans whose input is too truncated */\n fallbackIntent?: string;\n /** When true, don't truncate output/intent previews */\n fullOutput?: boolean;\n /** Final task response from the task store (full, not OTEL-truncated) */\n taskResponse?: string;\n}\n\n/**\n * Tracks displayed outputs and intents to avoid repeating the same text.\n * When a duplicate is detected, shows \"= <first-span-name>\" instead.\n */\ninterface DeduplicationContext {\n /** Map from normalized output prefix → span name that first displayed it */\n outputs: Map<string, string>;\n /** Map from normalized intent prefix → span name that first displayed it */\n intents: Map<string, string>;\n}\n\n/** Normalize text for dedup comparison: lowercase, collapse whitespace, take prefix */\nfunction dedupeKey(text: string): string {\n return text.replace(/\\s+/g, ' ').trim().slice(0, 100).toLowerCase();\n}\n\n/** Check if text was already displayed; if so return the reference name. Otherwise register it. */\nfunction dedupeOrRegister(\n ctx: DeduplicationContext,\n kind: 'outputs' | 'intents',\n text: string,\n spanName: string\n): string | null {\n if (!text || text.length < 20) return null;\n const key = dedupeKey(text);\n if (!key) return null;\n const map = ctx[kind];\n const existing = map.get(key);\n if (existing && existing !== spanName) {\n return existing;\n }\n // Cross-check: intents against outputs and vice versa\n const otherMap = kind === 'outputs' ? ctx.intents : ctx.outputs;\n const crossRef = otherMap.get(key);\n if (crossRef && crossRef !== spanName) {\n map.set(key, spanName);\n return crossRef;\n }\n map.set(key, spanName);\n return null;\n}\n\n/**\n * Serialize a trace (by ID) into a compact text tree for LLM prompts.\n * Auto-detects the backend (Grafana Tempo, Jaeger, local files).\n */\nexport async function serializeTraceForPrompt(\n traceIdOrPath: string,\n maxChars?: number,\n backendConfig?: Partial<TraceBackendConfig>,\n /** Final task response from the task store (not truncated by OTEL) */\n taskResponse?: string\n): Promise<string> {\n let spans: NormalizedSpan[];\n\n // If it looks like a file path, read it directly\n if (traceIdOrPath.includes('/') || traceIdOrPath.endsWith('.ndjson')) {\n const { parseNDJSONTrace } = await import('../debug-visualizer/trace-reader');\n const trace = await parseNDJSONTrace(traceIdOrPath);\n spans = parseLocalNDJSONSpans(trace.spans as any[]);\n } else {\n // It's a trace ID — fetch from backends\n spans = await fetchTraceSpans(traceIdOrPath, backendConfig);\n }\n\n if (spans.length === 0) {\n return '(no trace data available)';\n }\n\n const tree = buildSpanTree(spans);\n\n // Extract the route-intent topic as a global fallback for AI intent\n const routeIntentTopic = extractRouteIntentTopic(spans);\n\n // maxChars > 100k signals \"full\" mode (no truncation of previews)\n const fullOutput = (maxChars ?? 4000) > 100000;\n\n return renderSpanYaml(tree, spans, {\n maxChars: maxChars ?? 4000,\n fallbackIntent: routeIntentTopic,\n fullOutput,\n taskResponse,\n });\n}\n\n/**\n * Render a span tree as a compact ASCII tree with durations and details.\n */\nexport function renderSpanTree(tree: SpanTree, opts?: RenderTreeOptions): string {\n const maxChars = opts?.maxChars ?? 4000;\n const maxDepth = opts?.maxDepth ?? 20;\n const lines: string[] = [];\n\n const dedup: DeduplicationContext = { outputs: new Map(), intents: new Map() };\n renderNode(\n tree,\n '',\n true,\n 0,\n maxDepth,\n lines,\n undefined,\n opts?.fallbackIntent,\n opts?.fullOutput,\n dedup\n );\n\n let result = lines.join('\\n');\n if (result.length > maxChars) {\n result = result.slice(0, maxChars - 20) + '\\n... (truncated)';\n }\n return result;\n}\n\n// Keep backward compat — renderTraceTree delegates to renderSpanTree with adapter\nexport function renderTraceTree(\n tree: any, // ExecutionNode from trace-reader\n opts?: RenderTreeOptions\n): string {\n // Convert ExecutionNode tree to SpanTree\n const convert = (node: any): SpanTree => ({\n span: {\n traceId: node.span?.traceId || '',\n spanId: node.span?.spanId || '',\n parentSpanId: node.span?.parentSpanId,\n name: node.span?.name || node.checkId || 'unknown',\n startTimeMs: timeValueToMs(node.span?.startTime || [0, 0]),\n endTimeMs: timeValueToMs(node.span?.endTime || [0, 0]),\n durationMs: node.span?.duration || 0,\n attributes: node.span?.attributes || {},\n events: (node.span?.events || []).map((e: any) => ({\n name: e.name,\n attributes: e.attributes || {},\n })),\n status: node.status === 'error' ? 'error' : 'ok',\n },\n children: (node.children || []).map(convert),\n });\n return renderSpanTree(convert(tree), opts);\n}\n\n// ---------------------------------------------------------------------------\n// YAML-based renderer\n// ---------------------------------------------------------------------------\n\n/**\n * Render a span tree as YAML-like structured output.\n * More readable than ASCII tree for JSON inputs/outputs.\n */\nexport function renderSpanYaml(\n tree: SpanTree,\n allSpans: NormalizedSpan[],\n opts?: RenderTreeOptions\n): string {\n const fullOutput = opts?.fullOutput ?? false;\n const maxLen = fullOutput ? 100000 : 120;\n const dedup: DeduplicationContext = { outputs: new Map(), intents: new Map() };\n const lines: string[] = [];\n\n renderYamlNode(tree, 0, lines, dedup, opts?.fallbackIntent, fullOutput, maxLen);\n\n // Append the final task response from the task store (not OTEL-truncated)\n if (opts?.taskResponse) {\n // Remove any trailing dedup line like \"output: = generate-response\"\n while (lines.length > 0 && /^\\s*output:\\s*=\\s*\\S+/.test(lines[lines.length - 1])) {\n lines.pop();\n }\n const ml = fullOutput ? 100000 : 500;\n const text = opts.taskResponse.replace(/\\*\\*/g, '').replace(/`/g, '').trim();\n if (fullOutput) {\n lines.push(' response: |');\n for (const line of text.split('\\n')) {\n lines.push(` ${line}`);\n }\n } else {\n const truncated = truncate(text.replace(/\\n/g, ' '), ml);\n lines.push(` response: ${truncated}`);\n if (text.length > ml) {\n lines.push(' # use --full for complete response');\n }\n }\n }\n\n return lines.join('\\n');\n}\n\nfunction renderYamlNode(\n node: SpanTree,\n indent: number,\n lines: string[],\n dedup: DeduplicationContext,\n fallbackIntent?: string,\n fullOutput?: boolean,\n maxLen?: number,\n parentSpan?: NormalizedSpan\n): void {\n const pad = ' '.repeat(indent);\n const attrs = node.span.attributes;\n const duration = formatDurationMs(node.span.durationMs);\n const name = node.span.name;\n const ml = maxLen ?? 120;\n\n // Helper: get display name — for AI spans, use parent check name\n const parentCheckId = parentSpan?.attributes['visor.check.id'];\n const parentCheckName = parentCheckId\n ? String(parentCheckId).replace(/^visor\\.check\\./, '')\n : undefined;\n const displayName =\n name === 'ai.request' && parentCheckName\n ? parentCheckName\n : String(attrs['visor.check.id'] || name).replace(/^visor\\.check\\./, '');\n\n // --- Tool calls ---\n const toolName = attrs['tool.name'] || attrs['visor.tool.name'];\n if (toolName) {\n const toolInput = extractToolInput(String(toolName), attrs);\n const toolResultLen = attrs['tool.result.length'] || attrs['tool.result.count'];\n const tn = String(toolName);\n // Detect \"no results\" for search tools: probe header is ~350-450 chars,\n // so result.length < 500 for search means no actual matches\n const isSearchTool = tn === 'search' || tn === 'searchCode' || tn === 'search_code';\n const numLen = toolResultLen ? Number(toolResultLen) : -1;\n const noResults = isSearchTool && numLen >= 0 && numLen < 500;\n const resultSize = noResults\n ? ' → no results'\n : toolResultLen\n ? ` → ${formatSize(numLen)}`\n : '';\n const successMark = attrs['tool.success'] === false ? ' ✗' : '';\n lines.push(`${pad}- ${tn}(${toolInput})${resultSize}${successMark}`);\n return;\n }\n\n // --- Search delegate ---\n if (name === 'search.delegate') {\n const query = attrs['search.query'] || '';\n lines.push(`${pad}search.delegate(\"${truncate(String(query), 80)}\") — ${duration}:`);\n for (const child of node.children) {\n renderYamlNode(\n child,\n indent + 1,\n lines,\n dedup,\n fallbackIntent,\n fullOutput,\n maxLen,\n node.span\n );\n }\n return;\n }\n\n // --- AI request ---\n if (name === 'ai.request') {\n const model = attrs['ai.model'] || attrs['gen_ai.request.model'] || '?';\n const tokensIn = attrs['ai.input_length'] || attrs['gen_ai.usage.input_tokens'] || '';\n const tokensOut = attrs['gen_ai.usage.output_tokens'] || '';\n const tokenParts: string[] = [];\n if (tokensIn) tokenParts.push(`${tokensIn} in`);\n if (tokensOut) tokenParts.push(`${tokensOut} out`);\n const tokenStr = tokenParts.length > 0 ? ` — ${tokenParts.join(', ')}` : '';\n\n const hasChildren = node.children.length > 0;\n lines.push(`${pad}ai: ${model} — ${duration}${tokenStr}${hasChildren ? ':' : ''}`);\n\n // Intent\n const aiInput = String(attrs['ai.input'] || '');\n let intent = extractAIIntent(aiInput, ml);\n if (!intent && parentSpan) {\n const promptPreview = String(\n parentSpan.attributes['visor.provider.request.prompt.preview'] || ''\n );\n if (promptPreview) intent = extractAIIntent(promptPreview, ml);\n if (!intent) {\n const inputOutputs = String(parentSpan.attributes['visor.check.input.outputs'] || '');\n if (inputOutputs) {\n try {\n const o = JSON.parse(inputOutputs);\n const t = o['route-intent']?.topic;\n if (t) intent = truncate(String(t), ml);\n } catch {}\n }\n }\n }\n if (!intent && fallbackIntent && parentSpan?.name !== 'search.delegate') {\n intent = fallbackIntent;\n }\n if (intent) {\n const intentRef = dedupeOrRegister(dedup, 'intents', intent, displayName);\n if (intentRef) {\n lines.push(`${pad} intent: = ${intentRef}`);\n } else {\n lines.push(`${pad} intent: ${intent}`);\n }\n }\n\n // Children (tool calls etc.)\n for (const child of node.children) {\n renderYamlNode(\n child,\n indent + 1,\n lines,\n dedup,\n fallbackIntent,\n fullOutput,\n maxLen,\n node.span\n );\n }\n\n // Output from parent check span (AI provider stores output on the check span, not ai.request)\n if (parentSpan) {\n const checkOutput = String(parentSpan.attributes['visor.check.output'] || '');\n if (checkOutput) {\n renderYamlOutput(\n checkOutput,\n `${pad} `,\n 'output',\n displayName,\n dedup,\n lines,\n fullOutput,\n ml\n );\n }\n }\n return;\n }\n\n // --- visor.run ---\n if (name === 'visor.run') {\n const source = attrs['visor.run.source'] || '';\n const visorVersion = attrs['visor.version'] || '';\n const probeVersion = attrs['probe.version'] || '';\n const slackUser = attrs['slack.user_id'] || '';\n lines.push(`${pad}visor.run:`);\n lines.push(`${pad} trace_id: ${node.span.traceId}`);\n if (visorVersion) lines.push(`${pad} visor: ${visorVersion}`);\n if (probeVersion) lines.push(`${pad} probe: ${probeVersion}`);\n if (source) lines.push(`${pad} source: ${source}`);\n if (slackUser) lines.push(`${pad} slack_user: ${slackUser}`);\n lines.push(`${pad} duration: ${duration}`);\n for (const child of node.children) {\n renderYamlNode(\n child,\n indent + 1,\n lines,\n dedup,\n fallbackIntent,\n fullOutput,\n maxLen,\n node.span\n );\n }\n return;\n }\n\n // --- Visor check ---\n const checkId = attrs['visor.check.id'];\n const checkType = attrs['visor.check.type'];\n if (checkId || name.startsWith('visor.check.')) {\n const cleanName = String(checkId || name).replace(/^visor\\.check\\./, '');\n const errMark = node.span.status === 'error' ? ' ✗' : '';\n lines.push(`${pad}${cleanName}:${errMark}`);\n if (checkType) lines.push(`${pad} type: ${checkType}`);\n lines.push(`${pad} duration: ${duration}`);\n\n // Input — show the actual question/topic first, then dependency outputs\n const inputContext = String(attrs['visor.check.input.context'] || '');\n const inputOutputs = String(attrs['visor.check.input.outputs'] || '');\n const question = extractQuestionFromContext(inputContext, inputOutputs);\n if (question || (inputOutputs && inputOutputs !== '{}')) {\n renderYamlInput(inputOutputs, `${pad} `, lines, fullOutput, ml, question);\n }\n\n // Children\n for (const child of node.children) {\n renderYamlNode(\n child,\n indent + 1,\n lines,\n dedup,\n fallbackIntent,\n fullOutput,\n maxLen,\n node.span\n );\n }\n\n // Output (after children)\n // Skip if check has a direct AI child — it already rendered this check's output\n const hasDirectAiChild = node.children.some(c => c.span.name === 'ai.request');\n if (!hasDirectAiChild) {\n const output = String(attrs['visor.check.output'] || '');\n if (output) {\n renderYamlOutput(output, `${pad} `, 'output', cleanName, dedup, lines, fullOutput, ml);\n }\n }\n return;\n }\n\n // --- Generic span ---\n const errMark = node.span.status === 'error' ? ' ✗' : '';\n const hasChildren = node.children.length > 0;\n lines.push(`${pad}${name} — ${duration}${errMark}${hasChildren ? ':' : ''}`);\n for (const child of node.children) {\n renderYamlNode(child, indent + 1, lines, dedup, fallbackIntent, fullOutput, maxLen, node.span);\n }\n}\n\n/**\n * Render a JSON output as YAML key-value pairs under a given prefix.\n * Handles deduplication — if the output was already shown, prints \"output: = <name>\".\n */\nfunction renderYamlOutput(\n rawOutput: string,\n pad: string,\n label: string,\n spanName: string,\n dedup: DeduplicationContext,\n lines: string[],\n fullOutput?: boolean,\n maxLen?: number\n): void {\n const ml = maxLen ?? 120;\n\n // Try to parse as JSON for structured display\n let obj: any;\n try {\n obj = JSON.parse(rawOutput);\n } catch {\n // Truncated JSON — use tolerant parser to extract what we can\n obj = parseTruncatedJson(rawOutput);\n if (!obj || typeof obj !== 'object') return;\n }\n\n // Unwrap single-key wrapper objects: {answer: {text: \"...\"}} → {text: \"...\"}\n // and {text: \"...\"} → render text inline\n if (typeof obj === 'object' && !Array.isArray(obj)) {\n const keys = Object.keys(obj);\n if (keys.length === 1 && typeof obj[keys[0]] === 'object' && obj[keys[0]] !== null) {\n obj = obj[keys[0]]; // unwrap {answer: {...}} → {...}\n }\n // If single text key, render inline\n const objKeys = Object.keys(obj);\n if (objKeys.length === 1 && objKeys[0] === 'text' && typeof obj.text === 'string') {\n const text = obj.text.replace(/\\*\\*/g, '').replace(/`/g, '').trim();\n const flat = text.replace(/\\n/g, ' ');\n const preview2 = fullOutput ? flat : truncate(flat, ml);\n const ref2 = dedupeOrRegister(dedup, 'outputs', truncate(flat, 100), spanName);\n if (ref2) {\n lines.push(`${pad}${label}: = ${ref2}`);\n } else {\n lines.push(`${pad}${label}: ${preview2}`);\n }\n return;\n }\n }\n\n // Get a dedup key from the formatted preview\n const preview = formatJsonPreview(obj, 200);\n if (!preview) return;\n const ref = dedupeOrRegister(dedup, 'outputs', preview, spanName);\n if (ref) {\n lines.push(`${pad}${label}: = ${ref}`);\n return;\n }\n\n // Render as YAML structure\n renderYamlValue(obj, pad, label, lines, fullOutput, ml);\n}\n\n/**\n * Render a JSON value as YAML structure.\n */\nfunction renderYamlValue(\n val: any,\n pad: string,\n key: string,\n lines: string[],\n fullOutput?: boolean,\n maxLen?: number,\n depth?: number\n): void {\n const ml = maxLen ?? 120;\n const d = depth ?? 0;\n\n if (val === null || val === undefined) return;\n\n if (typeof val === 'boolean' || typeof val === 'number') {\n lines.push(`${pad}${key}: ${val}`);\n return;\n }\n\n if (typeof val === 'string') {\n // Skip stringified JSON\n if (val.startsWith('{') || val.startsWith('[')) return;\n const clean = val.replace(/\\*\\*/g, '').replace(/`/g, '').trim();\n if (fullOutput && clean.length > 100 && clean.includes('\\n')) {\n // Multiline YAML\n lines.push(`${pad}${key}: |`);\n for (const line of clean.split('\\n').slice(0, fullOutput ? 500 : 5)) {\n lines.push(`${pad} ${line}`);\n }\n } else {\n const flat = clean.replace(/\\n/g, ' ');\n const truncVal = fullOutput ? flat : truncate(flat, ml);\n lines.push(`${pad}${key}: ${truncVal}`);\n }\n return;\n }\n\n if (Array.isArray(val)) {\n if (val.length === 0) {\n lines.push(`${pad}${key}: []`);\n return;\n }\n // Limit array depth\n const maxItems = fullOutput ? 20 : 3;\n lines.push(`${pad}${key}:`);\n for (let i = 0; i < Math.min(val.length, maxItems); i++) {\n const item = val[i];\n if (typeof item === 'object' && item !== null) {\n const entries = Object.entries(item).filter(\n ([k]) => k !== 'raw' && k !== 'skills' && k !== 'tags'\n );\n if (entries.length > 0) {\n // Put first key on same line as dash\n const [firstKey, firstVal] = entries[0];\n if (firstVal === null || firstVal === undefined || typeof firstVal !== 'object') {\n const sv =\n typeof firstVal === 'string'\n ? fullOutput\n ? firstVal.split('\\n')[0]\n : truncate(firstVal.split('\\n')[0], ml)\n : String(firstVal ?? '');\n lines.push(`${pad} - ${firstKey}: ${sv}`);\n } else {\n // Complex first value — render key on dash line, value below\n lines.push(`${pad} - ${firstKey}:`);\n for (const [ck, cv] of Object.entries(firstVal)) {\n if (ck === 'raw' || ck === 'skills' || ck === 'tags') continue;\n renderYamlValue(cv, `${pad} `, ck, lines, fullOutput, ml, d + 2);\n }\n }\n for (let j = 1; j < entries.length; j++) {\n const [k, v] = entries[j];\n renderYamlValue(v, `${pad} `, k, lines, fullOutput, ml, d + 1);\n }\n }\n } else {\n lines.push(`${pad} - ${truncate(String(item), ml)}`);\n }\n }\n if (val.length > maxItems) {\n lines.push(`${pad} # ... ${val.length - maxItems} more`);\n }\n return;\n }\n\n if (typeof val === 'object') {\n // Limit nesting depth\n if (d > 3) {\n const keys = Object.keys(val);\n lines.push(`${pad}${key}: {${keys.slice(0, 4).join(', ')}${keys.length > 4 ? ', ...' : ''}}`);\n return;\n }\n lines.push(`${pad}${key}:`);\n for (const [k, v] of Object.entries(val)) {\n if (k === 'raw' || k === 'skills' || k === 'tags') continue;\n renderYamlValue(v, `${pad} `, k, lines, fullOutput, ml, d + 1);\n }\n }\n}\n\n/**\n * Extract the actual user question/topic from the check's input context.\n * The context contains the full execution context including outputs from\n * dependency checks. We look for the route-intent topic, args.topic, or\n * other indicators of the user's question.\n */\nfunction extractQuestionFromContext(\n contextStr: string,\n inputOutputsStr: string\n): string | undefined {\n if (!contextStr) return undefined;\n\n try {\n const ctx = JSON.parse(contextStr);\n\n // 1. Check outputs.route-intent.topic (most common)\n const outputs = ctx.outputs || {};\n const routeIntent = outputs['route-intent'];\n if (routeIntent) {\n const topic = routeIntent.topic || routeIntent.intent || routeIntent.question;\n if (topic && typeof topic === 'string') return topic;\n // route-intent might be a string itself\n if (typeof routeIntent === 'string') return routeIntent;\n }\n\n // 2. Check args.topic or args.question\n const args = ctx.args || {};\n if (args.topic && typeof args.topic === 'string') return args.topic;\n if (args.question && typeof args.question === 'string') return args.question;\n if (args.intent && typeof args.intent === 'string') return args.intent;\n\n // 3. Check any output with a topic field not already in inputOutputs\n for (const key of Object.keys(outputs)) {\n const val = outputs[key];\n if (typeof val === 'object' && val !== null) {\n if (val.topic && typeof val.topic === 'string') {\n // Skip if this output is already shown in inputOutputs\n try {\n const depOutputs = JSON.parse(inputOutputsStr);\n if (depOutputs[key]) continue;\n } catch {}\n return val.topic;\n }\n }\n }\n } catch {\n // Truncated JSON — try regex extraction\n const topicMatch = contextStr.match(/\"topic\"\\s*:\\s*\"([^\"]+)\"/);\n if (topicMatch) return topicMatch[1];\n }\n\n return undefined;\n}\n\n/**\n * Render input dependencies as YAML.\n */\nfunction renderYamlInput(\n inputOutputsStr: string,\n pad: string,\n lines: string[],\n fullOutput?: boolean,\n maxLen?: number,\n question?: string\n): void {\n const ml = maxLen ?? 120;\n\n // Show the question/topic prominently first\n if (question) {\n lines.push(`${pad}input: ${truncate(question, fullOutput ? 100000 : ml)}`);\n }\n\n try {\n const inputs = JSON.parse(inputOutputsStr);\n if (typeof inputs !== 'object' || inputs === null) return;\n const keys = Object.keys(inputs);\n if (keys.length === 0) return;\n\n if (!question) lines.push(`${pad}input:`);\n for (const key of keys) {\n const val = inputs[key];\n if (typeof val === 'object' && val !== null) {\n renderYamlValue(val, `${pad} `, key, lines, fullOutput, ml, 0);\n } else {\n lines.push(`${pad} ${key}: ${truncate(String(val), ml)}`);\n }\n }\n } catch {\n // Can't parse — skip\n }\n}\n\n// ---------------------------------------------------------------------------\n// ASCII tree renderer (kept for backward compat / tests)\n// ---------------------------------------------------------------------------\n\nfunction renderNode(\n node: SpanTree,\n prefix: string,\n isLast: boolean,\n depth: number,\n maxDepth: number,\n lines: string[],\n parentSpan?: NormalizedSpan,\n fallbackIntent?: string,\n fullOutput?: boolean,\n dedup?: DeduplicationContext\n): void {\n if (depth > maxDepth) return;\n\n const hasChildren = node.children.length > 0;\n const connector = depth === 0 ? '' : isLast ? '└── ' : '├── ';\n const { line: formatted, output: deferredOutput } = formatSpanLine(\n node.span,\n parentSpan,\n fallbackIntent,\n fullOutput,\n dedup,\n hasChildren\n );\n\n // Skip if formatSpanLine returns empty (filtered)\n if (formatted) {\n lines.push(`${prefix}${connector}${formatted}`);\n }\n\n const childPrefix = depth === 0 ? '' : formatted ? prefix + (isLast ? ' ' : '│ ') : prefix; // no indent increase if this node was skipped\n\n for (let i = 0; i < node.children.length; i++) {\n const isChildLast = i === node.children.length - 1 && !deferredOutput;\n renderNode(\n node.children[i],\n childPrefix,\n isChildLast,\n depth + (formatted ? 1 : 0),\n maxDepth,\n lines,\n node.span,\n fallbackIntent,\n fullOutput,\n dedup\n );\n }\n\n // Show deferred output at the bottom, after all children\n if (deferredOutput) {\n lines.push(`${childPrefix}└─→ ${deferredOutput}`);\n }\n}\n\ninterface FormatResult {\n line: string;\n /** Output to show after children (deferred to bottom) */\n output?: string;\n}\n\nfunction formatSpanLine(\n span: NormalizedSpan,\n parentSpan?: NormalizedSpan,\n fallbackIntent?: string,\n fullOutput?: boolean,\n dedup?: DeduplicationContext,\n hasChildren?: boolean\n): FormatResult {\n const duration = formatDurationMs(span.durationMs);\n const attrs = span.attributes;\n const name = span.name;\n const previewLimit = fullOutput ? 10000 : 120;\n\n // Helper: get a display name for this span (used in dedup references)\n const displayName = String(attrs['visor.check.id'] || name).replace(/^visor\\.check\\./, '');\n\n // Helper: deduplicate an output string — returns the display string (without → prefix)\n const dedupeOutputStr = (rawOutput: string, label: string): string => {\n if (!rawOutput) return '';\n if (!dedup) return rawOutput;\n const ref = dedupeOrRegister(dedup, 'outputs', rawOutput, label);\n if (ref) return `(= ${ref})`;\n return rawOutput;\n };\n\n // Helper: deduplicate an intent string — returns the display string\n const dedupeIntentStr = (rawIntent: string, label: string): string => {\n if (!rawIntent || !dedup) return rawIntent ? ` 💬 ${rawIntent}` : '';\n const ref = dedupeOrRegister(dedup, 'intents', rawIntent, label);\n if (ref) return ` 💬 (= ${ref})`;\n return ` 💬 ${rawIntent}`;\n };\n\n // Helper: format output as inline or deferred depending on whether node has children\n const makeResult = (line: string, outputPreview: string): FormatResult => {\n if (!outputPreview) return { line };\n if (hasChildren) {\n // Defer output to bottom — show after children\n return { line, output: outputPreview };\n }\n // Inline for leaf nodes\n return { line: `${line} → ${outputPreview}` };\n };\n\n // --- Tool calls: extract real inputs ---\n const toolName = attrs['tool.name'] || attrs['visor.tool.name'];\n if (toolName) {\n const toolInput = extractToolInput(String(toolName), attrs);\n const toolResultLen = attrs['tool.result.length'] || attrs['tool.result.count'];\n const toolSuccess = attrs['tool.success'];\n const tn = String(toolName);\n const isSearchTool = tn === 'search' || tn === 'searchCode' || tn === 'search_code';\n const numLen = toolResultLen ? Number(toolResultLen) : -1;\n const noResults = isSearchTool && numLen >= 0 && numLen < 500;\n const resultSize = noResults ? 'no results' : toolResultLen ? formatSize(numLen) : '';\n const successMark = toolSuccess === false ? ' ✗' : '';\n const durStr =\n Number(attrs['tool.duration_ms']) > 0\n ? ` (${formatDurationMs(Number(attrs['tool.duration_ms']))})`\n : '';\n return {\n line: `${toolName}(${toolInput})${durStr}${resultSize ? ` → ${resultSize}` : ''}${successMark}`,\n };\n }\n\n // --- Search delegate: show the search query ---\n if (name === 'search.delegate') {\n const query = attrs['search.query'] || '';\n return { line: `search.delegate(${truncate(String(query), 80)}) (${duration})` };\n }\n\n // --- AI request: show model + input summary ---\n if (name === 'ai.request') {\n const model = attrs['ai.model'] || attrs['gen_ai.request.model'] || '?';\n const tokensIn = attrs['ai.input_length'] || attrs['gen_ai.usage.input_tokens'] || '';\n const tokensOut = attrs['gen_ai.usage.output_tokens'] || '';\n const tokenParts: string[] = [];\n if (tokensIn) tokenParts.push(`${tokensIn} in`);\n if (tokensOut) tokenParts.push(`${tokensOut} out`);\n const tokenStr = tokenParts.length > 0 ? ` [${tokenParts.join(', ')}]` : '';\n\n // Extract a short intent/question from ai.input — inline on same line\n const aiInput = String(attrs['ai.input'] || '');\n let intent = extractAIIntent(aiInput, previewLimit);\n\n // Fallback: if ai.input was truncated, try parent check's attributes\n if (!intent && parentSpan) {\n // Try prompt preview first\n const promptPreview = String(\n parentSpan.attributes['visor.provider.request.prompt.preview'] || ''\n );\n if (promptPreview) {\n intent = extractAIIntent(promptPreview, previewLimit);\n }\n // Try extracting topic from parent check's input context (route-intent output)\n if (!intent) {\n const inputOutputs = String(parentSpan.attributes['visor.check.input.outputs'] || '');\n if (inputOutputs) {\n try {\n const outputs = JSON.parse(inputOutputs);\n const topic = outputs['route-intent']?.topic;\n if (topic) intent = truncate(String(topic), 150);\n } catch {}\n }\n }\n }\n // Global fallback: use the route-intent topic — but only for top-level AI,\n // not for sub-delegates whose intent is the search.delegate query\n if (!intent && fallbackIntent && parentSpan?.name !== 'search.delegate') {\n intent = fallbackIntent;\n }\n const intentStr = intent ? dedupeIntentStr(intent, displayName) : '';\n\n // Get output preview from parent check's output\n let outputPreview = '';\n if (parentSpan) {\n const checkOutput = String(parentSpan.attributes['visor.check.output'] || '');\n if (checkOutput) {\n const preview = extractOutputPreview(checkOutput, previewLimit);\n if (preview) outputPreview = dedupeOutputStr(preview, displayName);\n }\n }\n\n const mainLine = `ai ${model} (${duration})${tokenStr}${intentStr}`;\n return makeResult(mainLine, outputPreview);\n }\n\n // --- Visor check: clean name, show type, show input/output preview ---\n const checkId = attrs['visor.check.id'];\n const checkType = attrs['visor.check.type'];\n if (checkId || name.startsWith('visor.check.')) {\n const cleanName = String(checkId || name).replace(/^visor\\.check\\./, '');\n const typeStr = checkType ? ` [${checkType}]` : '';\n\n // Show input dependencies\n const inputOutputs = String(attrs['visor.check.input.outputs'] || '');\n let inputStr = '';\n if (inputOutputs && inputOutputs !== '{}') {\n inputStr = ' ' + formatInputPreview(inputOutputs, previewLimit);\n }\n\n // Get output preview for checks that produce meaningful output\n const output = String(attrs['visor.check.output'] || '');\n let outputPreview = '';\n if (output) {\n const preview = extractOutputPreview(output, previewLimit);\n if (preview) outputPreview = dedupeOutputStr(preview, cleanName);\n }\n\n const mainLine = `${cleanName}${typeStr} (${duration})${inputStr}`;\n return makeResult(mainLine, outputPreview);\n }\n\n // --- visor.run: show source info ---\n if (name === 'visor.run') {\n const source = attrs['visor.run.source'] || '';\n const sourceStr = source ? ` (${source})` : '';\n return { line: `visor.run${sourceStr} (${duration})` };\n }\n\n // --- Generic span ---\n return { line: `${name} (${duration})${span.status === 'error' ? ' ✗' : ''}` };\n}\n\n/**\n * Extract a meaningful input description for a tool call.\n * Parses the Pattern/Path from tool.result for search tools,\n * and file paths for extract tools.\n */\nfunction extractToolInput(\n toolName: string,\n attrs: Record<string, string | number | boolean>\n): string {\n const result = String(attrs['tool.result'] || '');\n const explicitInput = String(attrs['tool.input'] || '');\n\n if (explicitInput) return truncate(explicitInput, 80);\n\n switch (toolName) {\n case 'search': {\n // Parse \"Pattern: ...\" from Probe search output\n const patMatch = result.match(/Pattern: (.+)/);\n const pathMatch = result.match(/Path: \\S+\\/([^\\s/]+)\\s/);\n const pattern = patMatch ? patMatch[1].trim() : '';\n const inPath = pathMatch ? pathMatch[1] : '';\n if (pattern && inPath) return `\"${truncate(pattern, 50)}\" in ${inPath}`;\n if (pattern) return `\"${truncate(pattern, 60)}\"`;\n return '';\n }\n case 'extract': {\n // Parse file paths from \"Files to extract:\" block\n const fileMatch = result.match(/Files to extract:\\n\\s*(\\S+)/);\n if (fileMatch) {\n const filePath = fileMatch[1];\n // Show just filename with parent dir for context\n const parts = filePath.split('/');\n return parts.length > 2 ? parts.slice(-2).join('/') : parts[parts.length - 1];\n }\n return '';\n }\n case 'listFiles': {\n const pathMatch = result.match(/^(\\S+):/);\n if (pathMatch) {\n const parts = pathMatch[1].split('/');\n return parts[parts.length - 1] || '';\n }\n return '';\n }\n default:\n return truncate(explicitInput, 60);\n }\n}\n\n/**\n * Extract a short intent/question from an AI prompt.\n * Looks for ## Current Request, <question>, or the user message.\n */\nfunction extractAIIntent(input: string, maxLen: number = 150): string {\n if (!input || input.length < 20) return '';\n\n // Try <question>...</question>\n const qMatch = input.match(/<question>([\\s\\S]*?)<\\/question>/);\n if (qMatch) return truncate(qMatch[1].trim(), maxLen);\n\n // Try \"## Current Request\" section\n const crMatch = input.match(/## Current Request\\s*\\n(?:User: )?(.+)/);\n if (crMatch) return truncate(crMatch[1].trim(), maxLen);\n\n // Try \"User:\" pattern\n const userMatch = input.match(/(?:^|\\n)User: (.+)/);\n if (userMatch) return truncate(userMatch[1].trim(), maxLen);\n\n // Try \"Primary message\" pattern\n const pmMatch = input.match(/Primary message[^:]*:\\s*\\n(.+)/);\n if (pmMatch) return truncate(pmMatch[1].trim(), maxLen);\n\n return '';\n}\n\n/**\n * Format a JSON output as a compact structural preview.\n * Shows key names + brief values, preserving the JSON nature of the data.\n */\nfunction formatJsonPreview(obj: any, maxLen: number): string {\n if (obj === null || obj === undefined) return '';\n if (typeof obj !== 'object') return truncate(String(obj), maxLen);\n\n if (Array.isArray(obj)) {\n if (obj.length === 0) return '[]';\n // Show array length + first item preview\n const first =\n typeof obj[0] === 'object' && obj[0] !== null\n ? obj[0].project_id || obj[0].id || obj[0].name || Object.keys(obj[0])[0] || '...'\n : String(obj[0]);\n return `[${obj.length}] ${truncate(String(first), 30)}${obj.length > 1 ? ', ...' : ''}`;\n }\n\n // Object — show key:value pairs compactly\n const parts: string[] = [];\n let len = 2; // for { }\n for (const [key, val] of Object.entries(obj)) {\n // Skip internal/verbose keys\n if (key === 'raw' || key === 'skills' || key === 'tags') continue;\n let valStr: string;\n if (val === null || val === undefined) continue;\n if (typeof val === 'boolean') valStr = String(val);\n else if (typeof val === 'number') valStr = String(val);\n else if (typeof val === 'string') {\n // Skip text fields that are stringified JSON (duplicate data)\n if (val.startsWith('{') || val.startsWith('[')) continue;\n // Clean markdown from text values\n const clean = val\n .replace(/\\*\\*/g, '')\n .replace(/^#+\\s*/gm, '')\n .replace(/`/g, '')\n .trim();\n valStr = `\"${truncate(clean.split('\\n')[0], Math.min(80, maxLen / 3))}\"`;\n } else if (Array.isArray(val)) valStr = `[${val.length}]`;\n else if (typeof val === 'object') valStr = `{${Object.keys(val).length} keys}`;\n else valStr = '...';\n\n const part = `${key}: ${valStr}`;\n if (len + part.length + 2 > maxLen) {\n parts.push('...');\n break;\n }\n parts.push(part);\n len += part.length + 2;\n }\n\n return `{${parts.join(', ')}}`;\n}\n\n/**\n * Extract a short preview from a check's output JSON.\n * Shows structural JSON preview preserving the data format.\n */\nfunction extractOutputPreview(output: string, maxLen: number = 120): string {\n try {\n const obj = JSON.parse(output);\n return formatJsonPreview(obj, maxLen);\n } catch {\n // JSON truncated by telemetry — extract top-level keys with regex\n return extractTruncatedJsonPreview(output, maxLen);\n }\n}\n\n/**\n * Extract a structured preview from truncated JSON (telemetry cuts at ~2048 chars).\n * Extracts top-level key:value pairs using regex.\n */\n/**\n * Tolerant JSON parser that extracts as much structure as possible from\n * truncated JSON (e.g., OTEL attribute values cut off at ~2048 chars).\n * Returns a partial JS object/array/string — whatever was parseable.\n */\nfunction parseTruncatedJson(input: string): any {\n let pos = 0;\n const len = input.length;\n\n function skipWhitespace(): void {\n while (pos < len && ' \\t\\n\\r'.includes(input[pos])) pos++;\n }\n\n function parseString(): string {\n if (input[pos] !== '\"') return '';\n pos++; // skip opening quote\n let result = '';\n while (pos < len) {\n const ch = input[pos];\n if (ch === '\\\\' && pos + 1 < len) {\n const next = input[pos + 1];\n if (next === 'n') {\n result += '\\n';\n pos += 2;\n continue;\n }\n if (next === 't') {\n result += '\\t';\n pos += 2;\n continue;\n }\n if (next === '\"') {\n result += '\"';\n pos += 2;\n continue;\n }\n if (next === '\\\\') {\n result += '\\\\';\n pos += 2;\n continue;\n }\n result += next;\n pos += 2;\n continue;\n }\n if (ch === '\"') {\n pos++;\n return result;\n }\n result += ch;\n pos++;\n }\n // Truncated — no closing quote\n return result;\n }\n\n function parseNumber(): number {\n const start = pos;\n if (input[pos] === '-') pos++;\n while (pos < len && input[pos] >= '0' && input[pos] <= '9') pos++;\n if (pos < len && input[pos] === '.') {\n pos++;\n while (pos < len && input[pos] >= '0' && input[pos] <= '9') pos++;\n }\n return Number(input.slice(start, pos));\n }\n\n function parseValue(): any {\n skipWhitespace();\n if (pos >= len) return undefined;\n const ch = input[pos];\n if (ch === '\"') return parseString();\n if (ch === '{') return parseObject();\n if (ch === '[') return parseArray();\n if (ch === 't' && input.slice(pos, pos + 4) === 'true') {\n pos += 4;\n return true;\n }\n if (ch === 'f' && input.slice(pos, pos + 5) === 'false') {\n pos += 5;\n return false;\n }\n if (ch === 'n' && input.slice(pos, pos + 4) === 'null') {\n pos += 4;\n return null;\n }\n if (ch === '-' || (ch >= '0' && ch <= '9')) return parseNumber();\n return undefined; // truncated\n }\n\n function parseObject(): Record<string, any> {\n const obj: Record<string, any> = {};\n pos++; // skip {\n skipWhitespace();\n while (pos < len && input[pos] !== '}') {\n skipWhitespace();\n if (pos >= len || input[pos] !== '\"') break; // truncated\n const key = parseString();\n skipWhitespace();\n if (pos >= len || input[pos] !== ':') {\n obj[key] = undefined;\n break;\n }\n pos++; // skip :\n const val = parseValue();\n if (val !== undefined) obj[key] = val;\n skipWhitespace();\n if (pos < len && input[pos] === ',') pos++;\n }\n if (pos < len && input[pos] === '}') pos++;\n return obj;\n }\n\n function parseArray(): any[] {\n const arr: any[] = [];\n pos++; // skip [\n skipWhitespace();\n while (pos < len && input[pos] !== ']') {\n const val = parseValue();\n if (val !== undefined) arr.push(val);\n else break; // truncated\n skipWhitespace();\n if (pos < len && input[pos] === ',') pos++;\n skipWhitespace();\n }\n if (pos < len && input[pos] === ']') pos++;\n return arr;\n }\n\n return parseValue();\n}\n\nfunction extractTruncatedJsonPreview(output: string, maxLen: number): string {\n if (!output.startsWith('{') && !output.startsWith('[')) return '';\n\n const parsed = parseTruncatedJson(output);\n if (!parsed || typeof parsed !== 'object') return '';\n\n return formatJsonPreview(parsed, maxLen);\n}\n\n/**\n * Format check inputs from visor.check.input.outputs into a compact summary.\n * Shows which checks provided data: ← {route-intent: {intent, topic}, build-config: {5 keys}}\n */\nfunction formatInputPreview(inputOutputsStr: string, maxLen: number): string {\n if (!inputOutputsStr) return '';\n try {\n const inputs = JSON.parse(inputOutputsStr);\n if (typeof inputs !== 'object' || inputs === null) return '';\n const keys = Object.keys(inputs);\n if (keys.length === 0) return '';\n\n const parts: string[] = [];\n let len = 2;\n for (const key of keys) {\n const val = inputs[key];\n let valStr: string;\n if (typeof val === 'object' && val !== null) {\n const vkeys = Object.keys(val);\n if (vkeys.length <= 3) {\n valStr = `{${vkeys.join(', ')}}`;\n } else {\n valStr = `{${vkeys.slice(0, 2).join(', ')}, ...${vkeys.length} keys}`;\n }\n } else {\n valStr = truncate(String(val), 30);\n }\n const part = `${key}: ${valStr}`;\n if (len + part.length + 2 > maxLen) {\n parts.push('...');\n break;\n }\n parts.push(part);\n len += part.length + 2;\n }\n return `← {${parts.join(', ')}}`;\n } catch {\n return '';\n }\n}\n\n/**\n * Extract the route-intent topic from the span list.\n * Looks for the route-intent check output or classify check output.\n */\nfunction extractRouteIntentTopic(spans: NormalizedSpan[]): string | undefined {\n // Try route-intent check output first\n const riSpan = spans.find(s => s.attributes['visor.check.id'] === 'route-intent');\n if (riSpan) {\n const output = String(riSpan.attributes['visor.check.output'] || '');\n if (output) {\n try {\n const obj = JSON.parse(output);\n if (obj.topic) return truncate(String(obj.topic), 150);\n } catch {}\n }\n }\n // Try classify check output\n const classifySpan = spans.find(s => s.attributes['visor.check.id'] === 'classify');\n if (classifySpan) {\n const output = String(classifySpan.attributes['visor.check.output'] || '');\n if (output) {\n try {\n const obj = JSON.parse(output);\n if (obj.topic) return truncate(String(obj.topic), 150);\n } catch {}\n }\n }\n return undefined;\n}\n\nfunction formatSize(chars: number): string {\n if (chars < 1000) return `${chars} chars`;\n return `${(chars / 1000).toFixed(1)}k chars`;\n}\n\nfunction formatDurationMs(ms: number): string {\n if (ms < 0) return '0ms';\n if (ms < 1000) return `${Math.round(ms)}ms`;\n if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`;\n const mins = Math.floor(ms / 60000);\n const secs = Math.round((ms % 60000) / 1000);\n return `${mins}m ${secs}s`;\n}\n\nfunction truncate(str: string, max: number): string {\n if (typeof str !== 'string') return '';\n if (str.length <= max) return str;\n // Show first and last portions with truncation marker in the middle\n const tail = Math.min(100, Math.floor(max / 3));\n const head = max - tail - 19; // 19 for \" ...[truncated]... \"\n if (head < 10) return str.slice(0, max - 3) + '...';\n return str.slice(0, head) + ' ...[truncated]... ' + str.slice(-tail);\n}\n","/**\n * Task Response Evaluator with LLM Judge + Trace Analysis.\n *\n * Evaluates completed agent tasks on two axes:\n * 1. Response quality (relevance, completeness, actionability)\n * 2. Execution quality (tool call efficiency, unnecessary delegations)\n *\n * Uses ProbeAgent in single-shot mode (same pattern as llm-judge.ts).\n */\n\nimport crypto from 'crypto';\nimport { logger } from '../logger';\nimport type { SqliteTaskStore } from './task-store';\nimport { serializeTraceForPrompt } from './trace-serializer';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface ResponseQuality {\n rating: number; // 1-5\n category: 'excellent' | 'good' | 'adequate' | 'poor' | 'off-topic' | 'error';\n relevance: boolean;\n completeness: boolean;\n actionable: boolean;\n reasoning: string;\n}\n\nexport interface ExecutionQuality {\n rating: number; // 1-5\n category: 'efficient' | 'adequate' | 'wasteful' | 'error';\n unnecessary_tool_calls?: number;\n reasoning: string;\n}\n\nexport interface TaskEvaluationResult {\n response_quality: ResponseQuality;\n execution_quality?: ExecutionQuality;\n overall_rating: number; // 1-5\n summary: string;\n}\n\nexport interface TaskEvaluatorConfig {\n model?: string;\n provider?: string;\n apiKey?: string;\n prompt?: string; // Override default evaluation system prompt\n traceDir?: string; // Where to find trace files (default: output/traces)\n}\n\n// ---------------------------------------------------------------------------\n// Default prompt\n// ---------------------------------------------------------------------------\n\nexport const DEFAULT_EVALUATION_PROMPT = `You are a task response quality evaluator for an AI agent system called Visor.\n\nYou will receive the user's original request and an execution trace inside <execution_trace> tags. The trace is a YAML-formatted view of the entire agent execution, including the final response. When no trace is available, the agent response is provided directly.\n\n## How to Read the Execution Trace\n\nThe trace is a tree of spans representing the agent's execution pipeline:\n\n**Top-level: \\`visor.run\\`** — The root span with metadata:\n- \\`trace_id\\`: Unique execution identifier\n- \\`visor\\` / \\`probe\\`: Software versions\n- \\`source\\`: Where the request came from (e.g., \"slack\", \"cli\")\n- \\`duration\\`: Total wall-clock time\n\n**Checks** — Named processing steps (e.g., \\`route-intent\\`, \\`explore-code\\`, \\`generate-response\\`):\n- \\`type\\`: \"ai\" (LLM-powered), \"script\" (deterministic), or \"workflow\" (sub-pipeline)\n- \\`duration\\`: How long this step took\n- \\`input\\`: What was passed to this check — may include an \\`intent\\` (the user's question as understood by the router) and dependency outputs\n- \\`output\\`: The check's result — may be structured JSON or plain text\n\n**AI blocks** (\\`ai: model-name\\`) — Individual LLM calls within checks:\n- Shows model used, duration, and token counts (input/output)\n- \\`intent\\`: The question or instruction sent to the LLM\n\n**Tool calls** — Listed as \\`- toolName(input) → size\\`:\n- \\`search(\"query\" in repo)\\`: Code search. \"→ no results\" means nothing was found; otherwise shows result size\n- \\`extract(file/path)\\`: File content extraction with result size\n- \\`listFiles(dir)\\`: Directory listing\n- \\`bash()\\`: Shell command execution\n\n**Delegations** (\\`search.delegate(\"query\")\\`) — Sub-agent searches:\n- Contains their own AI blocks and tool calls\n- Used for complex multi-step code exploration\n\n**The \\`response\\` field** at the end of the trace is the final answer sent back to the user. This is the primary output to evaluate.\n\n**Symbols:**\n- \\`✗\\` marks failed/error spans\n- \\`= check-name\\` means output is identical to that check's output (deduplication)\n\n## Evaluation Criteria\n\n**Response Quality** (1-5):\n- **Relevance**: Does the response directly address what the user asked? A response about the wrong topic or that misunderstands the question scores low.\n- **Completeness**: Does it fully answer the question? Partial answers, missing key details, or surface-level responses score lower.\n- **Actionable**: Can the user act on this information? Vague or generic advice scores lower than specific, concrete answers with code references.\n- Rating: 5=excellent (thorough, specific, directly useful), 4=good (answers well but minor gaps), 3=adequate (addresses question but lacks depth), 2=poor (partially relevant or very incomplete), 1=off-topic or error\n\n**Execution Quality** (1-5, only when trace is provided):\n- **Efficiency**: Were tool calls necessary and well-targeted? Good search queries that find results on the first try score high.\n- **Redundancy**: Were there duplicate searches, unnecessary re-searches with slightly different queries, or tools called for information already available?\n- **Delegation quality**: Were search delegations productive? Did they explore relevant code paths?\n- **Token usage**: Was input context kept reasonable, or did the agent load excessive amounts of code?\n- Rating: 5=efficient (minimal, targeted tool use), 4=adequate (minor redundancy), 3=some waste (noticeable unnecessary calls), 2=wasteful (many redundant searches or delegations), 1=error/broken execution\n\n**Overall Rating** (1-5): Weighted combination — response quality matters most, execution quality is secondary. A perfect response from a wasteful execution still scores 3-4 overall.\n\nYou MUST respond with valid JSON matching the provided schema. Be specific in your reasoning — reference actual check names, tool calls, or response content.`;\n\n// ---------------------------------------------------------------------------\n// JSON Schema\n// ---------------------------------------------------------------------------\n\nfunction buildEvaluationSchema(includeExecution: boolean): Record<string, unknown> {\n const schema: Record<string, unknown> = {\n type: 'object',\n required: ['response_quality', 'overall_rating', 'summary'],\n properties: {\n response_quality: {\n type: 'object',\n required: ['rating', 'category', 'relevance', 'completeness', 'actionable', 'reasoning'],\n properties: {\n rating: { type: 'integer', minimum: 1, maximum: 5 },\n category: {\n type: 'string',\n enum: ['excellent', 'good', 'adequate', 'poor', 'off-topic', 'error'],\n },\n relevance: { type: 'boolean' },\n completeness: { type: 'boolean' },\n actionable: { type: 'boolean' },\n reasoning: { type: 'string' },\n },\n },\n overall_rating: { type: 'integer', minimum: 1, maximum: 5 },\n summary: { type: 'string' },\n },\n };\n\n if (includeExecution) {\n (schema.required as string[]).push('execution_quality');\n (schema.properties as Record<string, unknown>).execution_quality = {\n type: 'object',\n required: ['rating', 'category', 'reasoning'],\n properties: {\n rating: { type: 'integer', minimum: 1, maximum: 5 },\n category: { type: 'string', enum: ['efficient', 'adequate', 'wasteful', 'error'] },\n unnecessary_tool_calls: { type: 'integer' },\n reasoning: { type: 'string' },\n },\n };\n }\n\n return schema;\n}\n\n// ---------------------------------------------------------------------------\n// Core evaluation\n// ---------------------------------------------------------------------------\n\n/**\n * Evaluate a completed task using an LLM judge.\n *\n * @param taskId - Task ID (full or prefix)\n * @param store - Initialized SqliteTaskStore\n * @param config - Optional evaluator configuration\n * @returns Evaluation result with ratings and reasoning\n */\nexport async function evaluateTask(\n taskId: string,\n store: SqliteTaskStore,\n config?: TaskEvaluatorConfig\n): Promise<TaskEvaluationResult> {\n // 1. Load task data\n const { rows } = store.listTasksRaw({ limit: 500 });\n const match = rows.find(r => r.id === taskId || r.id.startsWith(taskId));\n if (!match) {\n throw new Error(`Task not found: ${taskId}`);\n }\n\n const fullTask = store.getTask(match.id);\n if (!fullTask) {\n throw new Error(`Task data not found: ${match.id}`);\n }\n\n // 2. Extract request and response text\n const requestText = match.request_message || 'No request text available';\n\n let responseText = 'No response available';\n if (fullTask.status?.message) {\n const parts = fullTask.status.message.parts ?? [];\n const textPart = parts.find((p: any) => typeof p.text === 'string');\n if (textPart) {\n responseText = (textPart as any).text;\n }\n }\n\n // 3. Shortcut for failed tasks with no real response\n if (fullTask.status.state === 'failed' && responseText === 'No response available') {\n return {\n response_quality: {\n rating: 1,\n category: 'error',\n relevance: false,\n completeness: false,\n actionable: false,\n reasoning: 'Task failed without producing a response.',\n },\n overall_rating: 1,\n summary: 'Task failed without producing a response.',\n };\n }\n\n // 4. Try to find and serialize execution trace (full mode, with task response)\n // Supports Grafana Tempo, Jaeger, and local NDJSON files (auto-detected)\n let traceTree: string | undefined;\n const traceId = match.metadata?.trace_id as string | undefined;\n const traceFile = match.metadata?.trace_file as string | undefined;\n\n if (traceFile || traceId) {\n try {\n const traceRef = traceFile || traceId!;\n // Use full mode (1M chars) so trace is not truncated, include task response\n traceTree = await serializeTraceForPrompt(\n traceRef,\n 1_000_000,\n { traceDir: config?.traceDir },\n responseText !== 'No response available' ? responseText : undefined\n );\n if (traceTree === '(no trace data available)') {\n traceTree = undefined;\n }\n } catch (err) {\n logger.debug(\n `[TaskEvaluator] Failed to load trace: ${err instanceof Error ? err.message : err}`\n );\n }\n }\n\n // 5. Build prompts\n const systemPrompt = config?.prompt || process.env.VISOR_EVAL_PROMPT || DEFAULT_EVALUATION_PROMPT;\n const hasTrace = !!traceTree;\n\n let userPrompt: string;\n if (traceTree) {\n // Trace includes the full execution + response — no need to duplicate\n userPrompt = `<user_request>\\n${requestText}\\n</user_request>\\n\\n<execution_trace>\\n${traceTree}\\n</execution_trace>`;\n } else {\n // No trace available — provide request and response directly\n userPrompt = `<user_request>\\n${requestText}\\n</user_request>\\n\\n<agent_response>\\n${responseText}\\n</agent_response>`;\n }\n\n // 6. Call LLM via ProbeAgent\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const { ProbeAgent } = require('@probelabs/probe');\n\n const model =\n config?.model || process.env.VISOR_EVAL_MODEL || process.env.VISOR_JUDGE_MODEL || undefined;\n const provider = config?.provider || process.env.VISOR_EVAL_PROVIDER || undefined;\n\n const agentOptions: Record<string, unknown> = {\n sessionId: `visor-task-eval-${Date.now()}`,\n systemPrompt,\n maxIterations: 1,\n disableTools: true,\n };\n if (model) agentOptions.model = model;\n if (provider) agentOptions.provider = provider;\n if (config?.apiKey) {\n const envKey =\n provider === 'openai'\n ? 'OPENAI_API_KEY'\n : provider === 'anthropic'\n ? 'ANTHROPIC_API_KEY'\n : 'GOOGLE_API_KEY';\n process.env[envKey] = config.apiKey;\n }\n\n const agent = new ProbeAgent(agentOptions);\n if (typeof agent.initialize === 'function') {\n await agent.initialize();\n }\n\n const jsonSchema = buildEvaluationSchema(hasTrace);\n const schemaStr = JSON.stringify(jsonSchema);\n const response = await agent.answer(userPrompt, undefined, { schema: schemaStr });\n\n // 7. Parse JSON response (same pattern as llm-judge.ts:181-196)\n let result: TaskEvaluationResult;\n try {\n const cleaned = response\n .replace(/^```(?:json)?\\s*\\n?/m, '')\n .replace(/\\n?```\\s*$/m, '')\n .trim();\n result = JSON.parse(cleaned);\n } catch {\n const jsonMatch = response.match(/\\{[\\s\\S]*\\}/);\n if (jsonMatch) {\n result = JSON.parse(jsonMatch[0]);\n } else {\n throw new Error(`Failed to parse evaluation response as JSON: ${response.slice(0, 200)}`);\n }\n }\n\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// Store evaluation as artifact\n// ---------------------------------------------------------------------------\n\n/**\n * Run evaluation and store result as a task artifact.\n */\nexport async function evaluateAndStore(\n taskId: string,\n store: SqliteTaskStore,\n config?: TaskEvaluatorConfig\n): Promise<TaskEvaluationResult> {\n const result = await evaluateTask(taskId, store, config);\n\n // Resolve full task ID\n const { rows } = store.listTasksRaw({ limit: 500 });\n const match = rows.find(r => r.id === taskId || r.id.startsWith(taskId));\n if (match) {\n store.addArtifact(match.id, {\n artifact_id: crypto.randomUUID(),\n name: 'evaluation',\n parts: [{ text: JSON.stringify(result), media_type: 'application/json' }],\n });\n }\n\n return result;\n}\n"],"mappings":"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","names":["hasChildren","errMark"]}
|
|
@@ -9,8 +9,8 @@ import {
|
|
|
9
9
|
setSpanError,
|
|
10
10
|
withActiveSpan,
|
|
11
11
|
withVisorRun
|
|
12
|
-
} from "./chunk-
|
|
13
|
-
import "./chunk-
|
|
12
|
+
} from "./chunk-V75NEIXL.mjs";
|
|
13
|
+
import "./chunk-4AXAVXG5.mjs";
|
|
14
14
|
import "./chunk-34QX63WK.mjs";
|
|
15
15
|
import "./chunk-UCMJJ3IM.mjs";
|
|
16
16
|
import "./chunk-J7LXIPZS.mjs";
|
|
@@ -26,4 +26,4 @@ export {
|
|
|
26
26
|
withActiveSpan,
|
|
27
27
|
withVisorRun
|
|
28
28
|
};
|
|
29
|
-
//# sourceMappingURL=trace-helpers-
|
|
29
|
+
//# sourceMappingURL=trace-helpers-HL5FBX65.mjs.map
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import {
|
|
2
|
+
__getOrCreateNdjsonPath,
|
|
3
|
+
_appendRunMarker,
|
|
4
|
+
addEvent,
|
|
5
|
+
getTracer,
|
|
6
|
+
getVisorRunAttributes,
|
|
7
|
+
init_trace_helpers,
|
|
8
|
+
setSpanAttributes,
|
|
9
|
+
setSpanError,
|
|
10
|
+
withActiveSpan,
|
|
11
|
+
withVisorRun
|
|
12
|
+
} from "./chunk-7ERVRLDV.mjs";
|
|
13
|
+
import "./chunk-QXT47ZHR.mjs";
|
|
14
|
+
import "./chunk-34QX63WK.mjs";
|
|
15
|
+
import "./chunk-UCMJJ3IM.mjs";
|
|
16
|
+
import "./chunk-J7LXIPZS.mjs";
|
|
17
|
+
init_trace_helpers();
|
|
18
|
+
export {
|
|
19
|
+
__getOrCreateNdjsonPath,
|
|
20
|
+
_appendRunMarker,
|
|
21
|
+
addEvent,
|
|
22
|
+
getTracer,
|
|
23
|
+
getVisorRunAttributes,
|
|
24
|
+
setSpanAttributes,
|
|
25
|
+
setSpanError,
|
|
26
|
+
withActiveSpan,
|
|
27
|
+
withVisorRun
|
|
28
|
+
};
|
|
29
|
+
//# sourceMappingURL=trace-helpers-WJXYVV4S.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import {
|
|
2
|
+
__esm
|
|
3
|
+
} from "./chunk-J7LXIPZS.mjs";
|
|
4
|
+
|
|
5
|
+
// src/debug-visualizer/trace-reader.ts
|
|
6
|
+
import * as fs from "fs";
|
|
7
|
+
import * as readline from "readline";
|
|
8
|
+
async function parseNDJSONTrace(filePath) {
|
|
9
|
+
const spans = [];
|
|
10
|
+
let lineNumber = 0;
|
|
11
|
+
const fileStream = fs.createReadStream(filePath);
|
|
12
|
+
const rl = readline.createInterface({
|
|
13
|
+
input: fileStream,
|
|
14
|
+
crlfDelay: Infinity
|
|
15
|
+
});
|
|
16
|
+
for await (const line of rl) {
|
|
17
|
+
lineNumber++;
|
|
18
|
+
if (!line.trim()) {
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
try {
|
|
22
|
+
const rawSpan = JSON.parse(line);
|
|
23
|
+
const processedSpan = processRawSpan(rawSpan);
|
|
24
|
+
spans.push(processedSpan);
|
|
25
|
+
} catch (error) {
|
|
26
|
+
console.warn(`[trace-reader] Malformed JSON at line ${lineNumber}: ${error}`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
if (spans.length === 0) {
|
|
30
|
+
throw new Error("No valid spans found in trace file");
|
|
31
|
+
}
|
|
32
|
+
const tree = buildExecutionTree(spans);
|
|
33
|
+
const snapshots = extractStateSnapshots(spans);
|
|
34
|
+
const timeline = computeTimeline(spans);
|
|
35
|
+
const sortedSpans = [...spans].sort((a, b) => compareTimeValues(a.startTime, b.startTime));
|
|
36
|
+
const firstSpan = sortedSpans[0];
|
|
37
|
+
const lastSpan = sortedSpans[sortedSpans.length - 1];
|
|
38
|
+
const startTimeMs = timeValueToMillis(firstSpan.startTime);
|
|
39
|
+
const endTimeMs = timeValueToMillis(lastSpan.endTime);
|
|
40
|
+
return {
|
|
41
|
+
runId: tree.checkId,
|
|
42
|
+
traceId: firstSpan.traceId,
|
|
43
|
+
spans,
|
|
44
|
+
tree,
|
|
45
|
+
timeline,
|
|
46
|
+
snapshots,
|
|
47
|
+
metadata: {
|
|
48
|
+
startTime: timeValueToISO(firstSpan.startTime),
|
|
49
|
+
endTime: timeValueToISO(lastSpan.endTime),
|
|
50
|
+
duration: endTimeMs - startTimeMs,
|
|
51
|
+
totalSpans: spans.length,
|
|
52
|
+
totalSnapshots: snapshots.length
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function processRawSpan(rawSpan) {
|
|
57
|
+
const traceId = rawSpan.traceId || "";
|
|
58
|
+
const spanId = rawSpan.spanId || "";
|
|
59
|
+
const parentSpanId = rawSpan.parentSpanId || void 0;
|
|
60
|
+
const name = rawSpan.name || "unknown";
|
|
61
|
+
const startTime = rawSpan.startTime || [0, 0];
|
|
62
|
+
const endTime = rawSpan.endTime || rawSpan.startTime || [0, 0];
|
|
63
|
+
const startMs = timeValueToMillis(startTime);
|
|
64
|
+
const endMs = timeValueToMillis(endTime);
|
|
65
|
+
const duration = endMs - startMs;
|
|
66
|
+
const attributes = rawSpan.attributes || {};
|
|
67
|
+
const events = (rawSpan.events || []).map((evt) => ({
|
|
68
|
+
name: evt.name || "unknown",
|
|
69
|
+
time: evt.time || [0, 0],
|
|
70
|
+
timestamp: evt.timestamp || timeValueToISO(evt.time || [0, 0]),
|
|
71
|
+
attributes: evt.attributes || {}
|
|
72
|
+
}));
|
|
73
|
+
const status = rawSpan.status?.code === 2 ? "error" : "ok";
|
|
74
|
+
return {
|
|
75
|
+
traceId,
|
|
76
|
+
spanId,
|
|
77
|
+
parentSpanId,
|
|
78
|
+
name,
|
|
79
|
+
startTime,
|
|
80
|
+
endTime,
|
|
81
|
+
duration,
|
|
82
|
+
attributes,
|
|
83
|
+
events,
|
|
84
|
+
status
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
function buildExecutionTree(spans) {
|
|
88
|
+
const nodeMap = /* @__PURE__ */ new Map();
|
|
89
|
+
for (const span of spans) {
|
|
90
|
+
const node = createExecutionNode(span);
|
|
91
|
+
nodeMap.set(span.spanId, node);
|
|
92
|
+
}
|
|
93
|
+
let rootNode;
|
|
94
|
+
for (const span of spans) {
|
|
95
|
+
const node = nodeMap.get(span.spanId);
|
|
96
|
+
if (!span.parentSpanId) {
|
|
97
|
+
rootNode = node;
|
|
98
|
+
} else {
|
|
99
|
+
const parent = nodeMap.get(span.parentSpanId);
|
|
100
|
+
if (parent) {
|
|
101
|
+
parent.children.push(node);
|
|
102
|
+
} else {
|
|
103
|
+
console.warn(`[trace-reader] Orphaned span: ${span.spanId} (parent: ${span.parentSpanId})`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if (!rootNode) {
|
|
108
|
+
console.warn("[trace-reader] No root span found, creating synthetic root");
|
|
109
|
+
rootNode = {
|
|
110
|
+
checkId: "synthetic-root",
|
|
111
|
+
type: "run",
|
|
112
|
+
status: "completed",
|
|
113
|
+
children: Array.from(nodeMap.values()).filter((n) => !n.span.parentSpanId),
|
|
114
|
+
span: spans[0],
|
|
115
|
+
// Use first span as placeholder
|
|
116
|
+
state: {}
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
return rootNode;
|
|
120
|
+
}
|
|
121
|
+
function createExecutionNode(span) {
|
|
122
|
+
const attrs = span.attributes;
|
|
123
|
+
const checkId = attrs["visor.check.id"] || attrs["visor.run.id"] || span.spanId;
|
|
124
|
+
let type = "unknown";
|
|
125
|
+
if (span.name === "visor.run") {
|
|
126
|
+
type = "run";
|
|
127
|
+
} else if (span.name === "visor.check") {
|
|
128
|
+
type = "check";
|
|
129
|
+
} else if (span.name.startsWith("visor.provider.")) {
|
|
130
|
+
type = "provider";
|
|
131
|
+
}
|
|
132
|
+
let status = "completed";
|
|
133
|
+
if (span.status === "error") {
|
|
134
|
+
status = "error";
|
|
135
|
+
} else if (attrs["visor.check.skipped"] === true) {
|
|
136
|
+
status = "skipped";
|
|
137
|
+
}
|
|
138
|
+
const state = {};
|
|
139
|
+
if (attrs["visor.check.input.context"]) {
|
|
140
|
+
try {
|
|
141
|
+
state.inputContext = JSON.parse(attrs["visor.check.input.context"]);
|
|
142
|
+
} catch {
|
|
143
|
+
state.inputContext = attrs["visor.check.input.context"];
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if (attrs["visor.check.output"]) {
|
|
147
|
+
try {
|
|
148
|
+
state.output = JSON.parse(attrs["visor.check.output"]);
|
|
149
|
+
} catch {
|
|
150
|
+
state.output = attrs["visor.check.output"];
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
if (span.status === "error" || attrs["visor.check.error"]) {
|
|
154
|
+
state.errors = [attrs["visor.check.error"] || "Unknown error"];
|
|
155
|
+
}
|
|
156
|
+
state.metadata = {
|
|
157
|
+
type: attrs["visor.check.type"],
|
|
158
|
+
duration: span.duration,
|
|
159
|
+
provider: attrs["visor.provider.type"]
|
|
160
|
+
};
|
|
161
|
+
return {
|
|
162
|
+
checkId,
|
|
163
|
+
type,
|
|
164
|
+
status,
|
|
165
|
+
children: [],
|
|
166
|
+
span,
|
|
167
|
+
state
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
function extractStateSnapshots(spans) {
|
|
171
|
+
const snapshots = [];
|
|
172
|
+
for (const span of spans) {
|
|
173
|
+
for (const event of span.events) {
|
|
174
|
+
if (event.name === "state.snapshot") {
|
|
175
|
+
const attrs = event.attributes || {};
|
|
176
|
+
const snapshot = {
|
|
177
|
+
checkId: attrs["visor.snapshot.check_id"] || span.attributes["visor.check.id"] || "unknown",
|
|
178
|
+
timestamp: attrs["visor.snapshot.timestamp"] || event.timestamp || timeValueToISO(event.time),
|
|
179
|
+
timestampNanos: event.time,
|
|
180
|
+
outputs: parseJSON(attrs["visor.snapshot.outputs"], {}),
|
|
181
|
+
memory: parseJSON(attrs["visor.snapshot.memory"], {})
|
|
182
|
+
};
|
|
183
|
+
snapshots.push(snapshot);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
snapshots.sort((a, b) => compareTimeValues(a.timestampNanos, b.timestampNanos));
|
|
188
|
+
return snapshots;
|
|
189
|
+
}
|
|
190
|
+
function computeTimeline(spans) {
|
|
191
|
+
const events = [];
|
|
192
|
+
for (const span of spans) {
|
|
193
|
+
const checkId = span.attributes["visor.check.id"] || span.spanId;
|
|
194
|
+
events.push({
|
|
195
|
+
type: "check.started",
|
|
196
|
+
checkId,
|
|
197
|
+
timestamp: timeValueToISO(span.startTime),
|
|
198
|
+
timestampNanos: span.startTime,
|
|
199
|
+
metadata: {
|
|
200
|
+
name: span.name,
|
|
201
|
+
type: span.attributes["visor.check.type"]
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
events.push({
|
|
205
|
+
type: span.status === "error" ? "check.failed" : "check.completed",
|
|
206
|
+
checkId,
|
|
207
|
+
timestamp: timeValueToISO(span.endTime),
|
|
208
|
+
timestampNanos: span.endTime,
|
|
209
|
+
duration: span.duration,
|
|
210
|
+
status: span.status,
|
|
211
|
+
metadata: {
|
|
212
|
+
name: span.name
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
for (const evt of span.events) {
|
|
216
|
+
events.push({
|
|
217
|
+
type: evt.name === "state.snapshot" ? "state.snapshot" : "event",
|
|
218
|
+
checkId: evt.attributes?.["visor.snapshot.check_id"] || checkId,
|
|
219
|
+
timestamp: evt.timestamp || timeValueToISO(evt.time),
|
|
220
|
+
timestampNanos: evt.time,
|
|
221
|
+
metadata: {
|
|
222
|
+
eventName: evt.name,
|
|
223
|
+
attributes: evt.attributes
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
events.sort((a, b) => compareTimeValues(a.timestampNanos, b.timestampNanos));
|
|
229
|
+
return events;
|
|
230
|
+
}
|
|
231
|
+
function timeValueToMillis(timeValue) {
|
|
232
|
+
const [seconds, nanos] = timeValue;
|
|
233
|
+
return seconds * 1e3 + nanos / 1e6;
|
|
234
|
+
}
|
|
235
|
+
function timeValueToISO(timeValue) {
|
|
236
|
+
const millis = timeValueToMillis(timeValue);
|
|
237
|
+
return new Date(millis).toISOString();
|
|
238
|
+
}
|
|
239
|
+
function compareTimeValues(a, b) {
|
|
240
|
+
if (a[0] !== b[0]) {
|
|
241
|
+
return a[0] - b[0];
|
|
242
|
+
}
|
|
243
|
+
return a[1] - b[1];
|
|
244
|
+
}
|
|
245
|
+
function parseJSON(value, defaultValue) {
|
|
246
|
+
if (typeof value !== "string") {
|
|
247
|
+
return defaultValue;
|
|
248
|
+
}
|
|
249
|
+
try {
|
|
250
|
+
return JSON.parse(value);
|
|
251
|
+
} catch {
|
|
252
|
+
return defaultValue;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
var init_trace_reader = __esm({
|
|
256
|
+
"src/debug-visualizer/trace-reader.ts"() {
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
init_trace_reader();
|
|
260
|
+
export {
|
|
261
|
+
buildExecutionTree,
|
|
262
|
+
computeTimeline,
|
|
263
|
+
extractStateSnapshots,
|
|
264
|
+
parseNDJSONTrace
|
|
265
|
+
};
|
|
266
|
+
//# sourceMappingURL=trace-reader-ZY77OFNM.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/debug-visualizer/trace-reader.ts"],"sourcesContent":["/**\n * Trace File Reader & Processor\n *\n * Reads OpenTelemetry NDJSON trace files and reconstructs execution tree structure\n * for the Debug Visualizer.\n *\n * Milestone 2: Trace File Reader\n */\n\nimport * as fs from 'fs';\nimport * as readline from 'readline';\n\n// ============================================================================\n// Core Data Structures\n// ============================================================================\n\n/**\n * OTEL span event with attributes\n */\nexport interface SpanEvent {\n name: string;\n time: [number, number]; // [seconds, nanoseconds]\n timestamp?: string; // ISO timestamp for convenience\n attributes?: Record<string, any>;\n}\n\n/**\n * Processed OTEL span with clean structure\n */\nexport interface ProcessedSpan {\n traceId: string;\n spanId: string;\n parentSpanId?: string;\n name: string;\n startTime: [number, number]; // [seconds, nanoseconds]\n endTime: [number, number]; // [seconds, nanoseconds]\n duration: number; // milliseconds\n attributes: Record<string, any>;\n events: SpanEvent[];\n status: 'ok' | 'error';\n}\n\n/**\n * Hierarchical execution tree node\n */\nexport interface ExecutionNode {\n checkId: string;\n type: string; // 'run', 'check', 'provider'\n status: 'pending' | 'running' | 'completed' | 'error' | 'skipped';\n children: ExecutionNode[];\n span: ProcessedSpan;\n state: {\n inputContext?: any;\n output?: any;\n errors?: string[];\n metadata?: Record<string, any>;\n };\n}\n\n/**\n * State snapshot for time-travel debugging\n */\nexport interface StateSnapshot {\n checkId: string;\n timestamp: string;\n timestampNanos: [number, number];\n outputs: Record<string, any>;\n memory: Record<string, any>;\n}\n\n/**\n * Timeline event for visualization\n */\nexport interface TimelineEvent {\n type: 'check.started' | 'check.completed' | 'check.failed' | 'state.snapshot' | 'event';\n checkId?: string;\n timestamp: string;\n timestampNanos: [number, number];\n duration?: number;\n status?: string;\n metadata?: Record<string, any>;\n}\n\n/**\n * Complete parsed execution trace\n */\nexport interface ExecutionTrace {\n runId: string;\n traceId: string;\n spans: ProcessedSpan[];\n tree: ExecutionNode;\n timeline: TimelineEvent[];\n snapshots: StateSnapshot[];\n metadata: {\n startTime: string;\n endTime: string;\n duration: number; // milliseconds\n totalSpans: number;\n totalSnapshots: number;\n };\n}\n\n// ============================================================================\n// NDJSON Parser\n// ============================================================================\n\n/**\n * Parse NDJSON trace file and return structured execution trace\n *\n * @param filePath - Path to NDJSON trace file\n * @returns Parsed execution trace with tree, timeline, and snapshots\n */\nexport async function parseNDJSONTrace(filePath: string): Promise<ExecutionTrace> {\n const spans: ProcessedSpan[] = [];\n let lineNumber = 0;\n\n // Read file line by line\n const fileStream = fs.createReadStream(filePath);\n const rl = readline.createInterface({\n input: fileStream,\n crlfDelay: Infinity,\n });\n\n for await (const line of rl) {\n lineNumber++;\n\n // Skip empty lines\n if (!line.trim()) {\n continue;\n }\n\n try {\n const rawSpan = JSON.parse(line);\n const processedSpan = processRawSpan(rawSpan);\n spans.push(processedSpan);\n } catch (error) {\n console.warn(`[trace-reader] Malformed JSON at line ${lineNumber}: ${error}`);\n // Continue parsing remaining lines\n }\n }\n\n if (spans.length === 0) {\n throw new Error('No valid spans found in trace file');\n }\n\n // Build execution tree\n const tree = buildExecutionTree(spans);\n\n // Extract state snapshots\n const snapshots = extractStateSnapshots(spans);\n\n // Compute timeline\n const timeline = computeTimeline(spans);\n\n // Calculate metadata\n const sortedSpans = [...spans].sort((a, b) => compareTimeValues(a.startTime, b.startTime));\n const firstSpan = sortedSpans[0];\n const lastSpan = sortedSpans[sortedSpans.length - 1];\n\n const startTimeMs = timeValueToMillis(firstSpan.startTime);\n const endTimeMs = timeValueToMillis(lastSpan.endTime);\n\n return {\n runId: tree.checkId,\n traceId: firstSpan.traceId,\n spans,\n tree,\n timeline,\n snapshots,\n metadata: {\n startTime: timeValueToISO(firstSpan.startTime),\n endTime: timeValueToISO(lastSpan.endTime),\n duration: endTimeMs - startTimeMs,\n totalSpans: spans.length,\n totalSnapshots: snapshots.length,\n },\n };\n}\n\n/**\n * Process raw OTEL span into clean structure\n */\nfunction processRawSpan(rawSpan: any): ProcessedSpan {\n // Extract span IDs\n const traceId = rawSpan.traceId || '';\n const spanId = rawSpan.spanId || '';\n const parentSpanId = rawSpan.parentSpanId || undefined;\n\n // Extract name\n const name = rawSpan.name || 'unknown';\n\n // Extract times\n const startTime = rawSpan.startTime || [0, 0];\n const endTime = rawSpan.endTime || rawSpan.startTime || [0, 0];\n\n // Calculate duration in milliseconds\n const startMs = timeValueToMillis(startTime);\n const endMs = timeValueToMillis(endTime);\n const duration = endMs - startMs;\n\n // Extract attributes\n const attributes = rawSpan.attributes || {};\n\n // Extract events\n const events: SpanEvent[] = (rawSpan.events || []).map((evt: any) => ({\n name: evt.name || 'unknown',\n time: evt.time || [0, 0],\n timestamp: evt.timestamp || timeValueToISO(evt.time || [0, 0]),\n attributes: evt.attributes || {},\n }));\n\n // Determine status\n const status = rawSpan.status?.code === 2 ? 'error' : 'ok';\n\n return {\n traceId,\n spanId,\n parentSpanId,\n name,\n startTime,\n endTime,\n duration,\n attributes,\n events,\n status,\n };\n}\n\n// ============================================================================\n// Execution Tree Builder\n// ============================================================================\n\n/**\n * Build hierarchical execution tree from flat list of spans\n *\n * @param spans - List of processed spans\n * @returns Root execution node with children\n */\nexport function buildExecutionTree(spans: ProcessedSpan[]): ExecutionNode {\n // Create map of spanId -> ExecutionNode\n const nodeMap = new Map<string, ExecutionNode>();\n\n // First pass: create all nodes\n for (const span of spans) {\n const node = createExecutionNode(span);\n nodeMap.set(span.spanId, node);\n }\n\n // Second pass: build parent-child relationships\n let rootNode: ExecutionNode | undefined;\n\n for (const span of spans) {\n const node = nodeMap.get(span.spanId)!;\n\n if (!span.parentSpanId) {\n // This is the root node (visor.run)\n rootNode = node;\n } else {\n // Find parent and add as child\n const parent = nodeMap.get(span.parentSpanId);\n if (parent) {\n parent.children.push(node);\n } else {\n // Orphaned span - parent not found\n console.warn(`[trace-reader] Orphaned span: ${span.spanId} (parent: ${span.parentSpanId})`);\n }\n }\n }\n\n if (!rootNode) {\n // No root found, create synthetic root\n console.warn('[trace-reader] No root span found, creating synthetic root');\n rootNode = {\n checkId: 'synthetic-root',\n type: 'run',\n status: 'completed',\n children: Array.from(nodeMap.values()).filter(n => !n.span.parentSpanId),\n span: spans[0], // Use first span as placeholder\n state: {},\n };\n }\n\n return rootNode;\n}\n\n/**\n * Create execution node from span\n */\nfunction createExecutionNode(span: ProcessedSpan): ExecutionNode {\n const attrs = span.attributes;\n\n // Extract check ID\n const checkId = attrs['visor.check.id'] || attrs['visor.run.id'] || span.spanId;\n\n // Determine type\n let type = 'unknown';\n if (span.name === 'visor.run') {\n type = 'run';\n } else if (span.name === 'visor.check') {\n type = 'check';\n } else if (span.name.startsWith('visor.provider.')) {\n type = 'provider';\n }\n\n // Determine status\n let status: ExecutionNode['status'] = 'completed';\n if (span.status === 'error') {\n status = 'error';\n } else if (attrs['visor.check.skipped'] === true) {\n status = 'skipped';\n }\n\n // Extract state\n const state: ExecutionNode['state'] = {};\n\n // Input context\n if (attrs['visor.check.input.context']) {\n try {\n state.inputContext = JSON.parse(attrs['visor.check.input.context']);\n } catch {\n state.inputContext = attrs['visor.check.input.context'];\n }\n }\n\n // Output\n if (attrs['visor.check.output']) {\n try {\n state.output = JSON.parse(attrs['visor.check.output']);\n } catch {\n state.output = attrs['visor.check.output'];\n }\n }\n\n // Errors\n if (span.status === 'error' || attrs['visor.check.error']) {\n state.errors = [attrs['visor.check.error'] || 'Unknown error'];\n }\n\n // Additional metadata\n state.metadata = {\n type: attrs['visor.check.type'],\n duration: span.duration,\n provider: attrs['visor.provider.type'],\n };\n\n return {\n checkId,\n type,\n status,\n children: [],\n span,\n state,\n };\n}\n\n// ============================================================================\n// State Snapshot Extractor\n// ============================================================================\n\n/**\n * Extract state snapshots from spans for time-travel debugging\n *\n * @param spans - List of processed spans\n * @returns Array of state snapshots sorted by timestamp\n */\nexport function extractStateSnapshots(spans: ProcessedSpan[]): StateSnapshot[] {\n const snapshots: StateSnapshot[] = [];\n\n for (const span of spans) {\n // Find state.snapshot events\n for (const event of span.events) {\n if (event.name === 'state.snapshot') {\n const attrs = event.attributes || {};\n\n const snapshot: StateSnapshot = {\n checkId:\n attrs['visor.snapshot.check_id'] || span.attributes['visor.check.id'] || 'unknown',\n timestamp:\n attrs['visor.snapshot.timestamp'] || event.timestamp || timeValueToISO(event.time),\n timestampNanos: event.time,\n outputs: parseJSON(attrs['visor.snapshot.outputs'], {}),\n memory: parseJSON(attrs['visor.snapshot.memory'], {}),\n };\n\n snapshots.push(snapshot);\n }\n }\n }\n\n // Sort by timestamp\n snapshots.sort((a, b) => compareTimeValues(a.timestampNanos, b.timestampNanos));\n\n return snapshots;\n}\n\n// ============================================================================\n// Timeline Generator\n// ============================================================================\n\n/**\n * Compute chronological timeline of execution events\n *\n * @param spans - List of processed spans\n * @returns Array of timeline events sorted chronologically\n */\nexport function computeTimeline(spans: ProcessedSpan[]): TimelineEvent[] {\n const events: TimelineEvent[] = [];\n\n for (const span of spans) {\n const checkId = span.attributes['visor.check.id'] || span.spanId;\n\n // Check started event\n events.push({\n type: 'check.started',\n checkId,\n timestamp: timeValueToISO(span.startTime),\n timestampNanos: span.startTime,\n metadata: {\n name: span.name,\n type: span.attributes['visor.check.type'],\n },\n });\n\n // Check completed/failed event\n events.push({\n type: span.status === 'error' ? 'check.failed' : 'check.completed',\n checkId,\n timestamp: timeValueToISO(span.endTime),\n timestampNanos: span.endTime,\n duration: span.duration,\n status: span.status,\n metadata: {\n name: span.name,\n },\n });\n\n // Add span events (state.snapshot, etc.)\n for (const evt of span.events) {\n events.push({\n type: evt.name === 'state.snapshot' ? 'state.snapshot' : 'event',\n checkId: evt.attributes?.['visor.snapshot.check_id'] || checkId,\n timestamp: evt.timestamp || timeValueToISO(evt.time),\n timestampNanos: evt.time,\n metadata: {\n eventName: evt.name,\n attributes: evt.attributes,\n },\n });\n }\n }\n\n // Sort chronologically\n events.sort((a, b) => compareTimeValues(a.timestampNanos, b.timestampNanos));\n\n return events;\n}\n\n// ============================================================================\n// Utility Functions\n// ============================================================================\n\n/**\n * Convert OTEL time value [seconds, nanoseconds] to milliseconds\n */\nfunction timeValueToMillis(timeValue: [number, number]): number {\n const [seconds, nanos] = timeValue;\n return seconds * 1000 + nanos / 1_000_000;\n}\n\n/**\n * Convert OTEL time value to ISO timestamp string\n */\nfunction timeValueToISO(timeValue: [number, number]): string {\n const millis = timeValueToMillis(timeValue);\n return new Date(millis).toISOString();\n}\n\n/**\n * Compare two time values for sorting\n * Returns: -1 if a < b, 0 if a === b, 1 if a > b\n */\nfunction compareTimeValues(a: [number, number], b: [number, number]): number {\n if (a[0] !== b[0]) {\n return a[0] - b[0];\n }\n return a[1] - b[1];\n}\n\n/**\n * Safely parse JSON string, return default on error\n */\nfunction parseJSON<T>(value: any, defaultValue: T): T {\n if (typeof value !== 'string') {\n return defaultValue;\n }\n\n try {\n return JSON.parse(value);\n } catch {\n return defaultValue;\n }\n}\n"],"mappings":";;;;;AASA,YAAY,QAAQ;AACpB,YAAY,cAAc;AAsG1B,eAAsB,iBAAiB,UAA2C;AAChF,QAAM,QAAyB,CAAC;AAChC,MAAI,aAAa;AAGjB,QAAM,aAAgB,oBAAiB,QAAQ;AAC/C,QAAM,KAAc,yBAAgB;AAAA,IAClC,OAAO;AAAA,IACP,WAAW;AAAA,EACb,CAAC;AAED,mBAAiB,QAAQ,IAAI;AAC3B;AAGA,QAAI,CAAC,KAAK,KAAK,GAAG;AAChB;AAAA,IACF;AAEA,QAAI;AACF,YAAM,UAAU,KAAK,MAAM,IAAI;AAC/B,YAAM,gBAAgB,eAAe,OAAO;AAC5C,YAAM,KAAK,aAAa;AAAA,IAC1B,SAAS,OAAO;AACd,cAAQ,KAAK,yCAAyC,UAAU,KAAK,KAAK,EAAE;AAAA,IAE9E;AAAA,EACF;AAEA,MAAI,MAAM,WAAW,GAAG;AACtB,UAAM,IAAI,MAAM,oCAAoC;AAAA,EACtD;AAGA,QAAM,OAAO,mBAAmB,KAAK;AAGrC,QAAM,YAAY,sBAAsB,KAAK;AAG7C,QAAM,WAAW,gBAAgB,KAAK;AAGtC,QAAM,cAAc,CAAC,GAAG,KAAK,EAAE,KAAK,CAAC,GAAG,MAAM,kBAAkB,EAAE,WAAW,EAAE,SAAS,CAAC;AACzF,QAAM,YAAY,YAAY,CAAC;AAC/B,QAAM,WAAW,YAAY,YAAY,SAAS,CAAC;AAEnD,QAAM,cAAc,kBAAkB,UAAU,SAAS;AACzD,QAAM,YAAY,kBAAkB,SAAS,OAAO;AAEpD,SAAO;AAAA,IACL,OAAO,KAAK;AAAA,IACZ,SAAS,UAAU;AAAA,IACnB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,UAAU;AAAA,MACR,WAAW,eAAe,UAAU,SAAS;AAAA,MAC7C,SAAS,eAAe,SAAS,OAAO;AAAA,MACxC,UAAU,YAAY;AAAA,MACtB,YAAY,MAAM;AAAA,MAClB,gBAAgB,UAAU;AAAA,IAC5B;AAAA,EACF;AACF;AAKA,SAAS,eAAe,SAA6B;AAEnD,QAAM,UAAU,QAAQ,WAAW;AACnC,QAAM,SAAS,QAAQ,UAAU;AACjC,QAAM,eAAe,QAAQ,gBAAgB;AAG7C,QAAM,OAAO,QAAQ,QAAQ;AAG7B,QAAM,YAAY,QAAQ,aAAa,CAAC,GAAG,CAAC;AAC5C,QAAM,UAAU,QAAQ,WAAW,QAAQ,aAAa,CAAC,GAAG,CAAC;AAG7D,QAAM,UAAU,kBAAkB,SAAS;AAC3C,QAAM,QAAQ,kBAAkB,OAAO;AACvC,QAAM,WAAW,QAAQ;AAGzB,QAAM,aAAa,QAAQ,cAAc,CAAC;AAG1C,QAAM,UAAuB,QAAQ,UAAU,CAAC,GAAG,IAAI,CAAC,SAAc;AAAA,IACpE,MAAM,IAAI,QAAQ;AAAA,IAClB,MAAM,IAAI,QAAQ,CAAC,GAAG,CAAC;AAAA,IACvB,WAAW,IAAI,aAAa,eAAe,IAAI,QAAQ,CAAC,GAAG,CAAC,CAAC;AAAA,IAC7D,YAAY,IAAI,cAAc,CAAC;AAAA,EACjC,EAAE;AAGF,QAAM,SAAS,QAAQ,QAAQ,SAAS,IAAI,UAAU;AAEtD,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAYO,SAAS,mBAAmB,OAAuC;AAExE,QAAM,UAAU,oBAAI,IAA2B;AAG/C,aAAW,QAAQ,OAAO;AACxB,UAAM,OAAO,oBAAoB,IAAI;AACrC,YAAQ,IAAI,KAAK,QAAQ,IAAI;AAAA,EAC/B;AAGA,MAAI;AAEJ,aAAW,QAAQ,OAAO;AACxB,UAAM,OAAO,QAAQ,IAAI,KAAK,MAAM;AAEpC,QAAI,CAAC,KAAK,cAAc;AAEtB,iBAAW;AAAA,IACb,OAAO;AAEL,YAAM,SAAS,QAAQ,IAAI,KAAK,YAAY;AAC5C,UAAI,QAAQ;AACV,eAAO,SAAS,KAAK,IAAI;AAAA,MAC3B,OAAO;AAEL,gBAAQ,KAAK,iCAAiC,KAAK,MAAM,aAAa,KAAK,YAAY,GAAG;AAAA,MAC5F;AAAA,IACF;AAAA,EACF;AAEA,MAAI,CAAC,UAAU;AAEb,YAAQ,KAAK,4DAA4D;AACzE,eAAW;AAAA,MACT,SAAS;AAAA,MACT,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,UAAU,MAAM,KAAK,QAAQ,OAAO,CAAC,EAAE,OAAO,OAAK,CAAC,EAAE,KAAK,YAAY;AAAA,MACvE,MAAM,MAAM,CAAC;AAAA;AAAA,MACb,OAAO,CAAC;AAAA,IACV;AAAA,EACF;AAEA,SAAO;AACT;AAKA,SAAS,oBAAoB,MAAoC;AAC/D,QAAM,QAAQ,KAAK;AAGnB,QAAM,UAAU,MAAM,gBAAgB,KAAK,MAAM,cAAc,KAAK,KAAK;AAGzE,MAAI,OAAO;AACX,MAAI,KAAK,SAAS,aAAa;AAC7B,WAAO;AAAA,EACT,WAAW,KAAK,SAAS,eAAe;AACtC,WAAO;AAAA,EACT,WAAW,KAAK,KAAK,WAAW,iBAAiB,GAAG;AAClD,WAAO;AAAA,EACT;AAGA,MAAI,SAAkC;AACtC,MAAI,KAAK,WAAW,SAAS;AAC3B,aAAS;AAAA,EACX,WAAW,MAAM,qBAAqB,MAAM,MAAM;AAChD,aAAS;AAAA,EACX;AAGA,QAAM,QAAgC,CAAC;AAGvC,MAAI,MAAM,2BAA2B,GAAG;AACtC,QAAI;AACF,YAAM,eAAe,KAAK,MAAM,MAAM,2BAA2B,CAAC;AAAA,IACpE,QAAQ;AACN,YAAM,eAAe,MAAM,2BAA2B;AAAA,IACxD;AAAA,EACF;AAGA,MAAI,MAAM,oBAAoB,GAAG;AAC/B,QAAI;AACF,YAAM,SAAS,KAAK,MAAM,MAAM,oBAAoB,CAAC;AAAA,IACvD,QAAQ;AACN,YAAM,SAAS,MAAM,oBAAoB;AAAA,IAC3C;AAAA,EACF;AAGA,MAAI,KAAK,WAAW,WAAW,MAAM,mBAAmB,GAAG;AACzD,UAAM,SAAS,CAAC,MAAM,mBAAmB,KAAK,eAAe;AAAA,EAC/D;AAGA,QAAM,WAAW;AAAA,IACf,MAAM,MAAM,kBAAkB;AAAA,IAC9B,UAAU,KAAK;AAAA,IACf,UAAU,MAAM,qBAAqB;AAAA,EACvC;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,UAAU,CAAC;AAAA,IACX;AAAA,IACA;AAAA,EACF;AACF;AAYO,SAAS,sBAAsB,OAAyC;AAC7E,QAAM,YAA6B,CAAC;AAEpC,aAAW,QAAQ,OAAO;AAExB,eAAW,SAAS,KAAK,QAAQ;AAC/B,UAAI,MAAM,SAAS,kBAAkB;AACnC,cAAM,QAAQ,MAAM,cAAc,CAAC;AAEnC,cAAM,WAA0B;AAAA,UAC9B,SACE,MAAM,yBAAyB,KAAK,KAAK,WAAW,gBAAgB,KAAK;AAAA,UAC3E,WACE,MAAM,0BAA0B,KAAK,MAAM,aAAa,eAAe,MAAM,IAAI;AAAA,UACnF,gBAAgB,MAAM;AAAA,UACtB,SAAS,UAAU,MAAM,wBAAwB,GAAG,CAAC,CAAC;AAAA,UACtD,QAAQ,UAAU,MAAM,uBAAuB,GAAG,CAAC,CAAC;AAAA,QACtD;AAEA,kBAAU,KAAK,QAAQ;AAAA,MACzB;AAAA,IACF;AAAA,EACF;AAGA,YAAU,KAAK,CAAC,GAAG,MAAM,kBAAkB,EAAE,gBAAgB,EAAE,cAAc,CAAC;AAE9E,SAAO;AACT;AAYO,SAAS,gBAAgB,OAAyC;AACvE,QAAM,SAA0B,CAAC;AAEjC,aAAW,QAAQ,OAAO;AACxB,UAAM,UAAU,KAAK,WAAW,gBAAgB,KAAK,KAAK;AAG1D,WAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN;AAAA,MACA,WAAW,eAAe,KAAK,SAAS;AAAA,MACxC,gBAAgB,KAAK;AAAA,MACrB,UAAU;AAAA,QACR,MAAM,KAAK;AAAA,QACX,MAAM,KAAK,WAAW,kBAAkB;AAAA,MAC1C;AAAA,IACF,CAAC;AAGD,WAAO,KAAK;AAAA,MACV,MAAM,KAAK,WAAW,UAAU,iBAAiB;AAAA,MACjD;AAAA,MACA,WAAW,eAAe,KAAK,OAAO;AAAA,MACtC,gBAAgB,KAAK;AAAA,MACrB,UAAU,KAAK;AAAA,MACf,QAAQ,KAAK;AAAA,MACb,UAAU;AAAA,QACR,MAAM,KAAK;AAAA,MACb;AAAA,IACF,CAAC;AAGD,eAAW,OAAO,KAAK,QAAQ;AAC7B,aAAO,KAAK;AAAA,QACV,MAAM,IAAI,SAAS,mBAAmB,mBAAmB;AAAA,QACzD,SAAS,IAAI,aAAa,yBAAyB,KAAK;AAAA,QACxD,WAAW,IAAI,aAAa,eAAe,IAAI,IAAI;AAAA,QACnD,gBAAgB,IAAI;AAAA,QACpB,UAAU;AAAA,UACR,WAAW,IAAI;AAAA,UACf,YAAY,IAAI;AAAA,QAClB;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAGA,SAAO,KAAK,CAAC,GAAG,MAAM,kBAAkB,EAAE,gBAAgB,EAAE,cAAc,CAAC;AAE3E,SAAO;AACT;AASA,SAAS,kBAAkB,WAAqC;AAC9D,QAAM,CAAC,SAAS,KAAK,IAAI;AACzB,SAAO,UAAU,MAAO,QAAQ;AAClC;AAKA,SAAS,eAAe,WAAqC;AAC3D,QAAM,SAAS,kBAAkB,SAAS;AAC1C,SAAO,IAAI,KAAK,MAAM,EAAE,YAAY;AACtC;AAMA,SAAS,kBAAkB,GAAqB,GAA6B;AAC3E,MAAI,EAAE,CAAC,MAAM,EAAE,CAAC,GAAG;AACjB,WAAO,EAAE,CAAC,IAAI,EAAE,CAAC;AAAA,EACnB;AACA,SAAO,EAAE,CAAC,IAAI,EAAE,CAAC;AACnB;AAKA,SAAS,UAAa,OAAY,cAAoB;AACpD,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO;AAAA,EACT;AAEA,MAAI;AACF,WAAO,KAAK,MAAM,KAAK;AAAA,EACzB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AArfA;AAAA;AAAA;AAAA;","names":[]}
|