@obsunified/collector 2.2.0 → 2.2.3

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 (45) hide show
  1. package/dist/auth.js +1 -1
  2. package/dist/{chunk-KMEZKGSO.js → chunk-2FALTDCE.js} +12 -4
  3. package/dist/chunk-2FALTDCE.js.map +1 -0
  4. package/dist/index.d.ts +61 -3
  5. package/dist/index.js +10933 -9769
  6. package/dist/index.js.map +1 -1
  7. package/package.json +2 -3
  8. package/src/__tests__/autosre-closed-loop.test.ts +1554 -17
  9. package/src/auth/dashboard-auth.test.ts +61 -0
  10. package/src/auth/dashboard-auth.ts +17 -2
  11. package/src/lib/ai-store/calls.ts +43 -23
  12. package/src/lib/ai-store.ts +12 -1
  13. package/src/lib/analyses-runner.ts +44 -7
  14. package/src/lib/autosre-change-correlation.ts +39 -12
  15. package/src/lib/autosre-findings.ts +237 -44
  16. package/src/lib/autosre-ontology-cache.test.ts +78 -0
  17. package/src/lib/autosre-ontology.ts +52 -28
  18. package/src/lib/autosre-remediation-gate.ts +942 -348
  19. package/src/lib/housekeeping.test.ts +70 -0
  20. package/src/lib/housekeeping.ts +91 -83
  21. package/src/lib/projects-store.test.ts +38 -1
  22. package/src/lib/projects-store.ts +23 -4
  23. package/src/lib/sql-db-postgres.integration.test.ts +75 -2
  24. package/src/lib/store/ingest.ts +8 -56
  25. package/src/lib/usage-store.ts +9 -3
  26. package/src/migrations/051_incident_active_dedup_unique.sql +31 -0
  27. package/src/migrations/052_remediation_callback.sql +10 -0
  28. package/src/migrations/053_remediation_callback_delivery.sql +7 -0
  29. package/src/migrations/alert-state-notified.test.ts +3 -3
  30. package/src/migrations-postgres/051_incident_active_dedup_unique.sql +31 -0
  31. package/src/migrations-postgres/052_remediation_callback.sql +10 -0
  32. package/src/migrations-postgres/053_remediation_callback_delivery.sql +7 -0
  33. package/src/plugins/ai-call-by-id.test.ts +73 -0
  34. package/src/plugins/ai-receiver.ts +10 -0
  35. package/src/plugins/alerts-evaluator.ts +15 -3
  36. package/src/plugins/autosre-routes.ts +374 -108
  37. package/src/plugins/connected-routes/deep-link-shapes.test.ts +44 -0
  38. package/src/plugins/connected-routes/manifest.test.ts +142 -0
  39. package/src/plugins/connected-routes/manifest.ts +123 -6
  40. package/src/plugins/connected-routes-links.test.ts +96 -0
  41. package/src/plugins/connected-routes.test.ts +3 -3
  42. package/src/plugins/connected-routes.ts +56 -83
  43. package/src/plugins/evidence-retrieval-routes.test.ts +32 -0
  44. package/src/plugins/evidence-retrieval-routes.ts +397 -25
  45. package/dist/chunk-KMEZKGSO.js.map +0 -1
@@ -15,8 +15,12 @@
15
15
  * RFC 0006 § Connected rail / Empty-state.
16
16
  */
17
17
 
18
+ import type { CausalProofTier } from "@obsunified/types";
18
19
  import { ActionConfidence } from "@obsunified/types/constants";
19
20
  import type { CollectorPlugin } from "../framework/collector";
21
+ import { getChangeEvent } from "../lib/autosre-change-correlation";
22
+ import { getInvestigationFinding } from "../lib/autosre-findings";
23
+ import { getIncidentInvestigation } from "../lib/autosre-remediation-gate";
20
24
  import { IdentityIndex } from "../lib/identity-index";
21
25
  import {
22
26
  mapAction,
@@ -38,6 +42,7 @@ import {
38
42
  type ActionContextLinkMetadata,
39
43
  type ConnectedEntityKind,
40
44
  type ConnectedManifest,
45
+ hrefForEntity,
41
46
  KNOWN_KINDS,
42
47
  linksFromActions,
43
48
  linksFromAi,
@@ -55,6 +60,7 @@ import {
55
60
  linkToTrace,
56
61
  MAX_LINKS_INLINE,
57
62
  profileLinksForTrace,
63
+ resolveFaultLocusTarget,
58
64
  } from "./connected-routes/manifest";
59
65
 
60
66
  export type {
@@ -1051,30 +1057,20 @@ export const connectedRoutesPlugin: CollectorPlugin = {
1051
1057
  }
1052
1058
  } else if (kind === "change") {
1053
1059
  const db = sqlDbFor(c.env);
1054
- const changeRow = await db
1055
- .prepare(
1056
- `SELECT * FROM change_events WHERE project_id = ? AND id = ? LIMIT 1`,
1057
- )
1058
- .bind(projectId, id)
1059
- .first<{
1060
- id: string;
1061
- environment: string;
1062
- kind: string;
1063
- target_node_id: string;
1064
- service_name: string | null;
1065
- version: string | null;
1066
- previous_version: string | null;
1067
- commit_sha: string | null;
1068
- summary: string;
1069
- }>();
1060
+ const changeRow = await getChangeEvent(
1061
+ db,
1062
+ projectId,
1063
+ id,
1064
+ c.req.query("environment") || undefined,
1065
+ );
1070
1066
  if (changeRow) {
1071
1067
  manifest.up = [
1072
1068
  {
1073
1069
  label: "Target Ontology Node",
1074
1070
  links: [
1075
1071
  {
1076
- label: `${changeRow.target_node_id} (${changeRow.environment})`,
1077
- href: `#/health?node=${encodeURIComponent(changeRow.target_node_id)}`,
1072
+ label: `${changeRow.targetNodeId} (${changeRow.environment})`,
1073
+ href: `#/health?node=${encodeURIComponent(changeRow.targetNodeId)}`,
1078
1074
  relationshipCertainty: "exact",
1079
1075
  reason: "Target node mutated by this change event.",
1080
1076
  },
@@ -1107,8 +1103,7 @@ export const connectedRoutesPlugin: CollectorPlugin = {
1107
1103
  entityKind: "finding",
1108
1104
  entityId: f.id,
1109
1105
  relationshipCertainty: "exact",
1110
- causalProofTier:
1111
- f.causal_proof_tier as import("@obsunified/types").CausalProofTier,
1106
+ causalProofTier: f.causal_proof_tier as CausalProofTier,
1112
1107
  confidence: f.confidence,
1113
1108
  reason: "Finding attributes this change event.",
1114
1109
  })),
@@ -1122,10 +1117,11 @@ export const connectedRoutesPlugin: CollectorPlugin = {
1122
1117
  const spans = changeRow.version
1123
1118
  ? await db
1124
1119
  .prepare(
1125
- `SELECT DISTINCT trace_id, span_id, service_name, span_name
1120
+ `SELECT trace_id, span_id, service_name, span_name
1126
1121
  FROM telemetry_spans
1127
1122
  WHERE project_id = ? AND environment = ? AND service_version = ? AND status_code = 2
1128
- ORDER BY start_time DESC LIMIT 5`,
1123
+ GROUP BY trace_id, span_id, service_name, span_name
1124
+ ORDER BY MAX(start_time) DESC LIMIT 5`,
1129
1125
  )
1130
1126
  .bind(projectId, changeRow.environment, changeRow.version)
1131
1127
  .all<{
@@ -1156,54 +1152,39 @@ export const connectedRoutesPlugin: CollectorPlugin = {
1156
1152
  }
1157
1153
  } else if (kind === "finding") {
1158
1154
  const db = sqlDbFor(c.env);
1159
- const findingRow = await db
1160
- .prepare(
1161
- `SELECT * FROM investigation_findings WHERE project_id = ? AND id = ? LIMIT 1`,
1162
- )
1163
- .bind(projectId, id)
1164
- .first<{
1165
- id: string;
1166
- environment: string;
1167
- incident_id: string | null;
1168
- title: string;
1169
- causal_proof_tier: string;
1170
- confidence: number;
1171
- symptom_entity_kind: string;
1172
- symptom_entity_id: string;
1173
- fault_locus_entity_kind: string;
1174
- fault_locus_entity_id: string;
1175
- contributing_change_id: string | null;
1176
- contending_trace_id: string | null;
1177
- agent_run_id: string | null;
1178
- }>();
1155
+ const findingRow = await getInvestigationFinding(
1156
+ db,
1157
+ projectId,
1158
+ id,
1159
+ c.req.query("environment") || undefined,
1160
+ );
1179
1161
  if (findingRow) {
1180
- const proofTier =
1181
- findingRow.causal_proof_tier as import("@obsunified/types").CausalProofTier;
1162
+ const proofTier = findingRow.causalProofTier as CausalProofTier;
1182
1163
  manifest.up = [];
1183
- if (findingRow.incident_id) {
1164
+ if (findingRow.incidentId) {
1184
1165
  manifest.up.push({
1185
1166
  label: "Incident Investigation",
1186
1167
  links: [
1187
1168
  {
1188
- label: `incident ${findingRow.incident_id}`,
1189
- href: `#/incidents/${encodeURIComponent(findingRow.incident_id)}`,
1169
+ label: `incident ${findingRow.incidentId}`,
1170
+ href: `#/incidents/${encodeURIComponent(findingRow.incidentId)}`,
1190
1171
  entityKind: "incident",
1191
- entityId: findingRow.incident_id,
1172
+ entityId: findingRow.incidentId,
1192
1173
  relationshipCertainty: "exact",
1193
1174
  causalProofTier: proofTier,
1194
1175
  },
1195
1176
  ],
1196
1177
  });
1197
1178
  }
1198
- if (findingRow.contributing_change_id) {
1179
+ if (findingRow.contributingChangeId) {
1199
1180
  manifest.up.push({
1200
1181
  label: "Contributing Change",
1201
1182
  links: [
1202
1183
  {
1203
- label: `change ${findingRow.contributing_change_id}`,
1204
- href: `#/changes/${encodeURIComponent(findingRow.contributing_change_id)}`,
1184
+ label: `change ${findingRow.contributingChangeId}`,
1185
+ href: `#/changes/${encodeURIComponent(findingRow.contributingChangeId)}`,
1205
1186
  entityKind: "change",
1206
- entityId: findingRow.contributing_change_id,
1187
+ entityId: findingRow.contributingChangeId,
1207
1188
  relationshipCertainty: "exact",
1208
1189
  causalProofTier: proofTier,
1209
1190
  confidence: findingRow.confidence,
@@ -1216,18 +1197,18 @@ export const connectedRoutesPlugin: CollectorPlugin = {
1216
1197
  label: "Fault Locus & Symptom",
1217
1198
  links: [
1218
1199
  {
1219
- label: `fault locus: ${findingRow.fault_locus_entity_id}`,
1220
- href: `#/traces?q=${encodeURIComponent(findingRow.fault_locus_entity_id)}`,
1200
+ label: `fault locus: ${findingRow.faultLocusEntityId}`,
1201
+ ...resolveFaultLocusTarget(findingRow.faultLocusEntityId),
1221
1202
  relationshipCertainty: "exact",
1222
1203
  causalProofTier: proofTier,
1223
1204
  },
1224
- ...(findingRow.contending_trace_id
1205
+ ...(findingRow.contendingTraceId
1225
1206
  ? [
1226
1207
  {
1227
- label: `contending trace ${findingRow.contending_trace_id}`,
1228
- href: `#/traces/${encodeURIComponent(findingRow.contending_trace_id)}`,
1208
+ label: `contending trace ${findingRow.contendingTraceId}`,
1209
+ href: `#/traces/${encodeURIComponent(findingRow.contendingTraceId)}`,
1229
1210
  entityKind: "trace" as const,
1230
- entityId: findingRow.contending_trace_id,
1211
+ entityId: findingRow.contendingTraceId,
1231
1212
  relationshipCertainty: "inferred" as const,
1232
1213
  causalProofTier: proofTier,
1233
1214
  },
@@ -1240,17 +1221,17 @@ export const connectedRoutesPlugin: CollectorPlugin = {
1240
1221
  {
1241
1222
  label: "Agent Lineage",
1242
1223
  links: [
1243
- ...(findingRow.agent_run_id
1224
+ ...(findingRow.agentRunId
1244
1225
  ? [
1245
1226
  {
1246
- ...linkToAgentRun(findingRow.agent_run_id),
1227
+ ...linkToAgentRun(findingRow.agentRunId),
1247
1228
  relationshipCertainty: "exact" as const,
1248
1229
  causalProofTier: proofTier,
1249
1230
  },
1250
1231
  ]
1251
1232
  : []),
1252
1233
  ],
1253
- emptyReason: !findingRow.agent_run_id
1234
+ emptyReason: !findingRow.agentRunId
1254
1235
  ? "No agent run linked to this finding."
1255
1236
  : undefined,
1256
1237
  },
@@ -1258,32 +1239,25 @@ export const connectedRoutesPlugin: CollectorPlugin = {
1258
1239
  }
1259
1240
  } else if (kind === "incident") {
1260
1241
  const db = sqlDbFor(c.env);
1261
- const incRow = await db
1262
- .prepare(
1263
- `SELECT * FROM incident_investigations WHERE project_id = ? AND id = ? LIMIT 1`,
1264
- )
1265
- .bind(projectId, id)
1266
- .first<{
1267
- id: string;
1268
- environment: string;
1269
- target_node_id: string;
1270
- symptom_anchor_kind: string;
1271
- symptom_anchor_id: string;
1272
- status: string;
1273
- }>();
1242
+ const incRow = await getIncidentInvestigation(
1243
+ db,
1244
+ projectId,
1245
+ id,
1246
+ c.req.query("environment") || undefined,
1247
+ );
1274
1248
  if (incRow) {
1275
1249
  manifest.up = [
1276
1250
  {
1277
1251
  label: "Trigger Signal",
1278
1252
  links: [
1279
1253
  {
1280
- label: `${incRow.symptom_anchor_kind}: ${incRow.symptom_anchor_id}`,
1281
- href: `#/alerts?id=${encodeURIComponent(incRow.symptom_anchor_id)}`,
1282
- entityKind:
1283
- incRow.symptom_anchor_kind === "analysis"
1284
- ? "analysis"
1285
- : "alert",
1286
- entityId: incRow.symptom_anchor_id,
1254
+ label: `${incRow.symptomAnchorKind}: ${incRow.symptomAnchorId}`,
1255
+ href: hrefForEntity(
1256
+ incRow.symptomAnchorKind,
1257
+ incRow.symptomAnchorId,
1258
+ ),
1259
+ entityKind: incRow.symptomAnchorKind,
1260
+ entityId: incRow.symptomAnchorId,
1287
1261
  relationshipCertainty: "exact",
1288
1262
  },
1289
1263
  ],
@@ -1310,8 +1284,7 @@ export const connectedRoutesPlugin: CollectorPlugin = {
1310
1284
  entityKind: "finding" as const,
1311
1285
  entityId: f.id,
1312
1286
  relationshipCertainty: "exact" as const,
1313
- causalProofTier:
1314
- f.causal_proof_tier as import("@obsunified/types").CausalProofTier,
1287
+ causalProofTier: f.causal_proof_tier as CausalProofTier,
1315
1288
  })),
1316
1289
  emptyReason:
1317
1290
  incFindings.results.length > 0
@@ -519,6 +519,38 @@ describe("evidenceRetrievalRoutesPlugin", () => {
519
519
  return (await bundleRes.json()) as EvidenceBundle;
520
520
  };
521
521
 
522
+ it("attaches the AutoSRE triage section only for intent triage_incident", async () => {
523
+ const { app, env } = setup();
524
+ const post = async (intent: string, targetTokens = 4000) => {
525
+ const res = await app.request(
526
+ "/internal/evidence/bundle",
527
+ {
528
+ method: "POST",
529
+ body: JSON.stringify({
530
+ anchor: { entityKind: "trace", entityId: "trace-1" },
531
+ intent,
532
+ budget: { targetTokens, detailLevel: "standard" },
533
+ }),
534
+ headers: { "content-type": "application/json" },
535
+ },
536
+ env,
537
+ );
538
+ expect(res.status).toBe(200);
539
+ return (await res.json()) as EvidenceBundle;
540
+ };
541
+
542
+ const debug = await post("debug_failure");
543
+ expect(debug.autosre).toBeUndefined();
544
+
545
+ const triage = await post("triage_incident");
546
+ expect(triage.autosre).toBeDefined();
547
+ // No unfiltered prior-findings fallback: an empty memory yields none.
548
+ expect(triage.autosre?.priorFindings).toEqual([]);
549
+ expect(triage.autosre?.faultLocus?.isBlackoutSuspected ?? false).toBe(
550
+ false,
551
+ );
552
+ });
553
+
522
554
  it("returns an incident-local trace bundle with log compaction provenance", async () => {
523
555
  const { app, env } = setup();
524
556
  const res = await app.request(