@obsunified/collector 2.2.3 → 2.3.0

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 (32) hide show
  1. package/dist/auth.d.ts +1 -1
  2. package/dist/{chunk-ZSF5XP3B.js → chunk-RASSTGNB.js} +30 -22
  3. package/dist/chunk-RASSTGNB.js.map +1 -0
  4. package/dist/index.d.ts +2 -2
  5. package/dist/index.js +2562 -2311
  6. package/dist/index.js.map +1 -1
  7. package/dist/{ingest-auth-C5-8Mly5.d.ts → ingest-auth-BmP5-f96.d.ts} +32 -1
  8. package/dist/{openai-GSJZGFGL.js → openai-VDKVVZAV.js} +2 -2
  9. package/package.json +4 -2
  10. package/src/__tests__/autosre-closed-loop.test.ts +110 -0
  11. package/src/__tests__/db-parity.integration.test.ts +961 -0
  12. package/src/__tests__/internal-contracts-routes.test.ts +451 -0
  13. package/src/framework/env.ts +105 -0
  14. package/src/index.ts +13 -4
  15. package/src/lib/ai-store/evaluations.test.ts +3 -3
  16. package/src/lib/analyses-store.test.ts +6 -6
  17. package/src/lib/db-parity.ts +883 -0
  18. package/src/lib/eval-cases-store.test.ts +1 -1
  19. package/src/lib/eval-cases-store.ts +16 -4
  20. package/src/lib/evidence-references.ts +30 -22
  21. package/src/lib/stage6.test.ts +1 -1
  22. package/src/migrations/parity.test.ts +105 -3
  23. package/src/plugins/ai-receiver.ts +6 -1
  24. package/src/plugins/autosre-routes.ts +251 -140
  25. package/src/plugins/connected-routes/manifest.test.ts +114 -32
  26. package/src/plugins/connected-routes/manifest.ts +59 -76
  27. package/src/plugins/connected-routes.test.ts +10 -8
  28. package/src/plugins/connected-routes.ts +42 -12
  29. package/src/plugins/eval-cases-routes.ts +33 -10
  30. package/src/plugins/evidence-retrieval-routes.ts +60 -25
  31. package/dist/chunk-ZSF5XP3B.js.map +0 -1
  32. /package/dist/{openai-GSJZGFGL.js.map → openai-VDKVVZAV.js.map} +0 -0
@@ -1,11 +1,11 @@
1
1
  import type {
2
- CausalProofTier,
3
2
  CausalVerificationStatus,
4
3
  ChangeEventKind,
5
- CohortAttributionVerdict,
6
4
  EvidenceEntityKind,
7
5
  GrantCallbackDelivery,
8
6
  GrantDeliveryResult,
7
+ InternalEndpointKey,
8
+ InternalEndpoints,
9
9
  InvestigationFindingInput,
10
10
  JsonValue,
11
11
  RemediationActionKind,
@@ -14,11 +14,16 @@ import type {
14
14
  RemediationStateSpec,
15
15
  } from "@obsunified/types";
16
16
  import {
17
+ ChangeEventInputSchema,
17
18
  ChangeEventKindSchema,
18
19
  EvidencePredicateSchema,
20
+ InvestigationFindingHttpInputSchema,
19
21
  RemediationActionKindSchema,
22
+ RemediationProposalInputSchema,
23
+ validateContractResponse,
20
24
  } from "@obsunified/types";
21
25
  import { AgentAutonomyLevel } from "@obsunified/types/constants";
26
+ import type { Context } from "hono";
22
27
  import type { CollectorPlugin } from "../framework/collector";
23
28
  import type { CollectorEnv } from "../framework/env";
24
29
  import {
@@ -64,6 +69,21 @@ import { assertSafeWebhookUrl } from "../lib/webhook-url";
64
69
  import { getProjectId } from "./_context";
65
70
  import { readJsonObject } from "./_json";
66
71
 
72
+ export function respondWithContract<
73
+ K extends InternalEndpointKey,
74
+ C extends {
75
+ json: (data: unknown, status?: 200 | 201 | 202) => Response;
76
+ },
77
+ >(
78
+ c: C,
79
+ endpoint: K,
80
+ payload: InternalEndpoints[K]["response"],
81
+ status: 200 | 201 | 202 = 200,
82
+ ): Response {
83
+ validateContractResponse(endpoint, payload, status);
84
+ return c.json(payload, status);
85
+ }
86
+
67
87
  const CHANGE_KINDS = new Set<string>(ChangeEventKindSchema.options);
68
88
 
69
89
  const REMEDIATION_ACTION_KINDS = new Set<string>(
@@ -300,52 +320,63 @@ export const autosreRoutesPlugin: CollectorPlugin = {
300
320
  ? body.actor
301
321
  : null;
302
322
 
323
+ const rawInput = {
324
+ id: typeof body.id === "string" ? body.id : undefined,
325
+ environment:
326
+ typeof body.environment === "string" ? body.environment : undefined,
327
+ kind: normalizedKind,
328
+ source:
329
+ body.source === "inferred_telemetry" ||
330
+ body.source === "autosre_remediation" ||
331
+ body.source === "ci_webhook"
332
+ ? body.source
333
+ : ("explicit_api" as const),
334
+ targetNodeId:
335
+ typeof body.targetNodeId === "string" ? body.targetNodeId : undefined,
336
+ serviceName,
337
+ namespace,
338
+ version: typeof body.version === "string" ? body.version : null,
339
+ previousVersion:
340
+ typeof body.previousVersion === "string"
341
+ ? body.previousVersion
342
+ : null,
343
+ commitSha: typeof body.commitSha === "string" ? body.commitSha : null,
344
+ previousCommitSha:
345
+ typeof body.previousCommitSha === "string"
346
+ ? body.previousCommitSha
347
+ : null,
348
+ configDigest:
349
+ typeof body.configDigest === "string" ? body.configDigest : null,
350
+ summary,
351
+ actorId,
352
+ repositoryUrl,
353
+ pullRequestUrl,
354
+ causedByActionId:
355
+ typeof body.causedByActionId === "string"
356
+ ? body.causedByActionId
357
+ : null,
358
+ agentRunId:
359
+ typeof body.agentRunId === "string" ? body.agentRunId : null,
360
+ touchedPaths,
361
+ attributes,
362
+ occurredAt:
363
+ typeof body.occurredAt === "string" ? body.occurredAt : undefined,
364
+ };
365
+ const parsedInput = ChangeEventInputSchema.safeParse(rawInput);
366
+ if (!parsedInput.success) {
367
+ return c.json(
368
+ {
369
+ error: parsedInput.error.issues
370
+ .map((issue) => `${issue.path.join(".")}: ${issue.message}`)
371
+ .join("; "),
372
+ },
373
+ 400,
374
+ );
375
+ }
376
+
303
377
  let changeEvent: Awaited<ReturnType<typeof recordChangeEvent>>;
304
378
  try {
305
- changeEvent = await recordChangeEvent(db, projectId, {
306
- id: typeof body.id === "string" ? body.id : undefined,
307
- environment:
308
- typeof body.environment === "string" ? body.environment : undefined,
309
- kind: normalizedKind,
310
- source:
311
- body.source === "inferred_telemetry" ||
312
- body.source === "autosre_remediation" ||
313
- body.source === "ci_webhook"
314
- ? body.source
315
- : "explicit_api",
316
- targetNodeId:
317
- typeof body.targetNodeId === "string"
318
- ? body.targetNodeId
319
- : undefined,
320
- serviceName,
321
- namespace,
322
- version: typeof body.version === "string" ? body.version : null,
323
- previousVersion:
324
- typeof body.previousVersion === "string"
325
- ? body.previousVersion
326
- : null,
327
- commitSha: typeof body.commitSha === "string" ? body.commitSha : null,
328
- previousCommitSha:
329
- typeof body.previousCommitSha === "string"
330
- ? body.previousCommitSha
331
- : null,
332
- configDigest:
333
- typeof body.configDigest === "string" ? body.configDigest : null,
334
- summary,
335
- actorId,
336
- repositoryUrl,
337
- pullRequestUrl,
338
- causedByActionId:
339
- typeof body.causedByActionId === "string"
340
- ? body.causedByActionId
341
- : null,
342
- agentRunId:
343
- typeof body.agentRunId === "string" ? body.agentRunId : null,
344
- touchedPaths,
345
- attributes,
346
- occurredAt:
347
- typeof body.occurredAt === "string" ? body.occurredAt : undefined,
348
- });
379
+ changeEvent = await recordChangeEvent(db, projectId, parsedInput.data);
349
380
  } catch (err) {
350
381
  if (err instanceof InvalidTimestampError) {
351
382
  return c.json({ error: err.message }, 400);
@@ -353,7 +384,12 @@ export const autosreRoutesPlugin: CollectorPlugin = {
353
384
  throw err;
354
385
  }
355
386
 
356
- return c.json({ changeEvent, change: changeEvent }, 201);
387
+ return respondWithContract(
388
+ c,
389
+ "POST /v1/changes",
390
+ { ...changeEvent, changeEvent, change: changeEvent },
391
+ 201,
392
+ );
357
393
  });
358
394
 
359
395
  // 2. Ontology Graph & Fault Localization
@@ -371,7 +407,7 @@ export const autosreRoutesPlugin: CollectorPlugin = {
371
407
  ? await loadTraceFaultLocusFromDb(db, projectId, traceId)
372
408
  : null;
373
409
 
374
- return c.json({
410
+ return respondWithContract(c, "GET /internal/ontology", {
375
411
  ...graph,
376
412
  faultLocus,
377
413
  });
@@ -398,7 +434,7 @@ export const autosreRoutesPlugin: CollectorPlugin = {
398
434
  since: c.req.query("since") || undefined,
399
435
  limit: parseLimit(c.req.query("limit"), 50),
400
436
  });
401
- return c.json({ changes });
437
+ return respondWithContract(c, "GET /internal/changes", { changes });
402
438
  } catch (err) {
403
439
  if (err instanceof InvalidTimestampError) {
404
440
  return c.json({ error: err.message }, 400);
@@ -434,7 +470,10 @@ export const autosreRoutesPlugin: CollectorPlugin = {
434
470
  if (!blastRadius) {
435
471
  return c.json({ error: "Change event not found" }, 404);
436
472
  }
437
- return c.json({ blastRadius, ...blastRadius });
473
+ return respondWithContract(c, "GET /internal/changes/:id/blast-radius", {
474
+ blastRadius,
475
+ ...blastRadius,
476
+ });
438
477
  });
439
478
 
440
479
  // 4. Incident Investigations & Lease Coordination
@@ -447,10 +486,12 @@ export const autosreRoutesPlugin: CollectorPlugin = {
447
486
  status: c.req.query("status") || undefined,
448
487
  limit: parseLimit(c.req.query("limit"), 50),
449
488
  });
450
- return c.json({ incidents });
489
+ return respondWithContract(c, "GET /internal/incidents", { incidents });
451
490
  });
452
491
 
453
- app.post("/internal/incidents", async (c) => {
492
+ const openIncidentHandler = async (
493
+ c: Context<{ Bindings: CollectorEnv }>,
494
+ ) => {
454
495
  const projectId = getProjectId(c);
455
496
  const body = await readJsonObject<Record<string, unknown>>(c);
456
497
  if (!body) {
@@ -504,8 +545,15 @@ export const autosreRoutesPlugin: CollectorPlugin = {
504
545
  ? (body.latestBundle as Record<string, unknown>)
505
546
  : null,
506
547
  });
507
- return c.json(result, result.deduplicated ? 200 : 201);
508
- });
548
+ return respondWithContract(
549
+ c,
550
+ "POST /internal/incidents",
551
+ result,
552
+ result.deduplicated ? 200 : 201,
553
+ );
554
+ };
555
+ app.post("/internal/incidents", openIncidentHandler);
556
+ app.post("/internal/incidents/open", openIncidentHandler);
509
557
 
510
558
  app.post("/internal/incidents/:id/lease", async (c) => {
511
559
  const projectId = getProjectId(c);
@@ -519,7 +567,9 @@ export const autosreRoutesPlugin: CollectorPlugin = {
519
567
  ? body.runnerId.trim()
520
568
  : typeof body.leaseOwner === "string"
521
569
  ? body.leaseOwner.trim()
522
- : "";
570
+ : typeof body.leaseOwnerId === "string"
571
+ ? body.leaseOwnerId.trim()
572
+ : "";
523
573
  if (!runnerId) {
524
574
  return c.json({ error: "runnerId is required" }, 400);
525
575
  }
@@ -531,17 +581,28 @@ export const autosreRoutesPlugin: CollectorPlugin = {
531
581
  : typeof body.ttlSeconds === "number" &&
532
582
  Number.isFinite(body.ttlSeconds)
533
583
  ? body.ttlSeconds
534
- : typeof body.leaseDurationMs === "number" &&
535
- Number.isFinite(body.leaseDurationMs)
536
- ? Math.ceil(body.leaseDurationMs / 1000)
537
- : undefined;
584
+ : typeof body.leaseSeconds === "number" &&
585
+ Number.isFinite(body.leaseSeconds)
586
+ ? body.leaseSeconds
587
+ : typeof body.leaseDurationMs === "number" &&
588
+ Number.isFinite(body.leaseDurationMs)
589
+ ? Math.ceil(body.leaseDurationMs / 1000)
590
+ : undefined;
538
591
  const result = await acquireIncidentLease(db, projectId, incidentId, {
539
592
  environment:
540
593
  typeof body.environment === "string" ? body.environment : undefined,
541
594
  runnerId,
542
595
  leaseDurationSeconds,
543
596
  });
544
- return c.json(result, result.acquired ? 200 : 409);
597
+ if (!result.acquired) {
598
+ return c.json(result, 409);
599
+ }
600
+ return respondWithContract(
601
+ c,
602
+ "POST /internal/incidents/:id/lease",
603
+ result,
604
+ 200,
605
+ );
545
606
  });
546
607
 
547
608
  app.post("/internal/incidents/:id/dispatch", async (c) => {
@@ -568,7 +629,15 @@ export const autosreRoutesPlugin: CollectorPlugin = {
568
629
  allowPrivateNetworks: c.env?.ALERT_WEBHOOK_ALLOW_PRIVATE === "true",
569
630
  logger: runtime.logger,
570
631
  });
571
- return c.json(result, result.delivered ? 200 : 502);
632
+ if (!result.delivered) {
633
+ return c.json({ ...result, dispatched: result.delivered }, 502);
634
+ }
635
+ return respondWithContract(
636
+ c,
637
+ "POST /internal/incidents/:id/dispatch",
638
+ { ...result, dispatched: result.delivered },
639
+ 200,
640
+ );
572
641
  });
573
642
 
574
643
  // 5. Investigation Findings & Counterfactual Verification
@@ -591,7 +660,7 @@ export const autosreRoutesPlugin: CollectorPlugin = {
591
660
  incidentId: c.req.query("incidentId") || undefined,
592
661
  limit: parseLimit(c.req.query("limit"), 20),
593
662
  });
594
- return c.json({ findings });
663
+ return respondWithContract(c, "GET /internal/findings", { findings });
595
664
  });
596
665
 
597
666
  app.post("/internal/findings", async (c) => {
@@ -657,72 +726,67 @@ export const autosreRoutesPlugin: CollectorPlugin = {
657
726
  }
658
727
  }
659
728
 
660
- const db = sqlDbFor(c.env);
661
- const input: InvestigationFindingInput = {
662
- id: typeof body.id === "string" ? body.id : undefined,
663
- idempotencyKey:
664
- typeof body.idempotencyKey === "string" && body.idempotencyKey.trim()
665
- ? body.idempotencyKey.trim()
666
- : undefined,
667
- environment:
668
- typeof body.environment === "string" ? body.environment : undefined,
669
- incidentId:
670
- typeof body.incidentId === "string" ? body.incidentId : null,
671
- fingerprintInput:
672
- body.fingerprintInput && typeof body.fingerprintInput === "object"
673
- ? (body.fingerprintInput as InvestigationFindingInput["fingerprintInput"])
674
- : undefined,
729
+ const parsed = InvestigationFindingHttpInputSchema.safeParse({
730
+ ...body,
675
731
  title,
676
732
  summary,
677
- causalProofTier:
678
- typeof body.causalProofTier === "string"
679
- ? (body.causalProofTier as CausalProofTier)
680
- : undefined,
681
- confidence:
682
- typeof body.confidence === "number" ? body.confidence : undefined,
683
- symptomEntityKind: symptomEntityKind as EvidenceEntityKind,
733
+ symptomEntityKind,
684
734
  symptomEntityId,
685
- faultLocusEntityKind: faultLocusEntityKind as EvidenceEntityKind,
735
+ faultLocusEntityKind,
686
736
  faultLocusEntityId,
687
- rootCauseEntityKind:
688
- typeof body.rootCauseEntityKind === "string"
689
- ? (body.rootCauseEntityKind as EvidenceEntityKind)
690
- : null,
691
- rootCauseEntityId:
692
- typeof body.rootCauseEntityId === "string"
693
- ? body.rootCauseEntityId
694
- : null,
695
- contributingChangeId:
696
- typeof body.contributingChangeId === "string"
697
- ? body.contributingChangeId
698
- : null,
699
- cohortVerdict:
700
- typeof body.cohortVerdict === "string"
701
- ? (body.cohortVerdict as CohortAttributionVerdict)
702
- : null,
703
- contendingTraceId:
704
- typeof body.contendingTraceId === "string"
705
- ? body.contendingTraceId
706
- : null,
707
- causalChain: Array.isArray(body.causalChain)
708
- ? (body.causalChain as InvestigationFindingInput["causalChain"])
709
- : [],
710
- citedEvidenceIds: Array.isArray(body.citedEvidenceIds)
711
- ? body.citedEvidenceIds.filter(
712
- (id): id is string => typeof id === "string",
713
- )
714
- : [],
715
- codeReferences: Array.isArray(body.codeReferences)
716
- ? (body.codeReferences as InvestigationFindingInput["codeReferences"])
717
- : undefined,
718
- agentRunId:
719
- typeof body.agentRunId === "string" ? body.agentRunId : null,
737
+ idempotencyKey:
738
+ typeof body.idempotencyKey === "string"
739
+ ? body.idempotencyKey.trim() || undefined
740
+ : body.idempotencyKey,
741
+ });
742
+ if (!parsed.success) {
743
+ return c.json(
744
+ {
745
+ error: parsed.error.issues
746
+ .map((issue) => `${issue.path.join(".")}: ${issue.message}`)
747
+ .join("; "),
748
+ },
749
+ 400,
750
+ );
751
+ }
752
+ const data = parsed.data;
753
+
754
+ const db = sqlDbFor(c.env);
755
+ const input: InvestigationFindingInput = {
756
+ id: data.id,
757
+ idempotencyKey: data.idempotencyKey,
758
+ environment: data.environment,
759
+ incidentId: data.incidentId ?? null,
760
+ fingerprintInput: data.fingerprintInput,
761
+ title: data.title,
762
+ summary: data.summary,
763
+ causalProofTier: data.causalProofTier,
764
+ confidence: data.confidence,
765
+ symptomEntityKind: data.symptomEntityKind,
766
+ symptomEntityId: data.symptomEntityId,
767
+ faultLocusEntityKind: data.faultLocusEntityKind,
768
+ faultLocusEntityId: data.faultLocusEntityId,
769
+ rootCauseEntityKind: data.rootCauseEntityKind ?? null,
770
+ rootCauseEntityId: data.rootCauseEntityId ?? null,
771
+ contributingChangeId: data.contributingChangeId ?? null,
772
+ cohortVerdict: data.cohortVerdict ?? null,
773
+ contendingTraceId: data.contendingTraceId ?? null,
774
+ causalChain: (data.causalChain ??
775
+ []) as InvestigationFindingInput["causalChain"],
776
+ citedEvidenceIds: data.citedEvidenceIds ?? [],
777
+ codeReferences: data.codeReferences,
778
+ agentRunId: data.agentRunId ?? null,
720
779
  };
721
780
 
722
781
  const finding = await recordInvestigationFinding(db, projectId, input, {
723
782
  logger: runtime.logger,
724
783
  });
725
- return c.json({ finding, id: finding.id }, 201);
784
+ return respondWithContract(
785
+ c,
786
+ "POST /internal/findings",
787
+ { ...finding, finding, id: finding.id },
788
+ 201,
789
+ );
726
790
  });
727
791
 
728
792
  app.post("/internal/findings/:id/counterfactual", async (c) => {
@@ -770,7 +834,15 @@ export const autosreRoutesPlugin: CollectorPlugin = {
770
834
  : undefined,
771
835
  },
772
836
  );
773
- return c.json({ verified: true, finding });
837
+ return respondWithContract(
838
+ c,
839
+ "POST /internal/findings/:id/counterfactual",
840
+ {
841
+ verified: true,
842
+ finding,
843
+ proof: finding.counterfactualProof,
844
+ },
845
+ );
774
846
  } catch (err) {
775
847
  const message = err instanceof Error ? err.message : String(err);
776
848
  if (err instanceof CounterfactualVerificationError) {
@@ -806,7 +878,7 @@ export const autosreRoutesPlugin: CollectorPlugin = {
806
878
  fail_count: number | null;
807
879
  }>();
808
880
  if (!row) return c.json({ error: "Eval run not found" }, 404);
809
- return c.json({
881
+ return respondWithContract(c, "GET /internal/evals/runs/:id", {
810
882
  run: {
811
883
  id: row.id,
812
884
  evalCaseId: row.eval_case_id,
@@ -832,7 +904,9 @@ export const autosreRoutesPlugin: CollectorPlugin = {
832
904
  undefined,
833
905
  limit: parseLimit(c.req.query("limit"), 50),
834
906
  });
835
- return c.json({ proposals });
907
+ return respondWithContract(c, "GET /internal/remediations", {
908
+ proposals,
909
+ });
836
910
  });
837
911
 
838
912
  app.post("/internal/remediations/propose", async (c) => {
@@ -906,7 +980,7 @@ export const autosreRoutesPlugin: CollectorPlugin = {
906
980
  );
907
981
  }
908
982
  }
909
- const input: RemediationProposalInput = {
983
+ const rawInput: RemediationProposalInput = {
910
984
  environment:
911
985
  typeof body.environment === "string" ? body.environment : undefined,
912
986
  incidentId,
@@ -918,6 +992,10 @@ export const autosreRoutesPlugin: CollectorPlugin = {
918
992
  desiredTargetState,
919
993
  requestedAutonomy,
920
994
  };
995
+ const parsedInput = RemediationProposalInputSchema.safeParse(rawInput);
996
+ const input: RemediationProposalInput = parsedInput.success
997
+ ? parsedInput.data
998
+ : rawInput;
921
999
 
922
1000
  try {
923
1001
  const proposal = await proposeRemediationAction(db, projectId, input, {
@@ -939,7 +1017,9 @@ export const autosreRoutesPlugin: CollectorPlugin = {
939
1017
  body,
940
1018
  proposal,
941
1019
  );
942
- return c.json(
1020
+ return respondWithContract(
1021
+ c,
1022
+ "POST /internal/remediations/propose",
943
1023
  {
944
1024
  proposal,
945
1025
  ...proposal,
@@ -1002,13 +1082,17 @@ export const autosreRoutesPlugin: CollectorPlugin = {
1002
1082
  proposal,
1003
1083
  storedCallback,
1004
1084
  );
1005
- return c.json({
1006
- proposal,
1007
- ...proposal,
1008
- grantToken: proposal.grantToken ?? null,
1009
- grantDelivery,
1010
- ...(webhookDelivery ? { webhookDelivery } : {}),
1011
- });
1085
+ return respondWithContract(
1086
+ c,
1087
+ "POST /internal/remediations/:id/approve",
1088
+ {
1089
+ proposal,
1090
+ ...proposal,
1091
+ grantToken: proposal.grantToken ?? null,
1092
+ grantDelivery,
1093
+ ...(webhookDelivery ? { webhookDelivery } : {}),
1094
+ },
1095
+ );
1012
1096
  } catch (err) {
1013
1097
  const message = err instanceof Error ? err.message : String(err);
1014
1098
  const status =
@@ -1048,7 +1132,11 @@ export const autosreRoutesPlugin: CollectorPlugin = {
1048
1132
  reason,
1049
1133
  },
1050
1134
  );
1051
- return c.json({ proposal, ...proposal });
1135
+ return respondWithContract(
1136
+ c,
1137
+ "POST /internal/remediations/:id/reject",
1138
+ { proposal, ...proposal },
1139
+ );
1052
1140
  } catch (err) {
1053
1141
  const message = err instanceof Error ? err.message : String(err);
1054
1142
  const status =
@@ -1088,7 +1176,11 @@ export const autosreRoutesPlugin: CollectorPlugin = {
1088
1176
  logger: runtime.logger,
1089
1177
  },
1090
1178
  );
1091
- return c.json({ claimed: true, proposal, ...proposal });
1179
+ return respondWithContract(c, "POST /internal/remediations/:id/claim", {
1180
+ claimed: true,
1181
+ proposal,
1182
+ ...proposal,
1183
+ });
1092
1184
  } catch (err) {
1093
1185
  const message = err instanceof Error ? err.message : String(err);
1094
1186
  const status =
@@ -1134,7 +1226,11 @@ export const autosreRoutesPlugin: CollectorPlugin = {
1134
1226
  : null,
1135
1227
  },
1136
1228
  );
1137
- return c.json({ proposal, ...proposal });
1229
+ return respondWithContract(
1230
+ c,
1231
+ "POST /internal/remediations/:id/complete",
1232
+ { proposal, ...proposal },
1233
+ );
1138
1234
  } catch (err) {
1139
1235
  const message = err instanceof Error ? err.message : String(err);
1140
1236
  const status =
@@ -1173,7 +1269,10 @@ export const autosreRoutesPlugin: CollectorPlugin = {
1173
1269
  : "Execution failed",
1174
1270
  },
1175
1271
  );
1176
- return c.json({ proposal, ...proposal });
1272
+ return respondWithContract(c, "POST /internal/remediations/:id/fail", {
1273
+ proposal,
1274
+ ...proposal,
1275
+ });
1177
1276
  } catch (err) {
1178
1277
  const message = err instanceof Error ? err.message : String(err);
1179
1278
  const status =
@@ -1200,7 +1299,10 @@ export const autosreRoutesPlugin: CollectorPlugin = {
1200
1299
  : undefined,
1201
1300
  logger: runtime.logger,
1202
1301
  });
1203
- return c.json({ summary, ...summary });
1302
+ return respondWithContract(c, "POST /internal/remediations/watchdog", {
1303
+ summary,
1304
+ ...summary,
1305
+ });
1204
1306
  });
1205
1307
 
1206
1308
  app.get("/internal/remediations/:id", async (c) => {
@@ -1216,7 +1318,9 @@ export const autosreRoutesPlugin: CollectorPlugin = {
1216
1318
  if (!proposal) {
1217
1319
  return c.json({ error: "Proposal not found" }, 404);
1218
1320
  }
1219
- return c.json({ proposal });
1321
+ return respondWithContract(c, "GET /internal/remediations/:id", {
1322
+ proposal,
1323
+ });
1220
1324
  });
1221
1325
 
1222
1326
  app.get("/internal/incidents/:id", async (c) => {
@@ -1247,7 +1351,11 @@ export const autosreRoutesPlugin: CollectorPlugin = {
1247
1351
  limit: 20,
1248
1352
  }),
1249
1353
  ]);
1250
- return c.json({ incident, findings, proposals });
1354
+ return respondWithContract(c, "GET /internal/incidents/:id", {
1355
+ incident,
1356
+ findings,
1357
+ proposals,
1358
+ });
1251
1359
  });
1252
1360
 
1253
1361
  app.get("/internal/findings/:id", async (c) => {
@@ -1263,7 +1371,7 @@ export const autosreRoutesPlugin: CollectorPlugin = {
1263
1371
  if (!finding) {
1264
1372
  return c.json({ error: "Finding not found" }, 404);
1265
1373
  }
1266
- return c.json({ finding });
1374
+ return respondWithContract(c, "GET /internal/findings/:id", { finding });
1267
1375
  });
1268
1376
 
1269
1377
  app.get("/internal/changes/:id", async (c) => {
@@ -1279,7 +1387,10 @@ export const autosreRoutesPlugin: CollectorPlugin = {
1279
1387
  if (!changeEvent) {
1280
1388
  return c.json({ error: "Change event not found" }, 404);
1281
1389
  }
1282
- return c.json({ changeEvent, change: changeEvent });
1390
+ return respondWithContract(c, "GET /internal/changes/:id", {
1391
+ changeEvent,
1392
+ change: changeEvent,
1393
+ });
1283
1394
  });
1284
1395
  },
1285
1396
  };