@obsunified/collector 2.1.1 → 2.2.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.
- package/dist/{chunk-XKQTVIRE.js → chunk-ZSF5XP3B.js} +8 -2
- package/dist/chunk-ZSF5XP3B.js.map +1 -0
- package/dist/index.d.ts +359 -2
- package/dist/index.js +5000 -334
- package/dist/index.js.map +1 -1
- package/dist/{openai-FJYOQ7WO.js → openai-GSJZGFGL.js} +2 -2
- package/package.json +2 -2
- package/src/__tests__/autosre-closed-loop.test.ts +2222 -0
- package/src/index.ts +43 -0
- package/src/lib/analyses-runner.ts +46 -0
- package/src/lib/autosre-change-correlation.ts +807 -0
- package/src/lib/autosre-findings.ts +763 -0
- package/src/lib/autosre-ontology-cache.test.ts +78 -0
- package/src/lib/autosre-ontology.ts +938 -0
- package/src/lib/autosre-remediation-gate.ts +2127 -0
- package/src/lib/eval-cases-store.ts +63 -1
- package/src/lib/evidence-references.ts +6 -0
- package/src/lib/housekeeping.test.ts +150 -0
- package/src/lib/housekeeping.ts +109 -1
- package/src/lib/store/ingest.ts +46 -6
- package/src/migrations/050_autosre_causal_ontology.sql +216 -0
- package/src/migrations/051_incident_active_dedup_unique.sql +31 -0
- package/src/migrations/alert-state-notified.test.ts +3 -3
- package/src/migrations/parity.test.ts +4 -0
- package/src/migrations/project-scoped-keys.test.ts +1 -1
- package/src/migrations-postgres/050_autosre_causal_ontology.sql +216 -0
- package/src/migrations-postgres/051_incident_active_dedup_unique.sql +31 -0
- package/src/plugins/alerts-evaluator.ts +29 -1
- package/src/plugins/autosre-routes.ts +1134 -0
- package/src/plugins/connected-routes/manifest.ts +10 -1
- package/src/plugins/connected-routes.ts +241 -0
- package/src/plugins/default-span-enrichment.ts +13 -1
- package/src/plugins/eval-cases-routes.ts +57 -2
- package/src/plugins/evidence-retrieval-routes.test.ts +32 -0
- package/src/plugins/evidence-retrieval-routes.ts +643 -5
- package/dist/chunk-XKQTVIRE.js.map +0 -1
- /package/dist/{openai-FJYOQ7WO.js.map → openai-GSJZGFGL.js.map} +0 -0
|
@@ -82,6 +82,12 @@ var evidenceRouteFor = (entityKind, entityId, traceId) => {
|
|
|
82
82
|
return traceRoute(entityId);
|
|
83
83
|
case "docs":
|
|
84
84
|
return entityId.startsWith("#/") ? entityId : `#/docs/${entityId}`;
|
|
85
|
+
case "change":
|
|
86
|
+
return `#/changes/${encodeURIComponent(entityId)}`;
|
|
87
|
+
case "finding":
|
|
88
|
+
return `#/findings/${encodeURIComponent(entityId)}`;
|
|
89
|
+
case "incident":
|
|
90
|
+
return `#/incidents/${encodeURIComponent(entityId)}`;
|
|
85
91
|
}
|
|
86
92
|
const exhaustive = entityKind;
|
|
87
93
|
return exhaustive;
|
|
@@ -598,7 +604,7 @@ var LlmCallError = class extends Error {
|
|
|
598
604
|
};
|
|
599
605
|
async function generateNarrative(req, config) {
|
|
600
606
|
if (config.provider === "openai") {
|
|
601
|
-
const { generateNarrativeOpenAI } = await import("./openai-
|
|
607
|
+
const { generateNarrativeOpenAI } = await import("./openai-GSJZGFGL.js");
|
|
602
608
|
return generateNarrativeOpenAI(req, config);
|
|
603
609
|
}
|
|
604
610
|
return generateNarrativeAnthropic(req, config);
|
|
@@ -668,4 +674,4 @@ export {
|
|
|
668
674
|
LlmCallError,
|
|
669
675
|
generateNarrative
|
|
670
676
|
};
|
|
671
|
-
//# sourceMappingURL=chunk-
|
|
677
|
+
//# sourceMappingURL=chunk-ZSF5XP3B.js.map
|
|
@@ -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\tcase \"change\":\n\t\t\treturn `#/changes/${encodeURIComponent(entityId)}`;\n\t\tcase \"finding\":\n\t\t\treturn `#/findings/${encodeURIComponent(entityId)}`;\n\t\tcase \"incident\":\n\t\t\treturn `#/incidents/${encodeURIComponent(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"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import * as hono from 'hono';
|
|
|
2
2
|
import { MiddlewareHandler, Hono, Context } from 'hono';
|
|
3
3
|
import * as hono_types from 'hono/types';
|
|
4
4
|
import * as _obsunified_types from '@obsunified/types';
|
|
5
|
-
import { StoredSpan, TelemetryOverviewOptions, TelemetryOverviewResponse, TelemetryTraceDetailResponse, TelemetryInstrumentationGapsResponse, TelemetryIssueOptions, TelemetryIssueOverviewResponse, TelemetryIssueDetailResponse, UsageEventRecord, UsageOverviewOptions, UsageOverviewResponse, UsageSessionDetailResponse, UsageSessionSummary, AlertRuleInput, AlertRule, AlertStateRow, AlertState, AlertEvaluationsListResponse, AlertComparison, JsonValue, EvidenceReference, EvidenceReferenceContract, Project, IngestKey, IngestKeyWithPlaintext } from '@obsunified/types';
|
|
5
|
+
import { StoredSpan, TelemetryOverviewOptions, TelemetryOverviewResponse, TelemetryTraceDetailResponse, TelemetryInstrumentationGapsResponse, TelemetryIssueOptions, TelemetryIssueOverviewResponse, TelemetryIssueDetailResponse, UsageEventRecord, UsageOverviewOptions, UsageOverviewResponse, UsageSessionDetailResponse, UsageSessionSummary, AlertRuleInput, AlertRule, AlertStateRow, AlertState, AlertEvaluationsListResponse, AlertComparison, ChangeBlastRadiusResult, ContentionCandidate, ChangeEvent, ChangeEventInput, InvestigationFinding, InvestigationFindingInput, OntologyNode, OntologyEdge, CanonicalNodeIdentity, OntologyGraphResponse, RelationshipCertainty, CausalTriple, IncidentInvestigation, RemediationProposal, EvidenceEntityKind, RemediationProposalInput, JsonValue, EvidenceReference, EvidenceReferenceContract, Project, IngestKey, IngestKeyWithPlaintext } from '@obsunified/types';
|
|
6
6
|
import { b as CollectorEnv, L as Logger, C as ChildSpanRunner, c as CollectorRouteContext } from './ingest-auth-C5-8Mly5.js';
|
|
7
7
|
export { a as CollectorApp, d as consoleLogger, e as createIngestAuth } from './ingest-auth-C5-8Mly5.js';
|
|
8
8
|
import { ActionConfidence } from '@obsunified/types/constants';
|
|
@@ -150,6 +150,14 @@ interface HousekeepingSummary {
|
|
|
150
150
|
metric_series: number;
|
|
151
151
|
evidence_refs: number;
|
|
152
152
|
evidence_ref_expansions: number;
|
|
153
|
+
ontology_edges?: number;
|
|
154
|
+
ontology_nodes?: number;
|
|
155
|
+
change_events?: number;
|
|
156
|
+
incident_investigations?: number;
|
|
157
|
+
/** `leased` incidents whose lease expired and were returned to `open` (RFC 0013 § 11). */
|
|
158
|
+
incident_leases_reaped?: number;
|
|
159
|
+
investigation_findings?: number;
|
|
160
|
+
remediation_proposals?: number;
|
|
153
161
|
}
|
|
154
162
|
|
|
155
163
|
interface ServiceMapOptions {
|
|
@@ -543,6 +551,8 @@ declare const analysesRoutesPlugin: CollectorPlugin;
|
|
|
543
551
|
|
|
544
552
|
declare const askRoutesPlugin: CollectorPlugin;
|
|
545
553
|
|
|
554
|
+
declare const autosreRoutesPlugin: CollectorPlugin;
|
|
555
|
+
|
|
546
556
|
declare const botFilterPlugin: CollectorPlugin;
|
|
547
557
|
|
|
548
558
|
type CausalConfidence = ActionConfidence;
|
|
@@ -1072,6 +1082,337 @@ declare class AlertsStore {
|
|
|
1072
1082
|
private evaluateAIRule;
|
|
1073
1083
|
}
|
|
1074
1084
|
|
|
1085
|
+
declare function recordChangeEvent(db: SqlDb, projectId: string, input: ChangeEventInput): Promise<ChangeEvent>;
|
|
1086
|
+
declare function listChangeEvents(db: SqlDb, options: {
|
|
1087
|
+
projectId: string;
|
|
1088
|
+
environment?: string;
|
|
1089
|
+
serviceName?: string;
|
|
1090
|
+
targetNodeId?: string;
|
|
1091
|
+
kind?: string;
|
|
1092
|
+
since?: string;
|
|
1093
|
+
limit?: number;
|
|
1094
|
+
}): Promise<ChangeEvent[]>;
|
|
1095
|
+
declare function getChangeEvent(db: SqlDb, projectId: string, changeId: string, environment?: string): Promise<ChangeEvent | null>;
|
|
1096
|
+
/**
|
|
1097
|
+
* Indexed cross-trace contention lookup (RFC 0013 § 4.B).
|
|
1098
|
+
* Uses `idx_spans_contention_lookup` (`project_id, environment, target_node_id, start_time, end_time`).
|
|
1099
|
+
* Enforces P1 #5: if the target node is unscoped (`is_scoped_identity = 0`), caps
|
|
1100
|
+
* `relationshipCertainty` at `"inferred"`.
|
|
1101
|
+
*/
|
|
1102
|
+
declare function findCrossTraceContention(db: SqlDb, options: {
|
|
1103
|
+
projectId: string;
|
|
1104
|
+
environment: string;
|
|
1105
|
+
targetNodeId: string;
|
|
1106
|
+
excludeTraceId: string;
|
|
1107
|
+
startTime: string;
|
|
1108
|
+
endTime: string;
|
|
1109
|
+
limit?: number;
|
|
1110
|
+
}): Promise<ContentionCandidate[]>;
|
|
1111
|
+
/**
|
|
1112
|
+
* Computes traffic-normalized cohort rate lift, minimum sample gates, shared-degradation
|
|
1113
|
+
* noisy-neighbor contention checks, and code-path overlap for a `change_event`
|
|
1114
|
+
* (RFC 0013 § 4.A, P1 #1, P1 #2).
|
|
1115
|
+
*/
|
|
1116
|
+
declare function computeChangeBlastRadius(db: SqlDb, projectId: string, changeId: string, options?: {
|
|
1117
|
+
environment?: string;
|
|
1118
|
+
minComparableSample?: number;
|
|
1119
|
+
}): Promise<ChangeBlastRadiusResult | null>;
|
|
1120
|
+
|
|
1121
|
+
declare function recordInvestigationFinding(db: SqlDb, projectId: string, input: InvestigationFindingInput, options?: {
|
|
1122
|
+
logger?: Logger;
|
|
1123
|
+
}): Promise<InvestigationFinding>;
|
|
1124
|
+
declare function getInvestigationFinding(db: SqlDb, projectId: string, findingId: string, environment?: string): Promise<InvestigationFinding | null>;
|
|
1125
|
+
declare function searchInvestigationFindings(db: SqlDb, options: {
|
|
1126
|
+
projectId: string;
|
|
1127
|
+
environment?: string;
|
|
1128
|
+
fingerprint?: string;
|
|
1129
|
+
symptomEntityId?: string;
|
|
1130
|
+
rootCauseEntityId?: string;
|
|
1131
|
+
causalVerificationStatus?: string;
|
|
1132
|
+
incidentId?: string;
|
|
1133
|
+
limit?: number;
|
|
1134
|
+
}): Promise<InvestigationFinding[]>;
|
|
1135
|
+
declare class CounterfactualVerificationError extends Error {
|
|
1136
|
+
constructor(message: string);
|
|
1137
|
+
}
|
|
1138
|
+
/**
|
|
1139
|
+
* Verifies a PairedCounterfactualProof in SQL against `eval_runs` and `eval_case_results`
|
|
1140
|
+
* (RFC 0013 § 5.A & P1 #3).
|
|
1141
|
+
*
|
|
1142
|
+
* Requires:
|
|
1143
|
+
* 1. `failingRunId` and all `passingRunIds` belong to `projectId` and ran against `evalCaseId`
|
|
1144
|
+
* with the identical non-null `input_payload_hash`.
|
|
1145
|
+
* 2. `failingRunId` failed (`passed = 0`) and its `failure_fingerprint` matches `finding.fingerprint`.
|
|
1146
|
+
* 3. All `passingRunIds` passed (`passed = 1`) and `passingRunIds.length >= requiredTrials`.
|
|
1147
|
+
* 4. Failing and passing runs have distinct recorded version/commit/prompt lineage.
|
|
1148
|
+
*/
|
|
1149
|
+
declare function verifyPairedCounterfactualProof(db: SqlDb, projectId: string, findingId: string, input: {
|
|
1150
|
+
environment?: string;
|
|
1151
|
+
evalCaseId: string;
|
|
1152
|
+
failingRunId: string;
|
|
1153
|
+
passingRunIds: string[];
|
|
1154
|
+
requiredTrials?: number;
|
|
1155
|
+
}): Promise<InvestigationFinding>;
|
|
1156
|
+
|
|
1157
|
+
interface ExtractedSpanLineage {
|
|
1158
|
+
environment: string;
|
|
1159
|
+
namespace: string;
|
|
1160
|
+
serviceName: string | null;
|
|
1161
|
+
serviceVersion: string | null;
|
|
1162
|
+
commitSha: string | null;
|
|
1163
|
+
configDigest: string | null;
|
|
1164
|
+
sourceNode: CanonicalNodeIdentity | null;
|
|
1165
|
+
targetNode: CanonicalNodeIdentity | null;
|
|
1166
|
+
edgePredicate: string | null;
|
|
1167
|
+
errorSignature: string | null;
|
|
1168
|
+
}
|
|
1169
|
+
/**
|
|
1170
|
+
* Extracts runtime lineage (`environment`, `serviceVersion`, `commitSha`, `configDigest`),
|
|
1171
|
+
* canonical source/target `OntologyNode` identities, and `errorSignature` from a span.
|
|
1172
|
+
*/
|
|
1173
|
+
declare function extractSpanLineageAndTopology(span: StoredSpan): ExtractedSpanLineage;
|
|
1174
|
+
/**
|
|
1175
|
+
* Upserts environment-scoped `ontology_nodes`, `ontology_edges`, and inferred version-transition
|
|
1176
|
+
* `change_events` from ingested spans.
|
|
1177
|
+
*/
|
|
1178
|
+
declare function upsertOntologyFromSpans(db: SqlDb, spans: StoredSpan[], options?: {
|
|
1179
|
+
bypassCache?: boolean;
|
|
1180
|
+
}): Promise<void>;
|
|
1181
|
+
/**
|
|
1182
|
+
* Tarjan's O(V + E) Strongly Connected Components (SCC) condensation algorithm
|
|
1183
|
+
* (RFC 0013 § 1.B & P2 #6). Ensures cyclic service/queue topologies never cause
|
|
1184
|
+
* infinite loops during dependency traversal.
|
|
1185
|
+
*/
|
|
1186
|
+
declare function computeTarjanScc(nodes: OntologyNode[], edges: OntologyEdge[]): {
|
|
1187
|
+
annotatedNodes: OntologyNode[];
|
|
1188
|
+
components: Array<{
|
|
1189
|
+
sccId: string;
|
|
1190
|
+
nodeIds: string[];
|
|
1191
|
+
isCyclic: boolean;
|
|
1192
|
+
}>;
|
|
1193
|
+
};
|
|
1194
|
+
interface TraceSpanLocusInput {
|
|
1195
|
+
span_id: string;
|
|
1196
|
+
parent_span_id: string | null;
|
|
1197
|
+
service_name: string | null;
|
|
1198
|
+
span_name: string;
|
|
1199
|
+
span_kind: number;
|
|
1200
|
+
status_code: number;
|
|
1201
|
+
status_message: string | null;
|
|
1202
|
+
start_time: string;
|
|
1203
|
+
end_time: string;
|
|
1204
|
+
duration_ms: number;
|
|
1205
|
+
environment?: string | null;
|
|
1206
|
+
target_node_id?: string | null;
|
|
1207
|
+
error_signature?: string | null;
|
|
1208
|
+
}
|
|
1209
|
+
interface TraceFaultLocusResult {
|
|
1210
|
+
faultLocusSpan: TraceSpanLocusInput | null;
|
|
1211
|
+
faultLocusNodeId: string | null;
|
|
1212
|
+
relationshipCertainty: RelationshipCertainty;
|
|
1213
|
+
causalProofTier: "unverified_locus";
|
|
1214
|
+
causalTriples: CausalTriple[];
|
|
1215
|
+
}
|
|
1216
|
+
/**
|
|
1217
|
+
* Per-trace cycle-guarded DFS fault locus detector (RFC 0013 § 1.B & P1 #1).
|
|
1218
|
+
* Identifies where an error surfaced (`originated_fault_in_span`) while keeping
|
|
1219
|
+
* `causalProofTier: "unverified_locus"` separate from `relationshipCertainty: "exact"`.
|
|
1220
|
+
*/
|
|
1221
|
+
declare function localizeTraceFaultLocus(traceId: string, spans: TraceSpanLocusInput[]): TraceFaultLocusResult;
|
|
1222
|
+
/**
|
|
1223
|
+
* Queries `telemetry_spans` for a trace and computes its fault locus.
|
|
1224
|
+
*/
|
|
1225
|
+
declare function loadTraceFaultLocusFromDb(db: SqlDb, projectId: string, traceId: string): Promise<TraceFaultLocusResult>;
|
|
1226
|
+
/**
|
|
1227
|
+
* Loads the environment-scoped operational ontology graph with Tarjan SCC
|
|
1228
|
+
* cycle metadata and dependency blackout indicators.
|
|
1229
|
+
*/
|
|
1230
|
+
declare function getOntologyGraph(db: SqlDb, options: {
|
|
1231
|
+
projectId: string;
|
|
1232
|
+
environment?: string;
|
|
1233
|
+
}): Promise<OntologyGraphResponse>;
|
|
1234
|
+
|
|
1235
|
+
/**
|
|
1236
|
+
* Opens or deduplicates an active `incident_investigation` (RFC 0013 § 6.A & P2 #7).
|
|
1237
|
+
* Multiple firing alerts or webhook retries for the same `(project_id, environment, target_node_id, symptom_category)`
|
|
1238
|
+
* coalesce into a single active incident.
|
|
1239
|
+
*/
|
|
1240
|
+
declare function openOrDeduplicateIncident(db: SqlDb, projectId: string, input: {
|
|
1241
|
+
id?: string;
|
|
1242
|
+
environment?: string;
|
|
1243
|
+
targetNodeId: string;
|
|
1244
|
+
symptomCategory?: string;
|
|
1245
|
+
symptomAnchorKind: EvidenceEntityKind;
|
|
1246
|
+
symptomAnchorId: string;
|
|
1247
|
+
symptomFingerprint?: string | null;
|
|
1248
|
+
latestBundle?: Record<string, unknown> | null;
|
|
1249
|
+
}): Promise<{
|
|
1250
|
+
incident: IncidentInvestigation;
|
|
1251
|
+
deduplicated: boolean;
|
|
1252
|
+
}>;
|
|
1253
|
+
declare function getIncidentInvestigation(db: SqlDb, projectId: string, incidentId: string, environment?: string): Promise<IncidentInvestigation | null>;
|
|
1254
|
+
declare function listIncidentInvestigations(db: SqlDb, options: {
|
|
1255
|
+
projectId: string;
|
|
1256
|
+
environment?: string;
|
|
1257
|
+
status?: string;
|
|
1258
|
+
limit?: number;
|
|
1259
|
+
}): Promise<IncidentInvestigation[]>;
|
|
1260
|
+
/**
|
|
1261
|
+
* Exclusive runner lease acquisition / renewal (RFC 0013 § 6.B & P2 #7).
|
|
1262
|
+
* Prevents competing AutoSRE runners from investigating or remediating the same
|
|
1263
|
+
* incident simultaneously.
|
|
1264
|
+
*/
|
|
1265
|
+
declare function acquireIncidentLease(db: SqlDb, projectId: string, incidentId: string, input: {
|
|
1266
|
+
environment?: string;
|
|
1267
|
+
runnerId: string;
|
|
1268
|
+
leaseDurationSeconds?: number;
|
|
1269
|
+
nowIso?: string;
|
|
1270
|
+
}): Promise<{
|
|
1271
|
+
acquired: true;
|
|
1272
|
+
incident: IncidentInvestigation;
|
|
1273
|
+
} | {
|
|
1274
|
+
acquired: false;
|
|
1275
|
+
reason: string;
|
|
1276
|
+
conflictOwnerId: string | null;
|
|
1277
|
+
leaseExpiresAt: string | null;
|
|
1278
|
+
}>;
|
|
1279
|
+
/**
|
|
1280
|
+
* SSRF-safe, HMAC-SHA256 signed AutoSRE incident webhook outbox delivery
|
|
1281
|
+
* (RFC 0013 § 6.A). Mirrors the alert webhook delivery: literal host check,
|
|
1282
|
+
* DNS classification (fail closed on private/unresolvable), no redirects and
|
|
1283
|
+
* a 5s timeout.
|
|
1284
|
+
*
|
|
1285
|
+
* Grant handoff (RFC 0013 § 6.B): when `webhook.grant` is supplied the payload
|
|
1286
|
+
* is an `autosre.remediation.grant_issued` event carrying `proposalId`,
|
|
1287
|
+
* `status` and the plaintext `grantToken`. The plaintext token exists only at
|
|
1288
|
+
* approve/propose time (the DB keeps its hash), so the caller that mints it
|
|
1289
|
+
* must hand it to this function in the same request. Grant deliveries do not
|
|
1290
|
+
* count against the incident's 5-attempt dispatch budget.
|
|
1291
|
+
*/
|
|
1292
|
+
declare function dispatchIncidentWebhook(db: SqlDb, projectId: string, incidentId: string, webhook: {
|
|
1293
|
+
url: string;
|
|
1294
|
+
secret?: string;
|
|
1295
|
+
environment?: string;
|
|
1296
|
+
allowPrivateNetworks?: boolean;
|
|
1297
|
+
hostLookup?: HostLookup;
|
|
1298
|
+
fetchImpl?: typeof fetch;
|
|
1299
|
+
logger?: Logger;
|
|
1300
|
+
grant?: {
|
|
1301
|
+
proposalId: string;
|
|
1302
|
+
status: string;
|
|
1303
|
+
grantToken: string;
|
|
1304
|
+
};
|
|
1305
|
+
}): Promise<{
|
|
1306
|
+
delivered: boolean;
|
|
1307
|
+
status?: number;
|
|
1308
|
+
error?: string;
|
|
1309
|
+
}>;
|
|
1310
|
+
declare class RemediationGateError extends Error {
|
|
1311
|
+
readonly statusCode: number;
|
|
1312
|
+
constructor(message: string, statusCode?: number);
|
|
1313
|
+
}
|
|
1314
|
+
/**
|
|
1315
|
+
* Pre-execution remediation proposal gate (RFC 0013 § 6.B & P1 #4).
|
|
1316
|
+
*/
|
|
1317
|
+
declare function proposeRemediationAction(db: SqlDb, projectId: string, input: RemediationProposalInput, options?: {
|
|
1318
|
+
logger?: Logger;
|
|
1319
|
+
}): Promise<RemediationProposal>;
|
|
1320
|
+
declare function getRemediationProposal(db: SqlDb, projectId: string, proposalId: string, environment?: string): Promise<RemediationProposal | null>;
|
|
1321
|
+
declare function listRemediationProposals(db: SqlDb, options: {
|
|
1322
|
+
projectId: string;
|
|
1323
|
+
environment?: string;
|
|
1324
|
+
status?: string;
|
|
1325
|
+
incidentId?: string;
|
|
1326
|
+
findingId?: string;
|
|
1327
|
+
limit?: number;
|
|
1328
|
+
}): Promise<RemediationProposal[]>;
|
|
1329
|
+
/**
|
|
1330
|
+
* Human dashboard session approval for `pending_human_approval` proposals
|
|
1331
|
+
* (RFC 0013 § 6.B & P1 #4). Mints a single-use `grantToken`.
|
|
1332
|
+
*
|
|
1333
|
+
* The status precondition lives in the UPDATE's WHERE clause (D1 and Postgres
|
|
1334
|
+
* have no multi-statement transactions beyond `batch`), so two concurrent
|
|
1335
|
+
* approvals cannot both mint a token: only one sees `meta.changes === 1`.
|
|
1336
|
+
*/
|
|
1337
|
+
declare function approveRemediationProposal(db: SqlDb, projectId: string, proposalId: string, options: {
|
|
1338
|
+
environment?: string;
|
|
1339
|
+
approvedByUserId: string;
|
|
1340
|
+
}): Promise<RemediationProposal>;
|
|
1341
|
+
declare function rejectRemediationProposal(db: SqlDb, projectId: string, proposalId: string, options: {
|
|
1342
|
+
environment?: string;
|
|
1343
|
+
rejectedByUserId: string;
|
|
1344
|
+
reason?: string;
|
|
1345
|
+
}): Promise<RemediationProposal>;
|
|
1346
|
+
/**
|
|
1347
|
+
* Single-use grant redemption with optimistic `expectedCurrentState` verification
|
|
1348
|
+
* against `ontology_nodes` (RFC 0013 § 6.B & P1 #4).
|
|
1349
|
+
*
|
|
1350
|
+
* The status, token-hash and expiry preconditions are part of the claiming
|
|
1351
|
+
* UPDATE, and the outcome is read from `meta.changes`: of N concurrent claims
|
|
1352
|
+
* for one token exactly one sees `changes === 1`; the rest get a 409. The
|
|
1353
|
+
* incident transition rides in the same `batch` and is guarded by an EXISTS on
|
|
1354
|
+
* the winning claim, so a losing claim cannot touch the incident.
|
|
1355
|
+
*/
|
|
1356
|
+
declare function claimRemediationGrant(db: SqlDb, projectId: string, proposalId: string, options: {
|
|
1357
|
+
environment?: string;
|
|
1358
|
+
grantToken: string;
|
|
1359
|
+
runnerId?: string;
|
|
1360
|
+
nowIso?: string;
|
|
1361
|
+
logger?: Logger;
|
|
1362
|
+
}): Promise<RemediationProposal>;
|
|
1363
|
+
/**
|
|
1364
|
+
* Records remediation completion, updates `ontology_nodes` to desired state,
|
|
1365
|
+
* and arms the +2m/+5m/+15m post-remediation watchdog on the finding.
|
|
1366
|
+
*
|
|
1367
|
+
* Only the runner that holds the claim may complete, and only while the claim
|
|
1368
|
+
* window is open (`now <= claim_expires_at`); otherwise 409. All follow-up
|
|
1369
|
+
* writes are guarded on the completion UPDATE having applied.
|
|
1370
|
+
*/
|
|
1371
|
+
declare function completeRemediationProposal(db: SqlDb, projectId: string, proposalId: string, options: {
|
|
1372
|
+
environment?: string;
|
|
1373
|
+
runnerId?: string;
|
|
1374
|
+
resultingChangeId?: string | null;
|
|
1375
|
+
remediationAgentRunId?: string | null;
|
|
1376
|
+
baselineRequestRatePerMin?: number | null;
|
|
1377
|
+
nowIso?: string;
|
|
1378
|
+
}): Promise<RemediationProposal>;
|
|
1379
|
+
declare function failRemediationProposal(db: SqlDb, projectId: string, proposalId: string, options: {
|
|
1380
|
+
environment?: string;
|
|
1381
|
+
runnerId?: string;
|
|
1382
|
+
failureReason: string;
|
|
1383
|
+
nowIso?: string;
|
|
1384
|
+
}): Promise<RemediationProposal>;
|
|
1385
|
+
interface WatchdogEvaluationSummary {
|
|
1386
|
+
evaluated: number;
|
|
1387
|
+
recovered: number;
|
|
1388
|
+
inconclusiveLowTraffic: number;
|
|
1389
|
+
stillDegraded: number;
|
|
1390
|
+
timedOutClaims: number;
|
|
1391
|
+
expiredGrants: number;
|
|
1392
|
+
/** Findings whose evaluation threw; logged and retried on the next tick. */
|
|
1393
|
+
failed: number;
|
|
1394
|
+
}
|
|
1395
|
+
/**
|
|
1396
|
+
* Post-Remediation Watchdog & Lease/Claim Timeout Evaluator (RFC 0013 § 5.B, § 6.B & P1 #3).
|
|
1397
|
+
*
|
|
1398
|
+
* Strictly separates:
|
|
1399
|
+
* - `recovery_status`: whether the service recovered under comparable traffic (`N >= minComparableRequests`)
|
|
1400
|
+
* - `causal_verification_status`: `"operationally_corroborated"` (never promoted to `"counterfactual_verified"`
|
|
1401
|
+
* by telemetry recovery alone!).
|
|
1402
|
+
*
|
|
1403
|
+
* Checkpoints are scheduled at +2m/+5m/+15m measured from the remediation's
|
|
1404
|
+
* completion time (not from whenever a tick happened to run), and a finding
|
|
1405
|
+
* only becomes `recovered` after two consecutive healthy checkpoints that each
|
|
1406
|
+
* had sufficient traffic. Pass `projectId` to scope the sweep to one tenant
|
|
1407
|
+
* (HTTP path); the cron path omits it and sweeps every project.
|
|
1408
|
+
*/
|
|
1409
|
+
declare function evaluateRemediationWatchdogs(db: SqlDb, options?: {
|
|
1410
|
+
nowIso?: string;
|
|
1411
|
+
minComparableRequests?: number;
|
|
1412
|
+
projectId?: string;
|
|
1413
|
+
logger?: Logger;
|
|
1414
|
+
}): Promise<WatchdogEvaluationSummary>;
|
|
1415
|
+
|
|
1075
1416
|
/**
|
|
1076
1417
|
* Storage seam for opaque blob payloads — replay rrweb chunks and
|
|
1077
1418
|
* pprof profiles.
|
|
@@ -1255,6 +1596,10 @@ interface EvalRunCandidate {
|
|
|
1255
1596
|
modelProvider: string | null;
|
|
1256
1597
|
model: string | null;
|
|
1257
1598
|
modelVersion: string | null;
|
|
1599
|
+
environment?: string | null;
|
|
1600
|
+
serviceVersion?: string | null;
|
|
1601
|
+
commitSha?: string | null;
|
|
1602
|
+
configDigest?: string | null;
|
|
1258
1603
|
}
|
|
1259
1604
|
interface EvalRunSourceCase {
|
|
1260
1605
|
id: string;
|
|
@@ -1276,6 +1621,12 @@ interface EvalRun {
|
|
|
1276
1621
|
evalCaseId: string | null;
|
|
1277
1622
|
status: EvalRunStatus;
|
|
1278
1623
|
candidate: EvalRunCandidate;
|
|
1624
|
+
environment?: string;
|
|
1625
|
+
serviceVersion?: string | null;
|
|
1626
|
+
commitSha?: string | null;
|
|
1627
|
+
configDigest?: string | null;
|
|
1628
|
+
inputPayloadHash?: string | null;
|
|
1629
|
+
failureFingerprint?: string | null;
|
|
1279
1630
|
startedAt: string | null;
|
|
1280
1631
|
endedAt: string | null;
|
|
1281
1632
|
totalCount: number;
|
|
@@ -1293,6 +1644,12 @@ interface EvalRunInput {
|
|
|
1293
1644
|
evalCaseId?: string | null;
|
|
1294
1645
|
status?: EvalRunStatus;
|
|
1295
1646
|
candidate?: Partial<EvalRunCandidate>;
|
|
1647
|
+
environment?: string | null;
|
|
1648
|
+
serviceVersion?: string | null;
|
|
1649
|
+
commitSha?: string | null;
|
|
1650
|
+
configDigest?: string | null;
|
|
1651
|
+
inputPayloadHash?: string | null;
|
|
1652
|
+
failureFingerprint?: string | null;
|
|
1296
1653
|
startedAt?: string | null;
|
|
1297
1654
|
endedAt?: string | null;
|
|
1298
1655
|
totalCount?: number;
|
|
@@ -1490,4 +1847,4 @@ declare const _default: hono.Hono<{
|
|
|
1490
1847
|
Bindings: CollectorEnv;
|
|
1491
1848
|
}, hono_types.BlankSchema, "/">;
|
|
1492
1849
|
|
|
1493
|
-
export { type ActionGraphPurgeSummary, type ActionGraphRetentionOptions, type ActionRecord, ActionStore, type AgentRunRecord, AlertsStore, type AnalysesRunSummary, type ArtifactRecord, type BlobListOptions, type BlobListResult, type BlobObject, type BlobPutOptions, type BlobStore, BlobStoreToR2Adapter, type CollectorAuthConfig, type CollectorConfig, CollectorEnv, type CollectorPlugin, CollectorRouteContext, CollectorRuntime, D1Adapter, type DashboardAuthConfig, type EvalCase, type EvalCaseInput, type EvalCaseListOptions, type EvalCaseSourceLinks, EvalCaseSourceNotFoundError, type EvalCaseSourceType, EvalCasesStore, type EvalResultRecord, type EvalRun, type EvalRunCandidate, type EvalRunInput, type EvalRunSourceCase, type EvalRunStatus, type EvalRunsListOptions, type HostLookup, Logger, MetricsStore, PostgresAdapter, type PostgresAdapterOptions, type PostgresTypeParserRegistry, ProjectsStore, R2BlobStore, type RetentionCleanupSummary, type RetrievalEventRecord, S3BlobStore, type S3BlobStoreOptions, type ScheduledTaskHandler, type SpanProcessorPlugin, type SqlDb, type SqlDbFactory, type SqlStatement, type TailEvent, TailHub, type TailKind, type ToolCallRecord, type UsageEventProcessorPlugin, actionRetentionCutoff, actionRoutesPlugin, aiReceiverPlugin, aiSpanPayloadsProcessorPlugin, alertsRoutesPlugin, allPlugins, analysesRoutesPlugin, askRoutesPlugin, botFilterPlugin, compareValue, configurePostgresTypeParsers, connectedRoutesPlugin, createAlertEvaluatorHandler, createAnalysesRunHandler, createDashboardAuth, createDefaultCollectorApp, createRetentionCleanupHandler, createTelemetryCollectorApp, dashboardRoutesPlugin, _default as default, defaultSpanEnrichmentPlugin, evalCasesRoutesPlugin, evaluateAllRules, evidenceRetrievalRoutesPlugin, genAiNormalizerPlugin, identityReceiverPlugin, isEvalCaseSourceType, isEvalRunStatus, issueEnrichmentPlugin, issueInsightsPlugin, logsReceiverPlugin, metricsReceiverPlugin, onboardingRoutesPlugin, otlpReceiverPlugin, platformRoutesPlugin, profileRoutesPlugin, projectsRoutesPlugin, purgeExpiredActionGraph, queryRoutesPlugin, redactionProcessorPlugin, replayQueryRoutesPlugin, replayReceiverPlugin, tailRoutesPlugin, timelineRoutesPlugin, uaEnrichmentPlugin, usageNormalizationPlugin, usagePrivacyPlugin, usageQueryRoutesPlugin, usageReceiverPlugin, usersQueryRoutesPlugin, utmEnrichmentPlugin };
|
|
1850
|
+
export { type ActionGraphPurgeSummary, type ActionGraphRetentionOptions, type ActionRecord, ActionStore, type AgentRunRecord, AlertsStore, type AnalysesRunSummary, type ArtifactRecord, type BlobListOptions, type BlobListResult, type BlobObject, type BlobPutOptions, type BlobStore, BlobStoreToR2Adapter, type CollectorAuthConfig, type CollectorConfig, CollectorEnv, type CollectorPlugin, CollectorRouteContext, CollectorRuntime, CounterfactualVerificationError, D1Adapter, type DashboardAuthConfig, type EvalCase, type EvalCaseInput, type EvalCaseListOptions, type EvalCaseSourceLinks, EvalCaseSourceNotFoundError, type EvalCaseSourceType, EvalCasesStore, type EvalResultRecord, type EvalRun, type EvalRunCandidate, type EvalRunInput, type EvalRunSourceCase, type EvalRunStatus, type EvalRunsListOptions, type HostLookup, Logger, MetricsStore, PostgresAdapter, type PostgresAdapterOptions, type PostgresTypeParserRegistry, ProjectsStore, R2BlobStore, RemediationGateError, type RetentionCleanupSummary, type RetrievalEventRecord, S3BlobStore, type S3BlobStoreOptions, type ScheduledTaskHandler, type SpanProcessorPlugin, type SqlDb, type SqlDbFactory, type SqlStatement, type TailEvent, TailHub, type TailKind, type ToolCallRecord, type UsageEventProcessorPlugin, type WatchdogEvaluationSummary, acquireIncidentLease, actionRetentionCutoff, actionRoutesPlugin, aiReceiverPlugin, aiSpanPayloadsProcessorPlugin, alertsRoutesPlugin, allPlugins, analysesRoutesPlugin, approveRemediationProposal, askRoutesPlugin, autosreRoutesPlugin, botFilterPlugin, claimRemediationGrant, compareValue, completeRemediationProposal, computeChangeBlastRadius, computeTarjanScc, configurePostgresTypeParsers, connectedRoutesPlugin, createAlertEvaluatorHandler, createAnalysesRunHandler, createDashboardAuth, createDefaultCollectorApp, createRetentionCleanupHandler, createTelemetryCollectorApp, dashboardRoutesPlugin, _default as default, defaultSpanEnrichmentPlugin, dispatchIncidentWebhook, evalCasesRoutesPlugin, evaluateAllRules, evaluateRemediationWatchdogs, evidenceRetrievalRoutesPlugin, extractSpanLineageAndTopology, failRemediationProposal, findCrossTraceContention, genAiNormalizerPlugin, getChangeEvent, getIncidentInvestigation, getInvestigationFinding, getOntologyGraph, getRemediationProposal, identityReceiverPlugin, isEvalCaseSourceType, isEvalRunStatus, issueEnrichmentPlugin, issueInsightsPlugin, listChangeEvents, listIncidentInvestigations, listRemediationProposals, loadTraceFaultLocusFromDb, localizeTraceFaultLocus, logsReceiverPlugin, metricsReceiverPlugin, onboardingRoutesPlugin, openOrDeduplicateIncident, otlpReceiverPlugin, platformRoutesPlugin, profileRoutesPlugin, projectsRoutesPlugin, proposeRemediationAction, purgeExpiredActionGraph, queryRoutesPlugin, recordChangeEvent, recordInvestigationFinding, redactionProcessorPlugin, rejectRemediationProposal, replayQueryRoutesPlugin, replayReceiverPlugin, searchInvestigationFindings, tailRoutesPlugin, timelineRoutesPlugin, uaEnrichmentPlugin, upsertOntologyFromSpans, usageNormalizationPlugin, usagePrivacyPlugin, usageQueryRoutesPlugin, usageReceiverPlugin, usersQueryRoutesPlugin, utmEnrichmentPlugin, verifyPairedCounterfactualProof };
|