@obsunified/collector 2.0.1

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.
Files changed (254) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +60 -0
  3. package/dist/auth.d.ts +2 -0
  4. package/dist/auth.js +9 -0
  5. package/dist/auth.js.map +1 -0
  6. package/dist/chunk-UGWDOGGH.js +319 -0
  7. package/dist/chunk-UGWDOGGH.js.map +1 -0
  8. package/dist/chunk-XKQTVIRE.js +671 -0
  9. package/dist/chunk-XKQTVIRE.js.map +1 -0
  10. package/dist/index.d.ts +1197 -0
  11. package/dist/index.js +17707 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/ingest-auth-HFVO1zIp.d.ts +165 -0
  14. package/dist/openai-FJYOQ7WO.js +384 -0
  15. package/dist/openai-FJYOQ7WO.js.map +1 -0
  16. package/package.json +73 -0
  17. package/src/ai-parity/ai-parity.acceptance.test.ts +449 -0
  18. package/src/analyses/derive.ts +642 -0
  19. package/src/analyses/index.ts +50 -0
  20. package/src/analyses/investigations.ts +277 -0
  21. package/src/analyses/tier0.ts +568 -0
  22. package/src/auth/dashboard-auth.ts +269 -0
  23. package/src/auth/ingest-auth.ts +147 -0
  24. package/src/durable-objects/tail-hub.test.ts +32 -0
  25. package/src/durable-objects/tail-hub.ts +158 -0
  26. package/src/framework/collector.ts +467 -0
  27. package/src/framework/env.ts +90 -0
  28. package/src/framework/logger.ts +81 -0
  29. package/src/index.ts +211 -0
  30. package/src/lib/action-aggregates.ts +1031 -0
  31. package/src/lib/action-identity.ts +106 -0
  32. package/src/lib/actions-store.ts +397 -0
  33. package/src/lib/ai-pricing.ts +80 -0
  34. package/src/lib/ai-store/calls.ts +148 -0
  35. package/src/lib/ai-store/evaluations.test.ts +47 -0
  36. package/src/lib/ai-store/evaluations.ts +125 -0
  37. package/src/lib/ai-store/helpers.ts +37 -0
  38. package/src/lib/ai-store/retention.ts +17 -0
  39. package/src/lib/ai-store/sessions.ts +277 -0
  40. package/src/lib/ai-store/spans.ts +109 -0
  41. package/src/lib/ai-store/types.ts +112 -0
  42. package/src/lib/ai-store.ts +76 -0
  43. package/src/lib/alerts-store.ts +458 -0
  44. package/src/lib/analyses-runner.ts +439 -0
  45. package/src/lib/analyses-store.test.ts +243 -0
  46. package/src/lib/analyses-store.ts +454 -0
  47. package/src/lib/ask.test.ts +302 -0
  48. package/src/lib/ask.ts +390 -0
  49. package/src/lib/blob-store-s3.ts +213 -0
  50. package/src/lib/blob-store.ts +366 -0
  51. package/src/lib/eval-cases-store.test.ts +171 -0
  52. package/src/lib/eval-cases-store.ts +1033 -0
  53. package/src/lib/evidence-references.test.ts +10 -0
  54. package/src/lib/evidence-references.ts +684 -0
  55. package/src/lib/hash.ts +26 -0
  56. package/src/lib/identity-index/action-lookups.ts +805 -0
  57. package/src/lib/identity-index/constants.ts +1 -0
  58. package/src/lib/identity-index/key-lookups.ts +199 -0
  59. package/src/lib/identity-index/mappers.ts +349 -0
  60. package/src/lib/identity-index/types.ts +196 -0
  61. package/src/lib/identity-index/user-lookup.ts +137 -0
  62. package/src/lib/identity-index.test.ts +624 -0
  63. package/src/lib/identity-index.ts +85 -0
  64. package/src/lib/json.ts +39 -0
  65. package/src/lib/llm.ts +263 -0
  66. package/src/lib/logs-store.ts +188 -0
  67. package/src/lib/metrics-store.test.ts +189 -0
  68. package/src/lib/metrics-store.ts +312 -0
  69. package/src/lib/narrate-gate.test.ts +260 -0
  70. package/src/lib/narrate-gate.ts +253 -0
  71. package/src/lib/openai.test.ts +300 -0
  72. package/src/lib/openai.ts +512 -0
  73. package/src/lib/otlp.ts +212 -0
  74. package/src/lib/parse-pprof.test.ts +64 -0
  75. package/src/lib/parse-pprof.ts +184 -0
  76. package/src/lib/payload-capture.ts +22 -0
  77. package/src/lib/projects-store.ts +207 -0
  78. package/src/lib/propagation-metric.test.ts +196 -0
  79. package/src/lib/propagation-metric.ts +175 -0
  80. package/src/lib/sql-db-dialect.test.ts +22 -0
  81. package/src/lib/sql-db-postgres.ts +229 -0
  82. package/src/lib/sql-db.ts +166 -0
  83. package/src/lib/stage6.test.ts +216 -0
  84. package/src/lib/store/export.ts +65 -0
  85. package/src/lib/store/helpers.ts +367 -0
  86. package/src/lib/store/ingest.ts +68 -0
  87. package/src/lib/store/issues.ts +158 -0
  88. package/src/lib/store/overview.ts +127 -0
  89. package/src/lib/store/retention.ts +10 -0
  90. package/src/lib/store/service-map.ts +384 -0
  91. package/src/lib/store/trace-detail.bench.ts +76 -0
  92. package/src/lib/store/trace-detail.test.ts +410 -0
  93. package/src/lib/store/trace-detail.ts +427 -0
  94. package/src/lib/store/trace-queries.test.ts +29 -0
  95. package/src/lib/store/trace-queries.ts +89 -0
  96. package/src/lib/store-overview.test.ts +77 -0
  97. package/src/lib/store.ts +96 -0
  98. package/src/lib/tail-publisher.ts +64 -0
  99. package/src/lib/test-utils/mem-sql-db.test.ts +93 -0
  100. package/src/lib/test-utils/mem-sql-db.ts +86 -0
  101. package/src/lib/ua-parser.ts +61 -0
  102. package/src/lib/usage-store.ts +567 -0
  103. package/src/migrations/001_telemetry_spans.sql +28 -0
  104. package/src/migrations/002_usage_events.sql +24 -0
  105. package/src/migrations/003_usage_analytics_columns.sql +13 -0
  106. package/src/migrations/004_logs.sql +19 -0
  107. package/src/migrations/005_ai_calls.sql +25 -0
  108. package/src/migrations/006_user_profiles.sql +11 -0
  109. package/src/migrations/007_session_replay_metadata.sql +10 -0
  110. package/src/migrations/008_session_replay_storage_bytes.sql +1 -0
  111. package/src/migrations/009_projects_and_keys.sql +22 -0
  112. package/src/migrations/010_signal_project_id.sql +15 -0
  113. package/src/migrations/011_default_project_seed.sql +3 -0
  114. package/src/migrations/012_alert_rules.sql +17 -0
  115. package/src/migrations/013_alert_evaluations.sql +13 -0
  116. package/src/migrations/014_alert_state.sql +9 -0
  117. package/src/migrations/015_metrics.sql +58 -0
  118. package/src/migrations/016_metrics_exp_histogram_summary.sql +5 -0
  119. package/src/migrations/017_span_dropped_counts.sql +11 -0
  120. package/src/migrations/018_log_flags.sql +8 -0
  121. package/src/migrations/019_ai_span_payloads.sql +30 -0
  122. package/src/migrations/020_ai_span_evaluations.sql +33 -0
  123. package/src/migrations/021_ai_span_session.sql +13 -0
  124. package/src/migrations/022_span_log_session_id.sql +13 -0
  125. package/src/migrations/023_service_map_indexes.sql +32 -0
  126. package/src/migrations/024_analysis_results.sql +49 -0
  127. package/src/migrations/025_stage6_autopinning_alerts.sql +38 -0
  128. package/src/migrations/026_obs_dashboard_project_seed.sql +18 -0
  129. package/src/migrations/027_identity_propagation.sql +50 -0
  130. package/src/migrations/028_profile_blobs.sql +57 -0
  131. package/src/migrations/029_telemetry_sdk_name.sql +20 -0
  132. package/src/migrations/030_analysis_claim.sql +18 -0
  133. package/src/migrations/031_agent_action_graph.sql +154 -0
  134. package/src/migrations/032_payload_capture_flags.sql +8 -0
  135. package/src/migrations/033_eval_cases.sql +52 -0
  136. package/src/migrations/034_eval_case_results.sql +23 -0
  137. package/src/migrations/035_metric_exemplars.sql +33 -0
  138. package/src/migrations/036_trace_instrumentation_gaps.sql +19 -0
  139. package/src/migrations/037_eval_runs.sql +37 -0
  140. package/src/migrations/038_tool_call_evidence.sql +11 -0
  141. package/src/migrations/039_drop_trace_instrumentation_gaps.sql +11 -0
  142. package/src/migrations/040_evidence_retrieval_refs.sql +50 -0
  143. package/src/migrations-postgres/001_telemetry_spans.sql +33 -0
  144. package/src/migrations-postgres/002_usage_events.sql +24 -0
  145. package/src/migrations-postgres/003_usage_analytics_columns.sql +13 -0
  146. package/src/migrations-postgres/004_logs.sql +19 -0
  147. package/src/migrations-postgres/005_ai_calls.sql +25 -0
  148. package/src/migrations-postgres/006_user_profiles.sql +11 -0
  149. package/src/migrations-postgres/007_session_replay_metadata.sql +10 -0
  150. package/src/migrations-postgres/008_session_replay_storage_bytes.sql +2 -0
  151. package/src/migrations-postgres/009_projects_and_keys.sql +25 -0
  152. package/src/migrations-postgres/010_signal_project_id.sql +18 -0
  153. package/src/migrations-postgres/011_default_project_seed.sql +5 -0
  154. package/src/migrations-postgres/012_alert_rules.sql +17 -0
  155. package/src/migrations-postgres/013_alert_evaluations.sql +13 -0
  156. package/src/migrations-postgres/014_alert_state.sql +9 -0
  157. package/src/migrations-postgres/015_metrics.sql +47 -0
  158. package/src/migrations-postgres/016_metrics_exp_histogram_summary.sql +1 -0
  159. package/src/migrations-postgres/017_span_dropped_counts.sql +4 -0
  160. package/src/migrations-postgres/018_log_flags.sql +2 -0
  161. package/src/migrations-postgres/019_ai_span_payloads.sql +18 -0
  162. package/src/migrations-postgres/020_ai_span_evaluations.sql +21 -0
  163. package/src/migrations-postgres/021_ai_span_session.sql +7 -0
  164. package/src/migrations-postgres/022_span_log_session_id.sql +7 -0
  165. package/src/migrations-postgres/023_service_map_indexes.sql +9 -0
  166. package/src/migrations-postgres/024_analysis_results.sql +41 -0
  167. package/src/migrations-postgres/025_stage6_autopinning_alerts.sql +17 -0
  168. package/src/migrations-postgres/026_obs_dashboard_project_seed.sql +14 -0
  169. package/src/migrations-postgres/027_identity_propagation.sql +27 -0
  170. package/src/migrations-postgres/028_profile_blobs.sql +48 -0
  171. package/src/migrations-postgres/029_telemetry_sdk_name.sql +5 -0
  172. package/src/migrations-postgres/030_analysis_claim.sql +5 -0
  173. package/src/migrations-postgres/031_agent_action_graph.sql +190 -0
  174. package/src/migrations-postgres/032_payload_capture_flags.sql +8 -0
  175. package/src/migrations-postgres/033_eval_cases.sql +55 -0
  176. package/src/migrations-postgres/034_eval_case_results.sql +26 -0
  177. package/src/migrations-postgres/035_metric_exemplars.sql +31 -0
  178. package/src/migrations-postgres/036_trace_instrumentation_gaps.sql +19 -0
  179. package/src/migrations-postgres/037_eval_runs.sql +40 -0
  180. package/src/migrations-postgres/038_tool_call_evidence.sql +11 -0
  181. package/src/migrations-postgres/039_drop_trace_instrumentation_gaps.sql +11 -0
  182. package/src/migrations-postgres/040_evidence_retrieval_refs.sql +47 -0
  183. package/src/migrations-postgres/README.md +40 -0
  184. package/src/otlp/acceptance.test.ts +373 -0
  185. package/src/otlp/decode/body.ts +127 -0
  186. package/src/otlp/decode/logs.ts +96 -0
  187. package/src/otlp/decode/metrics.ts +402 -0
  188. package/src/otlp/decode/traces.ts +92 -0
  189. package/src/otlp/decode/values.ts +150 -0
  190. package/src/otlp/decode.test.ts +800 -0
  191. package/src/otlp/decode.ts +11 -0
  192. package/src/otlp/gen/opentelemetry/proto/collector/logs/v1/logs_service_pb.ts +132 -0
  193. package/src/otlp/gen/opentelemetry/proto/collector/metrics/v1/metrics_service_pb.ts +158 -0
  194. package/src/otlp/gen/opentelemetry/proto/collector/trace/v1/trace_service_pb.ts +158 -0
  195. package/src/otlp/gen/opentelemetry/proto/common/v1/common_pb.ts +289 -0
  196. package/src/otlp/gen/opentelemetry/proto/logs/v1/logs_pb.ts +397 -0
  197. package/src/otlp/gen/opentelemetry/proto/metrics/v1/metrics_pb.ts +1329 -0
  198. package/src/otlp/gen/opentelemetry/proto/resource/v1/resource_pb.ts +86 -0
  199. package/src/otlp/gen/opentelemetry/proto/trace/v1/trace_pb.ts +710 -0
  200. package/src/otlp/response.test.ts +158 -0
  201. package/src/otlp/response.ts +129 -0
  202. package/src/plugins/_context.ts +15 -0
  203. package/src/plugins/action-graph-processor/enrichers.ts +45 -0
  204. package/src/plugins/action-graph-processor/redaction.ts +84 -0
  205. package/src/plugins/action-graph-processor.test.ts +1023 -0
  206. package/src/plugins/action-graph-processor.ts +1172 -0
  207. package/src/plugins/action-routes.test.ts +716 -0
  208. package/src/plugins/action-routes.ts +343 -0
  209. package/src/plugins/ai-receiver.ts +254 -0
  210. package/src/plugins/ai-span-payloads-processor.test.ts +90 -0
  211. package/src/plugins/ai-span-payloads-processor.ts +180 -0
  212. package/src/plugins/alerts-evaluator.test.ts +91 -0
  213. package/src/plugins/alerts-evaluator.ts +328 -0
  214. package/src/plugins/alerts-routes.ts +101 -0
  215. package/src/plugins/analyses-routes.ts +138 -0
  216. package/src/plugins/ask-routes.ts +182 -0
  217. package/src/plugins/bot-filter.ts +19 -0
  218. package/src/plugins/connected-routes/manifest.ts +531 -0
  219. package/src/plugins/connected-routes.test.ts +1791 -0
  220. package/src/plugins/connected-routes.ts +1103 -0
  221. package/src/plugins/dashboard-routes.ts +41 -0
  222. package/src/plugins/default-span-enrichment.ts +92 -0
  223. package/src/plugins/eval-cases-routes.test.ts +391 -0
  224. package/src/plugins/eval-cases-routes.ts +529 -0
  225. package/src/plugins/evidence-retrieval-routes.benchmark.test.ts +245 -0
  226. package/src/plugins/evidence-retrieval-routes.test.ts +932 -0
  227. package/src/plugins/evidence-retrieval-routes.ts +2179 -0
  228. package/src/plugins/gen-ai-normalizer.test.ts +680 -0
  229. package/src/plugins/gen-ai-normalizer.ts +302 -0
  230. package/src/plugins/identity-receiver.ts +96 -0
  231. package/src/plugins/issue-enrichment.ts +132 -0
  232. package/src/plugins/issue-insights.ts +81 -0
  233. package/src/plugins/logs-receiver.ts +160 -0
  234. package/src/plugins/metrics-receiver.ts +91 -0
  235. package/src/plugins/onboarding-routes.ts +54 -0
  236. package/src/plugins/otlp-receiver.ts +124 -0
  237. package/src/plugins/platform-routes.ts +165 -0
  238. package/src/plugins/profile-routes.ts +468 -0
  239. package/src/plugins/projects-routes.ts +92 -0
  240. package/src/plugins/query-routes.ts +220 -0
  241. package/src/plugins/redaction-processor.ts +132 -0
  242. package/src/plugins/replay-query-routes.test.ts +38 -0
  243. package/src/plugins/replay-query-routes.ts +199 -0
  244. package/src/plugins/replay-receiver.ts +81 -0
  245. package/src/plugins/tail-routes.test.ts +47 -0
  246. package/src/plugins/tail-routes.ts +42 -0
  247. package/src/plugins/timeline-routes.ts +244 -0
  248. package/src/plugins/ua-enrichment.ts +27 -0
  249. package/src/plugins/usage-normalization.ts +46 -0
  250. package/src/plugins/usage-privacy.ts +55 -0
  251. package/src/plugins/usage-query-routes.ts +102 -0
  252. package/src/plugins/usage-receiver.ts +129 -0
  253. package/src/plugins/users-query-routes.ts +78 -0
  254. package/src/plugins/utm-enrichment.ts +36 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/evidence-references.ts","../src/lib/llm.ts"],"sourcesContent":["import type {\n\tAIEvaluationRecord,\n\tAlertEvaluation,\n\tAlertRule,\n\tAnalysisResult,\n\tAskEvidence,\n\tEvidenceCitation,\n\tEvidenceEntityKind,\n\tEvidenceNextPivot,\n\tEvidenceReference,\n\tJsonValue,\n} from \"@obsunified/types\";\n\nconst analysisRoute = (analysisId: string) =>\n\t`#/investigate/${encodeURIComponent(analysisId)}`;\n\nexport const analysisEvidenceReference = (\n\tevidence: AskEvidence,\n): EvidenceReference => {\n\tconst route = analysisRoute(evidence.analysisId);\n\tconst status = evidence.result?.status ?? \"unknown\";\n\treturn {\n\t\tevidenceId: `ask:analysis:${evidence.analysisId}`,\n\t\tentityKind: \"analysis\",\n\t\tentityId: evidence.analysisId,\n\t\troute,\n\t\tsource: \"ask.run_analysis\",\n\t\tconfidence: evidence.result ? 1 : 0.55,\n\t\treason: evidence.result\n\t\t\t? `Ask consulted analysis \"${evidence.definition.title}\" with latest status ${status}.`\n\t\t\t: `Ask consulted analysis \"${evidence.definition.title}\", but no latest result was available.`,\n\t\tcitations: [\n\t\t\t{\n\t\t\t\tlabel: evidence.definition.title,\n\t\t\t\tentityKind: \"analysis\",\n\t\t\t\tentityId: evidence.analysisId,\n\t\t\t\troute,\n\t\t\t},\n\t\t],\n\t\tsuggestedNextPivots: [\n\t\t\t{\n\t\t\t\tlabel: \"Open investigation\",\n\t\t\t\tentityKind: \"analysis\",\n\t\t\t\tentityId: evidence.analysisId,\n\t\t\t\troute,\n\t\t\t\treason:\n\t\t\t\t\t\"Review the analysis payload, narrative, and connected telemetry rail.\",\n\t\t\t},\n\t\t],\n\t};\n};\n\nexport const askEvidenceReferences = (\n\tevidence: Iterable<AskEvidence>,\n): EvidenceReference[] =>\n\t[...evidence].map((item) => analysisEvidenceReference(item));\n\nconst MAX_ANALYSIS_EVIDENCE_REFERENCES = 50;\n\ninterface EvidenceTable {\n\ttitle?: string | null;\n\theaders: string[];\n\trows: JsonValue[][];\n}\n\ninterface RowEntity {\n\tentityKind: EvidenceEntityKind;\n\tentityId: string;\n\troute: string;\n\tlabel: string;\n\tconfidence: number;\n\tcitations: EvidenceCitation[];\n\tsuggestedNextPivots: EvidenceNextPivot[];\n}\n\nconst isRecord = (value: unknown): value is Record<string, JsonValue> =>\n\t!!value && typeof value === \"object\" && !Array.isArray(value);\n\nconst asString = (value: JsonValue | undefined): string | null => {\n\tif (typeof value === \"string\" && value.trim()) return value.trim();\n\tif (typeof value === \"number\" && Number.isFinite(value)) return String(value);\n\treturn null;\n};\n\nconst normalizeHeader = (header: string) =>\n\theader\n\t\t.trim()\n\t\t.toLowerCase()\n\t\t.replace(/[\\s-]+/g, \"_\");\n\nconst valueFor = (\n\trow: JsonValue[],\n\theaderIndex: Map<string, number>,\n\tnames: string[],\n): string | null => {\n\tfor (const name of names) {\n\t\tconst index = headerIndex.get(name);\n\t\tif (index === undefined) continue;\n\t\tconst value = asString(row[index]);\n\t\tif (value) return value;\n\t}\n\treturn null;\n};\n\nconst traceRoute = (traceId: string) =>\n\t`#/traces/${encodeURIComponent(traceId)}`;\n\nconst spanRoute = (traceId: string, spanId: string) =>\n\t`${traceRoute(traceId)}#span=${encodeURIComponent(spanId)}`;\n\nexport const evidenceRouteFor = (\n\tentityKind: EvidenceEntityKind,\n\tentityId: string,\n\ttraceId?: string | null,\n) => {\n\tswitch (entityKind) {\n\t\tcase \"analysis\":\n\t\t\treturn analysisRoute(entityId);\n\t\tcase \"action\":\n\t\t\treturn `#/actions/${encodeURIComponent(entityId)}`;\n\t\tcase \"alert\":\n\t\t\treturn `#/alerts?alert=${encodeURIComponent(entityId)}`;\n\t\tcase \"agent_run\":\n\t\t\treturn `#/agent-runs/${encodeURIComponent(entityId)}`;\n\t\tcase \"ai_call\":\n\t\t\treturn `#/ai/calls/${encodeURIComponent(entityId)}`;\n\t\tcase \"eval\":\n\t\t\treturn `#/evals/${encodeURIComponent(entityId)}`;\n\t\tcase \"log\":\n\t\t\treturn `#/logs?id=${encodeURIComponent(entityId)}`;\n\t\tcase \"profile\":\n\t\t\treturn traceId\n\t\t\t\t? `#/profiles/${encodeURIComponent(entityId)}?trace_id=${encodeURIComponent(traceId)}`\n\t\t\t\t: `#/profiles/${encodeURIComponent(entityId)}`;\n\t\tcase \"replay\":\n\t\t\treturn `#/replay?session=${encodeURIComponent(entityId)}`;\n\t\tcase \"service\":\n\t\t\treturn `#/traces?service=${encodeURIComponent(entityId)}`;\n\t\tcase \"span\": {\n\t\t\tconst [tracePart, spanPart] = entityId.split(\":\");\n\t\t\treturn spanPart ? spanRoute(tracePart, spanPart) : `#/spans/${entityId}`;\n\t\t}\n\t\tcase \"tool_call\":\n\t\t\treturn `#/tool-calls/${encodeURIComponent(entityId)}`;\n\t\tcase \"trace\":\n\t\t\treturn traceRoute(entityId);\n\t\tcase \"docs\":\n\t\t\treturn entityId.startsWith(\"#/\") ? entityId : `#/docs/${entityId}`;\n\t}\n\tconst exhaustive: never = entityKind;\n\treturn exhaustive;\n};\n\nconst parseEvidenceTables = (\n\tpayload: Record<string, unknown>,\n): Array<{ sectionName: string; table: EvidenceTable }> => {\n\tconst evidence = payload.evidence;\n\tif (!isRecord(evidence)) return [];\n\n\tconst tables: Array<{ sectionName: string; table: EvidenceTable }> = [];\n\tfor (const [sectionName, section] of Object.entries(evidence)) {\n\t\tif (!isRecord(section)) continue;\n\t\tconst headers = section.headers;\n\t\tconst rows = section.rows;\n\t\tif (\n\t\t\t!Array.isArray(headers) ||\n\t\t\t!headers.every(\n\t\t\t\t(header): header is string => typeof header === \"string\",\n\t\t\t) ||\n\t\t\t!Array.isArray(rows)\n\t\t) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst normalizedRows = rows.filter((row): row is JsonValue[] =>\n\t\t\tArray.isArray(row),\n\t\t);\n\t\ttables.push({\n\t\t\tsectionName,\n\t\t\ttable: {\n\t\t\t\ttitle: asString(section.title),\n\t\t\t\theaders,\n\t\t\t\trows: normalizedRows,\n\t\t\t},\n\t\t});\n\t}\n\treturn tables;\n};\n\nconst instrumentationGapEvidenceReferences = (\n\tresult: Pick<AnalysisResult, \"analysisId\" | \"payload\">,\n): EvidenceReference[] => {\n\tconst gaps = result.payload.instrumentationGaps;\n\tif (!isRecord(gaps)) return [];\n\tconst traceId = asString(gaps.traceId);\n\tconst blindspots = gaps.blindspots;\n\tif (!traceId || !Array.isArray(blindspots)) return [];\n\n\treturn blindspots\n\t\t.filter(isRecord)\n\t\t.map((gap, index): EvidenceReference | null => {\n\t\t\tconst spanId = asString(gap.parentSpanId);\n\t\t\tif (!spanId) return null;\n\t\t\tconst serviceName = asString(gap.parentServiceName);\n\t\t\tconst spanName = asString(gap.parentSpanName);\n\t\t\tconst durationMs =\n\t\t\t\ttypeof gap.durationMs === \"number\" && Number.isFinite(gap.durationMs)\n\t\t\t\t\t? gap.durationMs\n\t\t\t\t\t: null;\n\t\t\tconst ratio =\n\t\t\t\ttypeof gap.ratioOfParent === \"number\" &&\n\t\t\t\tNumber.isFinite(gap.ratioOfParent)\n\t\t\t\t\t? gap.ratioOfParent\n\t\t\t\t\t: null;\n\t\t\tconst recommendation = asString(gap.recommendation);\n\t\t\tconst thresholdVersion =\n\t\t\t\tasString(gap.thresholdVersion) ??\n\t\t\t\t(isRecord(gaps.thresholds) ? asString(gaps.thresholds.version) : null);\n\t\t\tconst entityId = `${traceId}:${spanId}`;\n\t\t\tconst route = spanRoute(traceId, spanId);\n\t\t\tconst label = spanName ? `${spanName} (${spanId})` : `span ${spanId}`;\n\t\t\tconst durationText =\n\t\t\t\tdurationMs === null ? \"unknown duration\" : `${durationMs}ms`;\n\t\t\tconst ratioText =\n\t\t\t\tratio === null ? \"\" : `, ${(ratio * 100).toFixed(1)}% of parent`;\n\t\t\tconst thresholdText = thresholdVersion\n\t\t\t\t? ` Thresholds: ${thresholdVersion}.`\n\t\t\t\t: \"\";\n\t\t\tconst docsRoute = \"#/docs/howto/ebpf\";\n\t\t\treturn {\n\t\t\t\tevidenceId: `analysis:${result.analysisId}:instrumentation-gap:${index}:span:${entityId}`,\n\t\t\t\tentityKind: \"span\",\n\t\t\t\tentityId,\n\t\t\t\troute,\n\t\t\t\tsource: \"analysis.payload.instrumentationGaps\",\n\t\t\t\tconfidence: 0.9,\n\t\t\t\treason: `Analysis \"${result.analysisId}\" found uninstrumented self-time on ${label}: ${durationText}${ratioText}.${thresholdText}${recommendation ? ` ${recommendation}` : \"\"}`,\n\t\t\t\tcitations: [\n\t\t\t\t\t{\n\t\t\t\t\t\tlabel: `trace ${traceId}`,\n\t\t\t\t\t\tentityKind: \"trace\",\n\t\t\t\t\t\tentityId: traceId,\n\t\t\t\t\t\troute: traceRoute(traceId),\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tsuggestedNextPivots: [\n\t\t\t\t\t{\n\t\t\t\t\t\tlabel: \"Open uninstrumented span\",\n\t\t\t\t\t\tentityKind: \"span\",\n\t\t\t\t\t\tentityId,\n\t\t\t\t\t\troute,\n\t\t\t\t\t\treason:\n\t\t\t\t\t\t\t\"Inspect the parent span whose self-time suggests missing child instrumentation.\",\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tlabel: \"Open trace gap data\",\n\t\t\t\t\t\tentityKind: \"trace\",\n\t\t\t\t\t\tentityId: traceId,\n\t\t\t\t\t\troute: `${traceRoute(traceId)}/gaps`,\n\t\t\t\t\t\treason:\n\t\t\t\t\t\t\t\"Review all structured instrumentation blindspots for this trace.\",\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tlabel: \"Open profiler setup docs\",\n\t\t\t\t\t\tentityKind: \"docs\",\n\t\t\t\t\t\tentityId: \"howto/ebpf\",\n\t\t\t\t\t\troute: docsRoute,\n\t\t\t\t\t\treason:\n\t\t\t\t\t\t\tserviceName != null\n\t\t\t\t\t\t\t\t? `Use profiler or eBPF setup guidance to cover missing spans in ${serviceName}.`\n\t\t\t\t\t\t\t\t: \"Use profiler or eBPF setup guidance to cover missing spans.\",\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t};\n\t\t})\n\t\t.filter((ref): ref is EvidenceReference => ref !== null)\n\t\t.slice(0, MAX_ANALYSIS_EVIDENCE_REFERENCES);\n};\n\nconst entityFromRow = (\n\trow: JsonValue[],\n\theaders: string[],\n): RowEntity | null => {\n\tconst headerIndex = new Map(headers.map((header, index) => [header, index]));\n\tconst traceId = valueFor(row, headerIndex, [\"trace_id\", \"traceid\", \"trace\"]);\n\tconst spanId = valueFor(row, headerIndex, [\"span_id\", \"spanid\"]);\n\tconst logId = valueFor(row, headerIndex, [\"log_id\", \"logid\"]);\n\tconst profileId = valueFor(row, headerIndex, [\"profile_id\", \"profileid\"]);\n\tconst actionId = valueFor(row, headerIndex, [\"action_id\", \"actionid\"]);\n\tconst agentRunId = valueFor(row, headerIndex, [\n\t\t\"agent_run_id\",\n\t\t\"agentrunid\",\n\t\t\"agent_run\",\n\t]);\n\tconst toolCallId = valueFor(row, headerIndex, [\n\t\t\"tool_call_id\",\n\t\t\"toolcallid\",\n\t\t\"tool_call\",\n\t]);\n\tconst evalId = valueFor(row, headerIndex, [\"eval_id\", \"evalid\"]);\n\tconst service = valueFor(row, headerIndex, [\"service\", \"service_name\"]);\n\n\tconst traceCitation = traceId\n\t\t? [\n\t\t\t\t{\n\t\t\t\t\tlabel: `trace ${traceId}`,\n\t\t\t\t\tentityKind: \"trace\" as const,\n\t\t\t\t\tentityId: traceId,\n\t\t\t\t\troute: traceRoute(traceId),\n\t\t\t\t},\n\t\t\t]\n\t\t: [];\n\n\tif (traceId && spanId) {\n\t\tconst entityId = `${traceId}:${spanId}`;\n\t\treturn {\n\t\t\tentityKind: \"span\",\n\t\t\tentityId,\n\t\t\troute: spanRoute(traceId, spanId),\n\t\t\tlabel: `span ${spanId}`,\n\t\t\tconfidence: 0.95,\n\t\t\tcitations: traceCitation,\n\t\t\tsuggestedNextPivots: [\n\t\t\t\t{\n\t\t\t\t\tlabel: \"Open span\",\n\t\t\t\t\tentityKind: \"span\",\n\t\t\t\t\tentityId,\n\t\t\t\t\troute: spanRoute(traceId, spanId),\n\t\t\t\t\treason: \"Inspect the cited span in its trace context.\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tlabel: \"Open trace\",\n\t\t\t\t\tentityKind: \"trace\",\n\t\t\t\t\tentityId: traceId,\n\t\t\t\t\troute: traceRoute(traceId),\n\t\t\t\t\treason: \"Review the surrounding trace context.\",\n\t\t\t\t},\n\t\t\t],\n\t\t};\n\t}\n\n\tconst candidates: Array<{\n\t\tentityKind: EvidenceEntityKind;\n\t\tentityId: string | null;\n\t\tlabelPrefix: string;\n\t\tconfidence: number;\n\t}> = [\n\t\t{\n\t\t\tentityKind: \"trace\",\n\t\t\tentityId: traceId,\n\t\t\tlabelPrefix: \"trace\",\n\t\t\tconfidence: 0.95,\n\t\t},\n\t\t{\n\t\t\tentityKind: \"action\",\n\t\t\tentityId: actionId,\n\t\t\tlabelPrefix: \"action\",\n\t\t\tconfidence: 0.9,\n\t\t},\n\t\t{\n\t\t\tentityKind: \"tool_call\",\n\t\t\tentityId: toolCallId,\n\t\t\tlabelPrefix: \"tool call\",\n\t\t\tconfidence: 0.9,\n\t\t},\n\t\t{\n\t\t\tentityKind: \"agent_run\",\n\t\t\tentityId: agentRunId,\n\t\t\tlabelPrefix: \"agent run\",\n\t\t\tconfidence: 0.9,\n\t\t},\n\t\t{\n\t\t\tentityKind: \"profile\",\n\t\t\tentityId: profileId,\n\t\t\tlabelPrefix: \"profile\",\n\t\t\tconfidence: 0.9,\n\t\t},\n\t\t{\n\t\t\tentityKind: \"log\",\n\t\t\tentityId: logId,\n\t\t\tlabelPrefix: \"log\",\n\t\t\tconfidence: 0.85,\n\t\t},\n\t\t{\n\t\t\tentityKind: \"eval\",\n\t\t\tentityId: evalId,\n\t\t\tlabelPrefix: \"eval\",\n\t\t\tconfidence: 0.85,\n\t\t},\n\t\t{\n\t\t\tentityKind: \"service\",\n\t\t\tentityId: service,\n\t\t\tlabelPrefix: \"service\",\n\t\t\tconfidence: 0.7,\n\t\t},\n\t];\n\n\tconst candidate = candidates.find((item) => item.entityId);\n\tif (!candidate?.entityId) return null;\n\n\tconst route = evidenceRouteFor(\n\t\tcandidate.entityKind,\n\t\tcandidate.entityId,\n\t\ttraceId,\n\t);\n\treturn {\n\t\tentityKind: candidate.entityKind,\n\t\tentityId: candidate.entityId,\n\t\troute,\n\t\tlabel: `${candidate.labelPrefix} ${candidate.entityId}`,\n\t\tconfidence: candidate.confidence,\n\t\tcitations: traceCitation.filter(\n\t\t\t(citation) =>\n\t\t\t\tcitation.entityKind !== candidate.entityKind ||\n\t\t\t\tcitation.entityId !== candidate.entityId,\n\t\t),\n\t\tsuggestedNextPivots: [\n\t\t\t{\n\t\t\t\tlabel: `Open ${candidate.labelPrefix}`,\n\t\t\t\tentityKind: candidate.entityKind,\n\t\t\t\tentityId: candidate.entityId,\n\t\t\t\troute,\n\t\t\t\treason: \"Inspect the cited evidence row.\",\n\t\t\t},\n\t\t],\n\t};\n};\n\nexport const alertEvidenceReferences = (\n\trule: AlertRule,\n\tevaluation?: AlertEvaluation | null,\n): EvidenceReference[] => {\n\tconst route = evidenceRouteFor(\"alert\", rule.id);\n\tconst references: EvidenceReference[] = [\n\t\t{\n\t\t\tevidenceId: evaluation\n\t\t\t\t? `alert:${rule.id}:evaluation:${evaluation.id}`\n\t\t\t\t: `alert:${rule.id}:rule`,\n\t\t\tentityKind: \"alert\",\n\t\t\tentityId: rule.id,\n\t\t\troute,\n\t\t\tsource: evaluation\n\t\t\t\t? \"alert_evaluations\"\n\t\t\t\t: \"alerts.rule_evaluation_preview\",\n\t\t\tconfidence: evaluation ? 1 : 0.8,\n\t\t\treason: evaluation\n\t\t\t\t? `Alert \"${rule.name}\" evaluated to ${evaluation.value} and state ${evaluation.state}.`\n\t\t\t\t: `Alert \"${rule.name}\" was evaluated against its configured threshold.`,\n\t\t\tcitations: [\n\t\t\t\t{\n\t\t\t\t\tlabel: rule.name,\n\t\t\t\t\tentityKind: \"alert\",\n\t\t\t\t\tentityId: rule.id,\n\t\t\t\t\troute,\n\t\t\t\t},\n\t\t\t],\n\t\t\tsuggestedNextPivots: [\n\t\t\t\t{\n\t\t\t\t\tlabel: \"Open alert\",\n\t\t\t\t\tentityKind: \"alert\",\n\t\t\t\t\tentityId: rule.id,\n\t\t\t\t\troute,\n\t\t\t\t\treason: \"Inspect the alert rule, threshold, and recent evaluations.\",\n\t\t\t\t},\n\t\t\t],\n\t\t},\n\t];\n\n\tif (rule.analysisId) {\n\t\tconst analysisRouteValue = analysisRoute(rule.analysisId);\n\t\treferences.push({\n\t\t\tevidenceId: evaluation\n\t\t\t\t? `alert:${rule.id}:evaluation:${evaluation.id}:analysis:${rule.analysisId}`\n\t\t\t\t: `alert:${rule.id}:analysis:${rule.analysisId}`,\n\t\t\tentityKind: \"analysis\",\n\t\t\tentityId: rule.analysisId,\n\t\t\troute: analysisRouteValue,\n\t\t\tsource: \"alert_rules.analysis_id\",\n\t\t\tconfidence: 0.9,\n\t\t\treason: `Alert \"${rule.name}\" is bound to analysis \"${rule.analysisId}\".`,\n\t\t\tcitations: [\n\t\t\t\t{\n\t\t\t\t\tlabel: rule.name,\n\t\t\t\t\tentityKind: \"alert\",\n\t\t\t\t\tentityId: rule.id,\n\t\t\t\t\troute,\n\t\t\t\t},\n\t\t\t],\n\t\t\tsuggestedNextPivots: [\n\t\t\t\t{\n\t\t\t\t\tlabel: \"Open investigation\",\n\t\t\t\t\tentityKind: \"analysis\",\n\t\t\t\t\tentityId: rule.analysisId,\n\t\t\t\t\troute: analysisRouteValue,\n\t\t\t\t\treason: \"Review the analysis result that drives this alert.\",\n\t\t\t\t},\n\t\t\t],\n\t\t});\n\t}\n\n\treturn references;\n};\n\nexport const aiEvaluationEvidenceReferences = (\n\tevaluation: Pick<\n\t\tAIEvaluationRecord,\n\t\t\"evaluationId\" | \"traceId\" | \"spanId\" | \"name\" | \"source\"\n\t>,\n): EvidenceReference[] => {\n\tconst spanEntityId = `${evaluation.traceId}:${evaluation.spanId}`;\n\tconst spanRouteValue = evidenceRouteFor(\n\t\t\"span\",\n\t\tspanEntityId,\n\t\tevaluation.traceId,\n\t);\n\treturn [\n\t\t{\n\t\t\tevidenceId: `ai-evaluation:${evaluation.evaluationId}`,\n\t\t\tentityKind: \"eval\",\n\t\t\tentityId: evaluation.evaluationId,\n\t\t\troute: evidenceRouteFor(\"eval\", evaluation.evaluationId),\n\t\t\tsource: `ai_span_evaluations.${evaluation.source}`,\n\t\t\tconfidence: 0.95,\n\t\t\treason: `AI evaluation \"${evaluation.name}\" graded span \"${evaluation.spanId}\".`,\n\t\t\tcitations: [\n\t\t\t\t{\n\t\t\t\t\tlabel: `span ${evaluation.spanId}`,\n\t\t\t\t\tentityKind: \"span\",\n\t\t\t\t\tentityId: spanEntityId,\n\t\t\t\t\troute: spanRouteValue,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tlabel: `trace ${evaluation.traceId}`,\n\t\t\t\t\tentityKind: \"trace\",\n\t\t\t\t\tentityId: evaluation.traceId,\n\t\t\t\t\troute: evidenceRouteFor(\"trace\", evaluation.traceId),\n\t\t\t\t},\n\t\t\t],\n\t\t\tsuggestedNextPivots: [\n\t\t\t\t{\n\t\t\t\t\tlabel: \"Open span\",\n\t\t\t\t\tentityKind: \"span\",\n\t\t\t\t\tentityId: spanEntityId,\n\t\t\t\t\troute: spanRouteValue,\n\t\t\t\t\treason: \"Inspect the evaluated AI span and its surrounding trace.\",\n\t\t\t\t},\n\t\t\t],\n\t\t},\n\t];\n};\n\nexport interface SourceEvidenceLinks {\n\tsourceAgentRunId?: string | null;\n\tsourceActionId?: string | null;\n\tsourceToolCallId?: string | null;\n\tsourceTraceId?: string | null;\n\tsourceSpanId?: string | null;\n}\n\nexport const sourceLinkEvidenceReferences = (\n\tsource: {\n\t\tsourceLabel: string;\n\t\tsourceId: string;\n\t\tsourceKind: \"eval_case\" | \"eval_case_result\" | \"eval_run\";\n\t\tsourceRoute?: string | null;\n\t\tsourceName?: string | null;\n\t},\n\tlinks: SourceEvidenceLinks,\n): EvidenceReference[] => {\n\tconst citations: EvidenceCitation[] = [];\n\tif (source.sourceRoute) {\n\t\tcitations.push({\n\t\t\tlabel: source.sourceName ?? source.sourceId,\n\t\t\tentityKind: \"eval\",\n\t\t\tentityId: source.sourceId,\n\t\t\troute: source.sourceRoute,\n\t\t});\n\t}\n\n\tconst candidates: Array<{\n\t\tkind: EvidenceEntityKind;\n\t\tid: string | null | undefined;\n\t\tlabel: string;\n\t\tconfidence: number;\n\t}> = [\n\t\t{\n\t\t\tkind: \"action\",\n\t\t\tid: links.sourceActionId,\n\t\t\tlabel: \"source action\",\n\t\t\tconfidence: 0.92,\n\t\t},\n\t\t{\n\t\t\tkind: \"tool_call\",\n\t\t\tid: links.sourceToolCallId,\n\t\t\tlabel: \"source tool call\",\n\t\t\tconfidence: 0.9,\n\t\t},\n\t\t{\n\t\t\tkind: \"agent_run\",\n\t\t\tid: links.sourceAgentRunId,\n\t\t\tlabel: \"source agent run\",\n\t\t\tconfidence: 0.9,\n\t\t},\n\t\t{\n\t\t\tkind: \"span\",\n\t\t\tid:\n\t\t\t\tlinks.sourceTraceId && links.sourceSpanId\n\t\t\t\t\t? `${links.sourceTraceId}:${links.sourceSpanId}`\n\t\t\t\t\t: null,\n\t\t\tlabel: \"source span\",\n\t\t\tconfidence: 0.9,\n\t\t},\n\t\t{\n\t\t\tkind: \"trace\",\n\t\t\tid: links.sourceTraceId,\n\t\t\tlabel: \"source trace\",\n\t\t\tconfidence: 0.88,\n\t\t},\n\t];\n\n\treturn candidates\n\t\t.filter((candidate): candidate is typeof candidate & { id: string } =>\n\t\t\tBoolean(candidate.id),\n\t\t)\n\t\t.map((candidate) => {\n\t\t\tconst route = evidenceRouteFor(\n\t\t\t\tcandidate.kind,\n\t\t\t\tcandidate.id,\n\t\t\t\tlinks.sourceTraceId,\n\t\t\t);\n\t\t\treturn {\n\t\t\t\tevidenceId: `${source.sourceKind}:${source.sourceId}:${candidate.kind}:${candidate.id}`,\n\t\t\t\tentityKind: candidate.kind,\n\t\t\t\tentityId: candidate.id,\n\t\t\t\troute,\n\t\t\t\tsource: `${source.sourceKind}.source_links`,\n\t\t\t\tconfidence: candidate.confidence,\n\t\t\t\treason: `${source.sourceLabel} links to ${candidate.label} \"${candidate.id}\".`,\n\t\t\t\tcitations,\n\t\t\t\tsuggestedNextPivots: [\n\t\t\t\t\t{\n\t\t\t\t\t\tlabel: `Open ${candidate.label}`,\n\t\t\t\t\t\tentityKind: candidate.kind,\n\t\t\t\t\t\tentityId: candidate.id,\n\t\t\t\t\t\troute,\n\t\t\t\t\t\treason:\n\t\t\t\t\t\t\t\"Inspect the production evidence behind this evaluation item.\",\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t} satisfies EvidenceReference;\n\t\t});\n};\n\nexport const analysisResultEvidenceReferences = (\n\tresult: Pick<AnalysisResult, \"analysisId\" | \"payload\">,\n): EvidenceReference[] => {\n\tconst references: EvidenceReference[] = [\n\t\t...instrumentationGapEvidenceReferences(result),\n\t];\n\tfor (const { sectionName, table } of parseEvidenceTables(result.payload)) {\n\t\tconst headers = table.headers.map(normalizeHeader);\n\t\tfor (const [rowIndex, row] of table.rows.entries()) {\n\t\t\tif (references.length >= MAX_ANALYSIS_EVIDENCE_REFERENCES) {\n\t\t\t\treturn references;\n\t\t\t}\n\t\t\tconst entity = entityFromRow(row, headers);\n\t\t\tif (!entity) continue;\n\n\t\t\tconst title = table.title ?? sectionName;\n\t\t\treferences.push({\n\t\t\t\tevidenceId: `analysis:${result.analysisId}:${sectionName}:${rowIndex}:${entity.entityKind}:${entity.entityId}`,\n\t\t\t\tentityKind: entity.entityKind,\n\t\t\t\tentityId: entity.entityId,\n\t\t\t\troute: entity.route,\n\t\t\t\tsource: `analysis.payload.evidence.${sectionName}`,\n\t\t\t\tconfidence: entity.confidence,\n\t\t\t\treason: `Analysis \"${result.analysisId}\" cited ${entity.label} in \"${title}\" row ${rowIndex + 1}.`,\n\t\t\t\tcitations: entity.citations,\n\t\t\t\tsuggestedNextPivots: entity.suggestedNextPivots,\n\t\t\t});\n\t\t}\n\t}\n\treturn references;\n};\n","/**\n * RFC 0002 Stage 3 — narrative LLM adapter.\n *\n * Single-provider for v1 (Anthropic). The shape is \"stuff in a system\n * prompt that bakes the rendering rules, ask for one declarative\n * sentence, return text\" — a different provider plugs in here without\n * changing callers.\n *\n * Rendering rules from the RFC's UX section, enforced at the system\n * prompt layer so the model can't accidentally drift:\n *\n * 1. Never start with \"I\" or \"Here's\" — declarative, not chatbot.\n * 2. Always cite — if the prompt mentions \"the trace\", a trace_id\n * must follow inline.\n * 3. Include a time anchor (\"starting 8 min ago\", \"since 14:32\").\n * 4. ≤2 sentences. We're labelling tiles, not writing essays.\n */\n\nimport type { AnalysisDefinition, AnalysisResult } from \"@obsunified/types\";\nimport type { ChildSpanRunner } from \"../framework/logger\";\n\nexport interface NarrativeRequest {\n\tdefinition: AnalysisDefinition;\n\tcurrent: AnalysisResult;\n\tprevious: AnalysisResult | null;\n}\n\n/**\n * Provider discriminator. The collector picks one based on which API key\n * env var is set (OpenAI takes precedence when both are set, since users\n * who configure both presumably did so deliberately for OpenAI).\n *\n * anthropic → POST https://api.anthropic.com/v1/messages\n * openai → POST https://api.openai.com/v1/chat/completions\n * (apiUrl override lets you point at openrouter, vLLM,\n * azure-openai-compat, ollama, etc.)\n */\nexport type LlmProvider = \"anthropic\" | \"openai\";\n\nexport interface LlmConfig {\n\tprovider: LlmProvider;\n\tapiKey: string;\n\t/** Model id. Provider-specific. e.g. \"claude-haiku-4-5\" / \"gpt-4o-mini\". */\n\tmodel: string;\n\t/** Override base URL — Anthropic-compatible or OpenAI-compatible. */\n\tapiUrl?: string;\n\t/** Optional abort signal used to cap provider calls from schedulers. */\n\tsignal?: AbortSignal;\n\t/**\n\t * Optional child-span runner. When set, every LLM API call (and tool\n\t * dispatch in the agent loop) is wrapped in an OpenInference-shaped span\n\t * with `gen_ai.system`, `gen_ai.request.model`, and post-call usage\n\t * attributes. Wired by the worker entrypoint via `runtime.withChildSpan`.\n\t */\n\ttracer?: ChildSpanRunner;\n}\n\nconst DEFAULT_API_URL = \"https://api.anthropic.com/v1/messages\";\n\nconst SYSTEM_PROMPT = `You write one-sentence operational narratives for a self-hosted observability dashboard.\n\nHard rules — violations are unusable:\n1. Never start with \"I\", \"Here's\", \"We\", \"It looks\", or any first-person/chatbot framing. Write declarative statements about the system.\n2. If you reference a trace, span, log, or service, include the supplied identifier inline (e.g. \"trace abc12345\").\n3. Include a time anchor: \"starting Nm ago\", \"for the last Nm\", \"since HH:MM\" — pull from the inputs.\n4. ≤ 2 sentences total, ≤ 220 characters. Numbers belong in the sentence, not in a separate label.\n5. If the inputs are insufficient to write a useful sentence, output exactly the string NO_NARRATIVE (no quotes).\n\nTone: like a senior on-call writing a Slack message. Specific, blunt, no hedging, no advice (\"you should...\"), no questions.`;\n\nconst renderTemplate = (\n\ttemplate: string,\n\tdef: AnalysisDefinition,\n\tr: AnalysisResult,\n): string => {\n\tconst scope = (def.scope ?? {}) as Record<string, unknown>;\n\tconst traceIds = Array.isArray(r.payload?.trace_ids)\n\t\t? (r.payload.trace_ids as unknown[])\n\t\t\t\t.filter((x): x is string => typeof x === \"string\")\n\t\t\t\t.slice(0, 5)\n\t\t\t\t.join(\", \")\n\t\t: \"\";\n\tconst subs: Record<string, string> = {\n\t\ttitle: def.title,\n\t\tstatus: r.status,\n\t\tprimary: r.primaryValue === null ? \"n/a\" : String(r.primaryValue),\n\t\tbaseline: r.baselineValue === null ? \"n/a\" : String(r.baselineValue),\n\t\tdelta_pct: r.deltaPct === null ? \"n/a\" : `${r.deltaPct.toFixed(1)}%`,\n\t\tservice: typeof scope.service === \"string\" ? scope.service : \"\",\n\t\ttrace_ids: traceIds,\n\t};\n\treturn template.replace(/\\{\\{(\\w+)\\}\\}/g, (_, k) => subs[k] ?? \"\");\n};\n\nconst buildUserPrompt = ({\n\tdefinition,\n\tcurrent,\n\tprevious,\n}: NarrativeRequest): string => {\n\tconst lines: string[] = [];\n\tconst tmplRendered = renderTemplate(\n\t\tdefinition.narrate?.prompt ?? \"\",\n\t\tdefinition,\n\t\tcurrent,\n\t).trim();\n\tif (tmplRendered) lines.push(tmplRendered);\n\n\tlines.push(\"\");\n\tlines.push(\"Current state:\");\n\tlines.push(`- panel: ${definition.title}`);\n\tlines.push(`- status: ${current.status}`);\n\tif (current.primaryValue !== null)\n\t\tlines.push(`- primary: ${current.primaryValue}`);\n\tif (current.baselineValue !== null)\n\t\tlines.push(`- baseline (1h): ${current.baselineValue}`);\n\tif (current.deltaPct !== null)\n\t\tlines.push(`- delta vs baseline: ${current.deltaPct.toFixed(1)}%`);\n\n\tconst since = previous\n\t\t? Math.max(\n\t\t\t\t1,\n\t\t\t\tMath.round(\n\t\t\t\t\t(new Date(current.generatedAt).getTime() -\n\t\t\t\t\t\tnew Date(previous.generatedAt).getTime()) /\n\t\t\t\t\t\t60000,\n\t\t\t\t),\n\t\t\t)\n\t\t: null;\n\tif (since !== null) {\n\t\tlines.push(\n\t\t\t`- previous run was ${since} minute${since === 1 ? \"\" : \"s\"} ago, status=${previous?.status ?? \"n/a\"}`,\n\t\t);\n\t}\n\n\tconst payloadKeys = Object.keys(current.payload ?? {}).filter(\n\t\t(k) => k !== \"sparkline\",\n\t);\n\tif (payloadKeys.length > 0) {\n\t\tlines.push(\"\");\n\t\tlines.push(\"Payload details:\");\n\t\tfor (const k of payloadKeys) {\n\t\t\tconst v = current.payload[k];\n\t\t\tconst rendered =\n\t\t\t\ttypeof v === \"string\" || typeof v === \"number\" || typeof v === \"boolean\"\n\t\t\t\t\t? String(v)\n\t\t\t\t\t: JSON.stringify(v);\n\t\t\tif (rendered.length <= 200) lines.push(`- ${k}: ${rendered}`);\n\t\t}\n\t}\n\n\tlines.push(\"\");\n\tlines.push(\n\t\t\"Output: a single sentence (max two), no preamble, no quotes, no Markdown.\",\n\t);\n\treturn lines.join(\"\\n\");\n};\n\nexport class LlmCallError extends Error {\n\treadonly status: number | null;\n\tconstructor(message: string, status: number | null = null) {\n\t\tsuper(message);\n\t\tthis.name = \"LlmCallError\";\n\t\tthis.status = status;\n\t}\n}\n\n/**\n * Provider dispatcher for narrative generation.\n *\n * Returns `null` when:\n * - the model returns the sentinel NO_NARRATIVE\n * - the response is empty\n * Throws `LlmCallError` on non-2xx HTTP responses so the runner can\n * count the failure against the budget without writing a bogus narrative.\n */\nexport async function generateNarrative(\n\treq: NarrativeRequest,\n\tconfig: LlmConfig,\n): Promise<string | null> {\n\tif (config.provider === \"openai\") {\n\t\t// Lazy import to keep the Anthropic-only path zero-cost on the worker\n\t\t// boot and avoid a circular import between llm.ts and openai.ts.\n\t\tconst { generateNarrativeOpenAI } = await import(\"./openai\");\n\t\treturn generateNarrativeOpenAI(req, config);\n\t}\n\treturn generateNarrativeAnthropic(req, config);\n}\n\nasync function generateNarrativeAnthropic(\n\treq: NarrativeRequest,\n\tconfig: LlmConfig,\n): Promise<string | null> {\n\tconst url = config.apiUrl ?? DEFAULT_API_URL;\n\tconst userPrompt = buildUserPrompt(req);\n\n\tconst tracer = config.tracer;\n\tconst callLLM = async (\n\t\tspan: { setAttribute(k: string, v: unknown): void } | null,\n\t): Promise<string | null> => {\n\t\tconst response = await fetch(url, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: {\n\t\t\t\t\"x-api-key\": config.apiKey,\n\t\t\t\t\"anthropic-version\": \"2023-06-01\",\n\t\t\t\t\"content-type\": \"application/json\",\n\t\t\t},\n\t\t\tbody: JSON.stringify({\n\t\t\t\tmodel: config.model,\n\t\t\t\tmax_tokens: 200,\n\t\t\t\tsystem: SYSTEM_PROMPT,\n\t\t\t\tmessages: [{ role: \"user\", content: userPrompt }],\n\t\t\t}),\n\t\t\tsignal: config.signal,\n\t\t});\n\t\tif (span) {\n\t\t\tspan.setAttribute(\"http.response.status_code\", response.status);\n\t\t}\n\n\t\tif (!response.ok) {\n\t\t\tconst text = await response.text().catch(() => \"\");\n\t\t\tthrow new LlmCallError(\n\t\t\t\t`anthropic ${response.status}: ${text.slice(0, 200)}`,\n\t\t\t\tresponse.status,\n\t\t\t);\n\t\t}\n\n\t\tconst body = (await response.json()) as {\n\t\t\tcontent?: Array<{ type?: string; text?: string }>;\n\t\t\tusage?: { input_tokens?: number; output_tokens?: number };\n\t\t\tstop_reason?: string;\n\t\t};\n\t\tif (body.stop_reason === \"max_tokens\") {\n\t\t\tthrow new LlmCallError(\"anthropic response truncated by max_tokens\");\n\t\t}\n\t\tif (span && body.usage) {\n\t\t\tif (typeof body.usage.input_tokens === \"number\")\n\t\t\t\tspan.setAttribute(\"gen_ai.usage.input_tokens\", body.usage.input_tokens);\n\t\t\tif (typeof body.usage.output_tokens === \"number\")\n\t\t\t\tspan.setAttribute(\n\t\t\t\t\t\"gen_ai.usage.output_tokens\",\n\t\t\t\t\tbody.usage.output_tokens,\n\t\t\t\t);\n\t\t}\n\t\tconst text = (body.content ?? [])\n\t\t\t.filter((block) => block?.type === \"text\")\n\t\t\t.map((block) => block?.text ?? \"\")\n\t\t\t.join(\"\")\n\t\t\t.trim();\n\n\t\tif (!text || text === \"NO_NARRATIVE\") return null;\n\t\t// Strip any accidental wrapping quotes the model might add despite the\n\t\t// system prompt.\n\t\treturn text.replace(/^[\"']|[\"']$/g, \"\").trim();\n\t};\n\n\tif (!tracer) return callLLM(null);\n\treturn tracer(\"llm.anthropic.messages\", async (span) => callLLM(span), {\n\t\t\"openinference.span.kind\": \"LLM\",\n\t\t\"gen_ai.system\": \"anthropic\",\n\t\t\"gen_ai.request.model\": config.model,\n\t\t\"gen_ai.request.max_tokens\": 200,\n\t});\n}\n"],"mappings":"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","names":["text"]}