@debugbundle/mcp 0.1.7 → 0.1.9

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 (3) hide show
  1. package/README.md +2 -0
  2. package/dist/main.cjs +2382 -155
  3. package/package.json +3 -3
package/dist/main.cjs CHANGED
@@ -14776,8 +14776,9 @@ var GitHubDispatchDeliverySchema = external_exports.object({
14776
14776
  delivery_id: external_exports.string(),
14777
14777
  rule_id: external_exports.string(),
14778
14778
  rule_name: external_exports.string(),
14779
- incident_id: external_exports.string(),
14780
- incident_title: external_exports.string(),
14779
+ incident_id: external_exports.string().nullable(),
14780
+ improvement_id: external_exports.string().nullable(),
14781
+ target_title: external_exports.string(),
14781
14782
  status: external_exports.enum(["pending", "retrying", "delivered", "failed", "skipped"]),
14782
14783
  attempt_count: external_exports.number().int(),
14783
14784
  last_attempt_at: external_exports.string().nullable(),
@@ -15259,6 +15260,7 @@ var ImprovementSchema = external_exports.object({
15259
15260
  summary: external_exports.string(),
15260
15261
  occurrence_count: external_exports.number().int(),
15261
15262
  evidence: external_exports.record(external_exports.unknown()),
15263
+ related_incident_ids: external_exports.array(external_exports.string()),
15262
15264
  first_detected_at: external_exports.string(),
15263
15265
  last_detected_at: external_exports.string(),
15264
15266
  resolved_at: external_exports.string().nullable(),
@@ -15791,6 +15793,7 @@ var ProjectTokenSchema = external_exports.object({
15791
15793
  token_id: external_exports.string(),
15792
15794
  project_id: external_exports.string(),
15793
15795
  label: external_exports.string(),
15796
+ allowed_origins: external_exports.array(external_exports.string()).default([]),
15794
15797
  created_at: external_exports.string(),
15795
15798
  last_used_at: external_exports.string().nullable(),
15796
15799
  revoked_at: external_exports.string().nullable(),
@@ -15875,7 +15878,8 @@ function createTokenManagementApi(client) {
15875
15878
  path: `/v1/projects/${input.projectId}/tokens`,
15876
15879
  bearerToken: input.bearerToken,
15877
15880
  body: {
15878
- label: input.label
15881
+ label: input.label,
15882
+ ...input.allowedOrigins === void 0 ? {} : { allowed_origins: input.allowedOrigins }
15879
15883
  }
15880
15884
  })
15881
15885
  );
@@ -16548,6 +16552,406 @@ function getRequestAnomalyThreshold(input) {
16548
16552
  return null;
16549
16553
  }
16550
16554
 
16555
+ // ../../packages/shared-types/src/capture-rules.ts
16556
+ var CAPTURE_RULE_EVENT_TYPES = [
16557
+ "backend_exception",
16558
+ "request_event",
16559
+ "log_event",
16560
+ "frontend_breadcrumb",
16561
+ "frontend_exception",
16562
+ "deploy_metadata",
16563
+ "error_suppressed",
16564
+ "probe_event"
16565
+ ];
16566
+ var CAPTURE_RULE_RUNTIME_VALUES = [
16567
+ "browser",
16568
+ "node",
16569
+ "python",
16570
+ "php",
16571
+ "java",
16572
+ "go",
16573
+ "ruby",
16574
+ "unknown"
16575
+ ];
16576
+ var CaptureRuleActionValues = ["demote", "sample", "drop"];
16577
+ var CaptureRuleActionSchema = external_exports.enum(CaptureRuleActionValues);
16578
+ var CaptureRuleSampleEventClassValues = ["preserve", "context"];
16579
+ var CaptureRuleSampleEventClassSchema = external_exports.enum(CaptureRuleSampleEventClassValues);
16580
+ var CaptureRuleRuntimeSchema = external_exports.enum(CAPTURE_RULE_RUNTIME_VALUES);
16581
+ var CaptureRuleEventTypeSchema = external_exports.enum(CAPTURE_RULE_EVENT_TYPES);
16582
+ var BrowserEventKindSchema = external_exports.enum(["window_error", "resource_error"]);
16583
+ function normalizeOptionalTrimmedString(value) {
16584
+ const trimmed = value?.trim();
16585
+ return trimmed && trimmed.length > 0 ? trimmed : void 0;
16586
+ }
16587
+ function normalizeOptionalLowercaseHost(value) {
16588
+ const trimmed = normalizeOptionalTrimmedString(value);
16589
+ return trimmed?.toLowerCase();
16590
+ }
16591
+ function normalizeOptionalPath(value) {
16592
+ const trimmed = normalizeOptionalTrimmedString(value);
16593
+ if (trimmed === void 0) {
16594
+ return void 0;
16595
+ }
16596
+ return trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
16597
+ }
16598
+ function hasValue(value) {
16599
+ return value !== void 0 && value !== null;
16600
+ }
16601
+ var UrlMatcherSchema = external_exports.object({
16602
+ host: external_exports.string().min(1).max(255).optional(),
16603
+ host_suffix: external_exports.string().min(1).max(255).optional(),
16604
+ path_prefix: external_exports.string().min(1).max(1024).optional(),
16605
+ path_equals: external_exports.string().min(1).max(1024).optional()
16606
+ }).transform((value) => {
16607
+ const normalized = {};
16608
+ const host = normalizeOptionalLowercaseHost(value.host);
16609
+ const hostSuffix = normalizeOptionalLowercaseHost(value.host_suffix);
16610
+ const pathPrefix = normalizeOptionalPath(value.path_prefix);
16611
+ const pathEquals = normalizeOptionalPath(value.path_equals);
16612
+ if (host !== void 0) {
16613
+ normalized.host = host;
16614
+ }
16615
+ if (hostSuffix !== void 0) {
16616
+ normalized.host_suffix = hostSuffix;
16617
+ }
16618
+ if (pathPrefix !== void 0) {
16619
+ normalized.path_prefix = pathPrefix;
16620
+ }
16621
+ if (pathEquals !== void 0) {
16622
+ normalized.path_equals = pathEquals;
16623
+ }
16624
+ return normalized;
16625
+ }).refine((value) => hasValue(value.host) || hasValue(value.host_suffix) || hasValue(value.path_prefix) || hasValue(value.path_equals), {
16626
+ message: "URL matchers must include at least one host or path constraint."
16627
+ });
16628
+ var StatusRangeSchema = external_exports.object({
16629
+ start: external_exports.number().int().min(100).max(599),
16630
+ end: external_exports.number().int().min(100).max(599)
16631
+ }).refine((value) => value.start <= value.end, {
16632
+ message: "Status range start must be less than or equal to end."
16633
+ });
16634
+ var CaptureRuleFingerprintSchema = external_exports.object({
16635
+ version: external_exports.string().min(1).max(32),
16636
+ value: external_exports.string().min(1).max(256)
16637
+ });
16638
+ function normalizeStringArray(values) {
16639
+ if (values === void 0) {
16640
+ return void 0;
16641
+ }
16642
+ return Array.from(new Set(values.map((value) => value.trim()).filter((value) => value.length > 0)));
16643
+ }
16644
+ function normalizeNumberArray(values) {
16645
+ if (values === void 0) {
16646
+ return void 0;
16647
+ }
16648
+ return Array.from(new Set(values)).sort((left, right) => left - right);
16649
+ }
16650
+ var CaptureRuleMatcherSchema = external_exports.object({
16651
+ event_types: external_exports.array(CaptureRuleEventTypeSchema).min(1).optional(),
16652
+ services: external_exports.array(external_exports.string().min(1).max(120)).min(1).optional(),
16653
+ environments: external_exports.array(external_exports.string().min(1).max(120)).min(1).optional(),
16654
+ runtime: external_exports.array(CaptureRuleRuntimeSchema).min(1).optional(),
16655
+ first_party: external_exports.boolean().optional(),
16656
+ error_name: external_exports.string().min(1).max(120).optional(),
16657
+ message_contains: external_exports.string().min(1).max(500).optional(),
16658
+ message_equals: external_exports.string().min(1).max(500).optional(),
16659
+ browser_event_kind: BrowserEventKindSchema.optional(),
16660
+ resource_url: UrlMatcherSchema.optional(),
16661
+ request_url: UrlMatcherSchema.optional(),
16662
+ status_codes: external_exports.array(external_exports.number().int().min(100).max(599)).min(1).optional(),
16663
+ status_ranges: external_exports.array(StatusRangeSchema).min(1).optional(),
16664
+ fingerprint: CaptureRuleFingerprintSchema.optional()
16665
+ }).transform((value) => {
16666
+ const normalized = {};
16667
+ const eventTypes = normalizeStringArray(value.event_types);
16668
+ const services = normalizeStringArray(value.services);
16669
+ const environments = normalizeStringArray(value.environments);
16670
+ const runtime = normalizeStringArray(value.runtime);
16671
+ const errorName = normalizeOptionalTrimmedString(value.error_name);
16672
+ const messageContains = normalizeOptionalTrimmedString(value.message_contains);
16673
+ const messageEquals = normalizeOptionalTrimmedString(value.message_equals);
16674
+ const statusCodes = normalizeNumberArray(value.status_codes);
16675
+ if (eventTypes !== void 0) {
16676
+ normalized.event_types = eventTypes;
16677
+ }
16678
+ if (services !== void 0) {
16679
+ normalized.services = services;
16680
+ }
16681
+ if (environments !== void 0) {
16682
+ normalized.environments = environments;
16683
+ }
16684
+ if (runtime !== void 0) {
16685
+ normalized.runtime = runtime;
16686
+ }
16687
+ if (value.first_party !== void 0) {
16688
+ normalized.first_party = value.first_party;
16689
+ }
16690
+ if (errorName !== void 0) {
16691
+ normalized.error_name = errorName;
16692
+ }
16693
+ if (messageContains !== void 0) {
16694
+ normalized.message_contains = messageContains;
16695
+ }
16696
+ if (messageEquals !== void 0) {
16697
+ normalized.message_equals = messageEquals;
16698
+ }
16699
+ if (value.browser_event_kind !== void 0) {
16700
+ normalized.browser_event_kind = value.browser_event_kind;
16701
+ }
16702
+ if (value.resource_url !== void 0) {
16703
+ normalized.resource_url = value.resource_url;
16704
+ }
16705
+ if (value.request_url !== void 0) {
16706
+ normalized.request_url = value.request_url;
16707
+ }
16708
+ if (statusCodes !== void 0) {
16709
+ normalized.status_codes = statusCodes;
16710
+ }
16711
+ if (value.status_ranges !== void 0) {
16712
+ normalized.status_ranges = value.status_ranges;
16713
+ }
16714
+ if (value.fingerprint !== void 0) {
16715
+ normalized.fingerprint = value.fingerprint;
16716
+ }
16717
+ return normalized;
16718
+ }).superRefine((value, context) => {
16719
+ const narrowingKeys = [
16720
+ "services",
16721
+ "environments",
16722
+ "runtime",
16723
+ "first_party",
16724
+ "error_name",
16725
+ "message_contains",
16726
+ "message_equals",
16727
+ "browser_event_kind",
16728
+ "resource_url",
16729
+ "request_url",
16730
+ "status_codes",
16731
+ "status_ranges",
16732
+ "fingerprint"
16733
+ ];
16734
+ if (!narrowingKeys.some((key) => hasValue(value[key]))) {
16735
+ context.addIssue({
16736
+ code: external_exports.ZodIssueCode.custom,
16737
+ message: "Capture rules must include at least one narrowing field beyond event_types."
16738
+ });
16739
+ }
16740
+ if (value.browser_event_kind === "resource_error") {
16741
+ const hasResourceConstraint = hasValue(value.resource_url) || hasValue(value.fingerprint);
16742
+ if (!hasResourceConstraint) {
16743
+ context.addIssue({
16744
+ code: external_exports.ZodIssueCode.custom,
16745
+ message: "Resource-error rules require a resource URL constraint or an exact fingerprint."
16746
+ });
16747
+ }
16748
+ }
16749
+ });
16750
+ var CaptureRuleCoreObjectSchema = external_exports.object({
16751
+ name: external_exports.string().trim().min(1).max(120),
16752
+ description: external_exports.string().trim().max(500).nullable(),
16753
+ enabled: external_exports.boolean(),
16754
+ action: CaptureRuleActionSchema,
16755
+ matcher: CaptureRuleMatcherSchema,
16756
+ sample_rate: external_exports.number().min(0).max(1).nullable(),
16757
+ sample_event_class: CaptureRuleSampleEventClassSchema.nullable(),
16758
+ created_by_user_id: external_exports.string().min(1).max(120).nullable(),
16759
+ created_from_incident_id: external_exports.string().min(1).max(120).nullable(),
16760
+ created_from_event_id: external_exports.string().min(1).max(120).nullable(),
16761
+ expires_at: external_exports.string().datetime().nullable()
16762
+ });
16763
+ function addCaptureRuleActionValidation(schema) {
16764
+ return schema.superRefine((value, context) => {
16765
+ if (value["action"] === "sample") {
16766
+ if (value["sample_rate"] === null) {
16767
+ context.addIssue({
16768
+ code: external_exports.ZodIssueCode.custom,
16769
+ path: ["sample_rate"],
16770
+ message: "Sample rules require sample_rate."
16771
+ });
16772
+ }
16773
+ if (value["sample_event_class"] === null) {
16774
+ context.addIssue({
16775
+ code: external_exports.ZodIssueCode.custom,
16776
+ path: ["sample_event_class"],
16777
+ message: "Sample rules require sample_event_class."
16778
+ });
16779
+ }
16780
+ return;
16781
+ }
16782
+ if (value["sample_rate"] !== null) {
16783
+ context.addIssue({
16784
+ code: external_exports.ZodIssueCode.custom,
16785
+ path: ["sample_rate"],
16786
+ message: "Only sample rules can set sample_rate."
16787
+ });
16788
+ }
16789
+ if (value["sample_event_class"] !== null) {
16790
+ context.addIssue({
16791
+ code: external_exports.ZodIssueCode.custom,
16792
+ path: ["sample_event_class"],
16793
+ message: "Only sample rules can set sample_event_class."
16794
+ });
16795
+ }
16796
+ });
16797
+ }
16798
+ var CaptureRuleSchema = addCaptureRuleActionValidation(
16799
+ CaptureRuleCoreObjectSchema.extend({
16800
+ id: external_exports.string().uuid(),
16801
+ project_id: external_exports.string().min(1).max(120),
16802
+ hit_count: external_exports.number().int().nonnegative(),
16803
+ last_matched_at: external_exports.string().datetime().nullable(),
16804
+ created_at: external_exports.string().datetime(),
16805
+ updated_at: external_exports.string().datetime()
16806
+ })
16807
+ );
16808
+ var CaptureRuleCreateSchema = external_exports.object({
16809
+ name: external_exports.string().trim().min(1).max(120),
16810
+ description: external_exports.string().trim().max(500).nullable().default(null),
16811
+ enabled: external_exports.boolean().default(true),
16812
+ action: CaptureRuleActionSchema,
16813
+ matcher: CaptureRuleMatcherSchema,
16814
+ sample_rate: external_exports.number().min(0).max(1).nullable().optional(),
16815
+ sample_event_class: CaptureRuleSampleEventClassSchema.nullable().optional(),
16816
+ created_by_user_id: external_exports.string().min(1).max(120).nullable().default(null),
16817
+ created_from_incident_id: external_exports.string().min(1).max(120).nullable().default(null),
16818
+ created_from_event_id: external_exports.string().min(1).max(120).nullable().default(null),
16819
+ expires_at: external_exports.string().datetime().nullable().default(null)
16820
+ }).superRefine((value, context) => {
16821
+ if (value.action === "sample") {
16822
+ if (value.sample_rate === void 0 || value.sample_rate === null) {
16823
+ context.addIssue({
16824
+ code: external_exports.ZodIssueCode.custom,
16825
+ path: ["sample_rate"],
16826
+ message: "Sample rules require sample_rate."
16827
+ });
16828
+ }
16829
+ return;
16830
+ }
16831
+ if (value.sample_rate !== void 0 && value.sample_rate !== null) {
16832
+ context.addIssue({
16833
+ code: external_exports.ZodIssueCode.custom,
16834
+ path: ["sample_rate"],
16835
+ message: "Only sample rules can set sample_rate."
16836
+ });
16837
+ }
16838
+ if (value.sample_event_class !== void 0 && value.sample_event_class !== null) {
16839
+ context.addIssue({
16840
+ code: external_exports.ZodIssueCode.custom,
16841
+ path: ["sample_event_class"],
16842
+ message: "Only sample rules can set sample_event_class."
16843
+ });
16844
+ }
16845
+ }).transform((value) => ({
16846
+ ...value,
16847
+ sample_rate: value.action === "sample" ? value.sample_rate : null,
16848
+ sample_event_class: value.action === "sample" ? value.sample_event_class ?? "preserve" : null
16849
+ }));
16850
+ var CaptureRuleUpdateSchema = external_exports.object({
16851
+ name: external_exports.string().trim().min(1).max(120).optional(),
16852
+ description: external_exports.string().trim().max(500).nullable().optional(),
16853
+ enabled: external_exports.boolean().optional(),
16854
+ action: CaptureRuleActionSchema.optional(),
16855
+ matcher: CaptureRuleMatcherSchema.optional(),
16856
+ sample_rate: external_exports.number().min(0).max(1).nullable().optional(),
16857
+ sample_event_class: CaptureRuleSampleEventClassSchema.nullable().optional(),
16858
+ expires_at: external_exports.string().datetime().nullable().optional()
16859
+ }).superRefine((value, context) => {
16860
+ if (Object.keys(value).length === 0) {
16861
+ context.addIssue({
16862
+ code: external_exports.ZodIssueCode.custom,
16863
+ message: "At least one capture rule field must be provided."
16864
+ });
16865
+ }
16866
+ const resolvedAction = value.action;
16867
+ if (resolvedAction === "sample") {
16868
+ if (!("sample_rate" in value)) {
16869
+ context.addIssue({
16870
+ code: external_exports.ZodIssueCode.custom,
16871
+ path: ["sample_rate"],
16872
+ message: "Sample rule updates must include sample_rate when changing action to sample."
16873
+ });
16874
+ }
16875
+ if (!("sample_event_class" in value)) {
16876
+ context.addIssue({
16877
+ code: external_exports.ZodIssueCode.custom,
16878
+ path: ["sample_event_class"],
16879
+ message: "Sample rule updates must include sample_event_class when changing action to sample."
16880
+ });
16881
+ }
16882
+ return;
16883
+ }
16884
+ if ("sample_rate" in value) {
16885
+ context.addIssue({
16886
+ code: external_exports.ZodIssueCode.custom,
16887
+ path: ["sample_rate"],
16888
+ message: "Sample rule fields can only be updated while setting action to sample."
16889
+ });
16890
+ }
16891
+ if ("sample_event_class" in value) {
16892
+ context.addIssue({
16893
+ code: external_exports.ZodIssueCode.custom,
16894
+ path: ["sample_event_class"],
16895
+ message: "Sample rule fields can only be updated while setting action to sample."
16896
+ });
16897
+ }
16898
+ });
16899
+ var CaptureRuleResponseSchema = external_exports.object({
16900
+ rule: CaptureRuleSchema
16901
+ });
16902
+ var CaptureRulesResponseSchema = external_exports.object({
16903
+ access_mode: external_exports.enum(["manage", "preview"]),
16904
+ rules: external_exports.array(CaptureRuleSchema)
16905
+ });
16906
+ var CaptureRulesFileSchema = external_exports.object({
16907
+ version: external_exports.literal(1),
16908
+ rules: external_exports.array(CaptureRuleSchema)
16909
+ });
16910
+ var CaptureRuleEvaluationUrlSchema = external_exports.object({
16911
+ host: external_exports.string().min(1).transform((value) => value.toLowerCase()).optional(),
16912
+ path: external_exports.string().min(1).transform((value) => value.startsWith("/") ? value : `/${value}`)
16913
+ });
16914
+ var CaptureRuleEvaluationContextSchema = external_exports.object({
16915
+ project_id: external_exports.string().min(1).max(120),
16916
+ event_id: external_exports.string().uuid(),
16917
+ event_type: CaptureRuleEventTypeSchema,
16918
+ service: external_exports.string().min(1).optional(),
16919
+ environment: external_exports.string().min(1).optional(),
16920
+ runtime: CaptureRuleRuntimeSchema,
16921
+ first_party: external_exports.boolean().optional(),
16922
+ error_name: external_exports.string().min(1).optional(),
16923
+ message: external_exports.string().min(1).optional(),
16924
+ browser_event_kind: BrowserEventKindSchema.optional(),
16925
+ resource_url: CaptureRuleEvaluationUrlSchema.optional(),
16926
+ request_url: CaptureRuleEvaluationUrlSchema.optional(),
16927
+ status_code: external_exports.number().int().min(0).max(599).optional(),
16928
+ fingerprint: CaptureRuleFingerprintSchema.optional()
16929
+ });
16930
+
16931
+ // ../../packages/shared-types/src/capture-rule-suggestions.ts
16932
+ var CaptureRuleSuggestionConfidenceSchema = external_exports.enum(["high", "medium", "low"]);
16933
+ var CaptureRuleSuggestionSchema = external_exports.object({
16934
+ suggestion_id: external_exports.string().min(1).max(120),
16935
+ label: external_exports.string().min(1).max(200),
16936
+ recommended_action: CaptureRuleActionSchema,
16937
+ confidence: CaptureRuleSuggestionConfidenceSchema,
16938
+ reason: external_exports.string().min(1).max(500),
16939
+ requires_confirmation: external_exports.boolean(),
16940
+ rule: CaptureRuleCreateSchema
16941
+ });
16942
+ var CaptureRuleSuggestionsResponseSchema = external_exports.object({
16943
+ suggestions: external_exports.array(CaptureRuleSuggestionSchema),
16944
+ bundle_status: external_exports.enum(["ready", "pending", "failed"]).optional(),
16945
+ bundle_reason: external_exports.string().nullable().optional()
16946
+ });
16947
+ var CreateCaptureRuleFromSuggestionSchema = external_exports.object({
16948
+ suggestion_id: external_exports.string().min(1).max(120),
16949
+ name: external_exports.string().trim().min(1).max(120).optional(),
16950
+ description: external_exports.string().trim().max(500).nullable().optional(),
16951
+ enabled: external_exports.boolean().optional(),
16952
+ expires_at: external_exports.string().datetime().nullable().optional()
16953
+ });
16954
+
16551
16955
  // ../../packages/shared-types/src/improvement-settings.ts
16552
16956
  var ImprovementBundleSensitivityValues = [
16553
16957
  "high_confidence",
@@ -16704,6 +17108,18 @@ var DeviceInfoSchema = external_exports.object({
16704
17108
  connection_type: external_exports.string().nullable(),
16705
17109
  color_scheme_preference: external_exports.enum(["light", "dark", "no-preference"]).nullable()
16706
17110
  }).strict();
17111
+ var BrowserExceptionEventSchema = external_exports.object({
17112
+ kind: external_exports.enum(["window_error", "resource_error"]),
17113
+ message: external_exports.string().nullable(),
17114
+ file_name: external_exports.string().nullable(),
17115
+ line_number: external_exports.number().int().nonnegative().nullable(),
17116
+ column_number: external_exports.number().int().nonnegative().nullable(),
17117
+ target: external_exports.object({
17118
+ tag_name: external_exports.string().nullable(),
17119
+ source_url: external_exports.string().nullable()
17120
+ }).nullable(),
17121
+ opaque: external_exports.boolean()
17122
+ }).strict();
16707
17123
  var FrontendExceptionPayloadSchema = external_exports.object({
16708
17124
  name: external_exports.string().min(1),
16709
17125
  message: external_exports.string().min(1),
@@ -16715,6 +17131,7 @@ var FrontendExceptionPayloadSchema = external_exports.object({
16715
17131
  }),
16716
17132
  breadcrumbs: external_exports.array(FrontendExceptionBreadcrumbSchema).optional(),
16717
17133
  device: DeviceInfoSchema.nullable().optional(),
17134
+ browser_event: BrowserExceptionEventSchema.optional(),
16718
17135
  dom_context: external_exports.object({
16719
17136
  mode: external_exports.literal("lightweight"),
16720
17137
  html_excerpt: external_exports.string().min(1)
@@ -17123,6 +17540,25 @@ function buildSkill() {
17123
17540
  "",
17124
17541
  "Use DebugBundle before starting a fresh bug investigation.",
17125
17542
  "",
17543
+ "## Investigation Quickstart",
17544
+ "",
17545
+ "When the user reports a bug, runtime failure, production incident, regression, broken deploy, or unknown error, start here before reading arbitrary source files.",
17546
+ "",
17547
+ "1. Run `debugbundle doctor --json` to learn whether the project is local-only or connected and whether the local scaffold is healthy.",
17548
+ "2. List actionable failures with `debugbundle incidents --source local --status open --json` for local data, or `debugbundle incidents --source cloud --status open --json` when the issue came from a hosted environment.",
17549
+ "3. Inspect the chosen incident with `debugbundle inspect <incident-id> --source <local|cloud> --json` and `debugbundle explain <incident-id> --source <local|cloud> --json`.",
17550
+ "4. Fetch evidence before editing code: `debugbundle bundle <incident-id> --source <local|cloud> --json` and `debugbundle reproduce <incident-id> --source <local|cloud> --json`.",
17551
+ "5. If local SDK or relay events have landed but no bundle exists yet, run `debugbundle process --preset <minimal|balanced|investigative> --json` and then list incidents again.",
17552
+ "",
17553
+ "Key local paths:",
17554
+ "- `.debugbundle/profile.json` \u2014 project map, service paths, and validation state",
17555
+ "- `.debugbundle/local/connection.json` \u2014 local-only vs connected mode and environment delivery policy",
17556
+ "- `.debugbundle/local/events/` \u2014 raw local SDK, relay, ingest, and watch event batches",
17557
+ "- `.debugbundle/local/state.json` \u2014 local incident index, lifecycle state, and bundle paths",
17558
+ "- `.debugbundle/bundles/local/` \u2014 locally generated bundle artifacts",
17559
+ "- `.debugbundle/bundles/local/reproductions/` \u2014 local reproduction artifacts",
17560
+ "- `.debugbundle/bundles/cloud/` \u2014 explicitly fetched cloud artifact cache",
17561
+ "",
17126
17562
  "## Core Workflow",
17127
17563
  "",
17128
17564
  "1. Check DebugBundle incidents first to avoid re-investigating a known failure.",
@@ -17152,6 +17588,18 @@ function buildSkill() {
17152
17588
  "- Run `debugbundle validate --fix` to restore missing generated setup files without overwriting the profile.",
17153
17589
  "- Run `debugbundle process` after local events land in `.debugbundle/local/events/`.",
17154
17590
  "",
17591
+ "## Browser Capture and Relay Setup",
17592
+ "",
17593
+ "When the repository has a browser frontend, verify capture end to end instead of stopping at backend SDK setup.",
17594
+ "",
17595
+ "1. Add `@debugbundle/sdk-browser` to each browser app that should capture console, error, navigation, or request context.",
17596
+ "2. Initialize the browser SDK from the app entrypoint with the active environment and a browser relay endpoint.",
17597
+ "3. Add a backend relay endpoint at `/debugbundle/browser` using the server SDK relay helper when available.",
17598
+ "4. For same-origin apps, keep the browser endpoint as `/debugbundle/browser`.",
17599
+ "5. For split frontend/backend hosts, configure the browser endpoint to the API host relay URL and require explicit frontend origin allowlisting on the backend.",
17600
+ "6. Ensure auth and CSRF middleware allow the relay path while the relay still enforces origin, content type, body size, schema validation, and rate limits.",
17601
+ "7. Trigger a local browser smoke event, then run `debugbundle process --json` and confirm the incident or context event appears before marking setup complete.",
17602
+ "",
17155
17603
  "## References",
17156
17604
  "",
17157
17605
  "- CLI reference: `references/cli.md`",
@@ -17173,23 +17621,35 @@ function buildCliReference() {
17173
17621
  "## Setup",
17174
17622
  "",
17175
17623
  "- `debugbundle setup [--non-interactive] [--json]`",
17176
- "- `debugbundle doctor [--json]`",
17624
+ "- `debugbundle doctor [--check-relay] [--json]`",
17177
17625
  "- `debugbundle validate [--fix] [--json]`",
17178
17626
  "- `debugbundle ingest <file> --format <format> [--json]`",
17179
17627
  "- `debugbundle watch --log <file> --format <format> [--json]`",
17180
17628
  "- `debugbundle watch --cloud --log <file> --format <format> [--json]`",
17181
17629
  "- `debugbundle process [--preset <minimal|balanced|investigative>] [--json]`",
17630
+ "- `debugbundle clean [--events] [--bundles] [--all] [--older-than <Nd>] [--json]`",
17182
17631
  "",
17183
17632
  "## Investigation",
17184
17633
  "",
17185
- "- `debugbundle incidents`",
17186
- "- `debugbundle inspect <incident-id>`",
17187
- "- `debugbundle bundle <incident-id>`",
17188
- "- `debugbundle reproduce <incident-id>`",
17189
- "- `debugbundle resolve <incident-id>`",
17190
- "- `debugbundle reopen <incident-id>`",
17634
+ "- `debugbundle incidents [--source <local|cloud>] [--project-id <id>] [--environment <name>] [--service <name>] [--status <status>] [--severity <severity>] [--cursor <cursor>] [--limit <n>] [--json]`",
17635
+ "- `debugbundle inspect <incident-id> [--source <local|cloud>] [--json]`",
17636
+ "- `debugbundle explain <incident-id> [--source <local|cloud>] [--json]`",
17637
+ "- `debugbundle bundle <incident-id> [--source <local|cloud>] [--json]`",
17638
+ "- `debugbundle reproduce <incident-id> [--source <local|cloud>] [--json]`",
17639
+ "- `debugbundle resolve <incident-id> [--source <local|cloud>] [--json]`",
17640
+ "- `debugbundle reopen <incident-id> [--source <local|cloud>] [--json]`",
17191
17641
  "- `debugbundle analyze --type improvement --local`",
17192
17642
  "",
17643
+ "## Operational Paths",
17644
+ "",
17645
+ "- `.debugbundle/profile.json` \u2014 committed project map and agent validation state",
17646
+ "- `.debugbundle/local/connection.json` \u2014 committed delivery policy and cloud connection metadata",
17647
+ "- `.debugbundle/local/events/` \u2014 gitignored raw local event batches",
17648
+ "- `.debugbundle/local/state.json` \u2014 gitignored local incident index and lifecycle state",
17649
+ "- `.debugbundle/bundles/local/` \u2014 gitignored local bundle artifacts",
17650
+ "- `.debugbundle/bundles/local/reproductions/` \u2014 gitignored local reproduction artifacts",
17651
+ "- `.debugbundle/bundles/cloud/` \u2014 gitignored cache for explicitly fetched cloud artifacts",
17652
+ "",
17193
17653
  "## Incident Hygiene",
17194
17654
  "",
17195
17655
  "Resolve incidents after a fix is verified or after an intentional smoke, dogfood, or verification incident has served its purpose.",
@@ -17221,6 +17681,17 @@ function buildMcpReference() {
17221
17681
  "",
17222
17682
  "Use the same incident-first workflow through MCP when an agent is operating in connected mode.",
17223
17683
  "",
17684
+ "## Investigation Tools",
17685
+ "",
17686
+ "- `doctor` \u2014 validate local profile, connection config, auth state, and setup health.",
17687
+ "- `list_incidents` \u2014 list local, cloud, or connected combined incidents; pass `source`, `status`, `environment`, `service`, `severity`, `cursor`, and `limit` when needed.",
17688
+ "- `get_incident` \u2014 fetch incident metadata by incident id.",
17689
+ "- `get_incident_context` \u2014 fetch deterministic explanation context for triage.",
17690
+ "- `get_bundle` \u2014 fetch the full debug bundle before proposing a fix.",
17691
+ "- `get_reproduction` \u2014 fetch reproduction guidance before editing code.",
17692
+ "- `resolve_incident` / `reopen_incident` \u2014 update lifecycle state after validation.",
17693
+ "- `analyze` \u2014 run local agent-oriented analysis from local bundles and skill schemas.",
17694
+ "",
17224
17695
  "- Prefer bundle retrieval tools before reading raw repository files.",
17225
17696
  "- Use MCP bundle access when the current issue originated in production.",
17226
17697
  "- Resolve fixed or intentionally generated incidents with `resolve_incident` so open incidents stay actionable.",
@@ -17244,8 +17715,12 @@ function buildBundleSchemaReference() {
17244
17715
  "Focus on:",
17245
17716
  "- `summary` for the failure synopsis and recommended action",
17246
17717
  "- `service` and `environment` for routing to the right code path",
17718
+ "- `context.error`, `context.request`, `context.response`, `context.logs`, `context.frontend`, `context.runtime`, `context.git`, `context.dependencies`, and `context.probe_data` for supporting evidence",
17719
+ "- `reproduction` for confidence, commands, and manual steps",
17247
17720
  "- `links.reproduction` for the generated reproduction artifact",
17248
17721
  "- `metadata.source` for whether the bundle came from local or cloud data",
17722
+ "",
17723
+ "Treat the bundle as the source of truth for the failure report. Use repository reads to confirm and patch the implicated code paths, not to rediscover incident context from scratch.",
17249
17724
  ""
17250
17725
  ].join("\n");
17251
17726
  }
@@ -17256,10 +17731,14 @@ function buildProfileEnrichmentReference() {
17256
17731
  "The setup profile is generated from static analysis and must be reviewed before agents rely on it for architecture decisions.",
17257
17732
  "",
17258
17733
  "Checklist:",
17259
- "- verify service kinds, frameworks, and runtime assumptions",
17260
- "- add critical paths and ownership notes",
17261
- "- confirm build, test, and lint workflows",
17262
- "- update `debugbundle.validation_status` to `agent-validated` when complete",
17734
+ "- verify `project.primary_languages`, `project.package_managers`, and `project.deployment_targets`",
17735
+ "- verify each service `kind`, `runtime`, `framework`, `paths`, `owns_routes`, and `depends_on` value against the repository",
17736
+ "- add critical paths for ingestion, processing, retrieval, SDK capture, auth, billing, and any project-specific high-risk workflows",
17737
+ "- confirm `repo.generated_paths` and `repo.do_not_edit_paths` match the local scaffold",
17738
+ "- confirm build, test, lint, and install workflows in `developer_workflows`",
17739
+ "- for browser frontends, confirm `@debugbundle/sdk-browser` is initialized and a backend `/debugbundle/browser` relay is reachable",
17740
+ "- for split frontend/backend hosts, confirm the browser SDK uses the API relay URL and the backend allowlists the frontend origin",
17741
+ "- update `debugbundle.last_reviewed_at` and set `debugbundle.validation_status` to `agent-validated` when complete",
17263
17742
  ""
17264
17743
  ].join("\n");
17265
17744
  }
@@ -17341,6 +17820,22 @@ function buildSkillEvals() {
17341
17820
  "Resolve verified or intentionally generated incidents after the workflow is complete.",
17342
17821
  "Leave unresolved incidents open when the failure is still live or unverified."
17343
17822
  ]
17823
+ },
17824
+ {
17825
+ name: "artifact_path_discovery",
17826
+ prompt: "The user reports an unknown local runtime error. Confirm the skill tells the agent which DebugBundle paths and commands to inspect first.",
17827
+ expected_behavior: [
17828
+ "Run doctor and list local open incidents before broad source exploration.",
17829
+ "Use .debugbundle/local/state.json, .debugbundle/bundles/local/, and reproduction artifact paths as the local evidence map."
17830
+ ]
17831
+ },
17832
+ {
17833
+ name: "connected_incident_fetch",
17834
+ prompt: "The user says a production incident fired in the hosted DebugBundle project. Confirm the skill points the agent to the cloud retrieval path.",
17835
+ expected_behavior: [
17836
+ "List cloud open incidents or use MCP list_incidents with source cloud.",
17837
+ "Fetch inspect, context, bundle, and reproduction artifacts before editing code."
17838
+ ]
17344
17839
  }
17345
17840
  ]
17346
17841
  },
@@ -17436,10 +17931,37 @@ async function pathExists(path, stat) {
17436
17931
  }
17437
17932
  }
17438
17933
  function formatZodErrors(error) {
17439
- return error.issues.map((issue) => ({
17440
- path: issue.path.join("."),
17441
- message: issue.message
17442
- }));
17934
+ return error.issues.map((issue) => {
17935
+ const path = issue.path.join(".");
17936
+ if (path === "services" || path.startsWith("services.")) {
17937
+ return {
17938
+ path,
17939
+ message: issue.message,
17940
+ suggestion: "Add service entries with name, kind, runtime, framework, paths, owns_routes, and depends_on.",
17941
+ example: '[{"name":"api","kind":"backend","runtime":"Node.js","framework":"Fastify","paths":["apps/api"],"owns_routes":["POST /checkout"],"depends_on":["worker"]}]'
17942
+ };
17943
+ }
17944
+ if (path === "critical_paths" || path.startsWith("critical_paths.")) {
17945
+ return {
17946
+ path,
17947
+ message: issue.message,
17948
+ suggestion: "Use object entries so each critical path records its owner_service and review notes.",
17949
+ example: '[{"name":"checkout","owner_service":"api","notes":"Creates the order, charges the card, and enqueues fulfillment."}]'
17950
+ };
17951
+ }
17952
+ if (path === "developer_workflows" || path.startsWith("developer_workflows.")) {
17953
+ return {
17954
+ path,
17955
+ message: issue.message,
17956
+ suggestion: "Provide install, build, test, and lint as command strings so agents can run the standard repo workflows.",
17957
+ example: '{"install":"pnpm install","build":"pnpm build","test":"pnpm test","lint":"pnpm lint"}'
17958
+ };
17959
+ }
17960
+ return {
17961
+ path,
17962
+ message: issue.message
17963
+ };
17964
+ });
17443
17965
  }
17444
17966
  async function validateProfile(rootDirectory, dependencies = {}) {
17445
17967
  const readFile = dependencies.readFile ?? ((filePath) => (0, import_promises.readFile)(filePath, "utf8"));
@@ -17786,6 +18308,118 @@ function createCliHttpClient(input, dependencies) {
17786
18308
  };
17787
18309
  }
17788
18310
 
18311
+ // ../cli/src/capture-rule-commands.ts
18312
+ var CaptureRuleApiError = class extends Error {
18313
+ status;
18314
+ constructor(status, message) {
18315
+ super(message);
18316
+ this.name = "CaptureRuleApiError";
18317
+ this.status = status;
18318
+ }
18319
+ };
18320
+ function toApiError(status, body, fallback) {
18321
+ if (typeof body === "object" && body !== null && "error" in body && typeof body.error === "string") {
18322
+ return new CaptureRuleApiError(status, body.error);
18323
+ }
18324
+ return new CaptureRuleApiError(status, fallback);
18325
+ }
18326
+ function createCaptureRuleApi(httpClient) {
18327
+ return {
18328
+ async listCaptureRules(input) {
18329
+ const response = await httpClient.request({
18330
+ method: "GET",
18331
+ path: `/v1/projects/${encodeURIComponent(input.projectId)}/capture-rules`,
18332
+ bearerToken: input.bearerToken
18333
+ });
18334
+ if (response.status !== 200) {
18335
+ throw toApiError(response.status, response.body, "Failed to list capture rules.");
18336
+ }
18337
+ const parsed = CaptureRulesResponseSchema.safeParse(response.body);
18338
+ if (!parsed.success) {
18339
+ throw new CaptureRuleApiError(500, "Invalid capture rule list response.");
18340
+ }
18341
+ return parsed.data;
18342
+ },
18343
+ async createCaptureRule(input) {
18344
+ const response = await httpClient.request({
18345
+ method: "POST",
18346
+ path: `/v1/projects/${encodeURIComponent(input.projectId)}/capture-rules`,
18347
+ bearerToken: input.bearerToken,
18348
+ body: input.create
18349
+ });
18350
+ if (response.status !== 201) {
18351
+ throw toApiError(response.status, response.body, "Failed to create capture rule.");
18352
+ }
18353
+ const parsed = CaptureRuleResponseSchema.safeParse(response.body);
18354
+ if (!parsed.success) {
18355
+ throw new CaptureRuleApiError(500, "Invalid capture rule create response.");
18356
+ }
18357
+ return parsed.data;
18358
+ },
18359
+ async suggestCaptureRulesFromIncident(input) {
18360
+ const response = await httpClient.request({
18361
+ method: "POST",
18362
+ path: `/v1/incidents/${encodeURIComponent(input.incidentId)}/capture-rule-suggestion`,
18363
+ bearerToken: input.bearerToken
18364
+ });
18365
+ if (response.status !== 200) {
18366
+ throw toApiError(response.status, response.body, "Failed to suggest capture rules.");
18367
+ }
18368
+ const parsed = CaptureRuleSuggestionsResponseSchema.safeParse(response.body);
18369
+ if (!parsed.success) {
18370
+ throw new CaptureRuleApiError(500, "Invalid capture rule suggestion response.");
18371
+ }
18372
+ return parsed.data;
18373
+ },
18374
+ async createCaptureRuleFromIncidentSuggestion(input) {
18375
+ const response = await httpClient.request({
18376
+ method: "POST",
18377
+ path: `/v1/incidents/${encodeURIComponent(input.incidentId)}/capture-rules`,
18378
+ bearerToken: input.bearerToken,
18379
+ body: input.create
18380
+ });
18381
+ if (response.status !== 201) {
18382
+ throw toApiError(response.status, response.body, "Failed to create capture rule from suggestion.");
18383
+ }
18384
+ const parsed = CaptureRuleResponseSchema.safeParse(response.body);
18385
+ if (!parsed.success) {
18386
+ throw new CaptureRuleApiError(500, "Invalid capture rule create-from-suggestion response.");
18387
+ }
18388
+ return parsed.data;
18389
+ },
18390
+ async updateCaptureRule(input) {
18391
+ const response = await httpClient.request({
18392
+ method: "PATCH",
18393
+ path: `/v1/projects/${encodeURIComponent(input.projectId)}/capture-rules/${encodeURIComponent(input.ruleId)}`,
18394
+ bearerToken: input.bearerToken,
18395
+ body: input.update
18396
+ });
18397
+ if (response.status !== 200) {
18398
+ throw toApiError(response.status, response.body, "Failed to update capture rule.");
18399
+ }
18400
+ const parsed = CaptureRuleResponseSchema.safeParse(response.body);
18401
+ if (!parsed.success) {
18402
+ throw new CaptureRuleApiError(500, "Invalid capture rule update response.");
18403
+ }
18404
+ return parsed.data;
18405
+ },
18406
+ async deleteCaptureRule(input) {
18407
+ const response = await httpClient.request({
18408
+ method: "DELETE",
18409
+ path: `/v1/projects/${encodeURIComponent(input.projectId)}/capture-rules/${encodeURIComponent(input.ruleId)}`,
18410
+ bearerToken: input.bearerToken
18411
+ });
18412
+ if (response.status !== 200) {
18413
+ throw toApiError(response.status, response.body, "Failed to delete capture rule.");
18414
+ }
18415
+ if (typeof response.body !== "object" || response.body === null || !("success" in response.body) || response.body.success !== true) {
18416
+ throw new CaptureRuleApiError(500, "Invalid capture rule delete response.");
18417
+ }
18418
+ return { success: true };
18419
+ }
18420
+ };
18421
+ }
18422
+
17789
18423
  // ../cli/src/capture-policy-commands.ts
17790
18424
  var CapturePolicyApiError = class extends Error {
17791
18425
  status;
@@ -17795,7 +18429,7 @@ var CapturePolicyApiError = class extends Error {
17795
18429
  this.status = status;
17796
18430
  }
17797
18431
  };
17798
- function toApiError(status, body, fallback) {
18432
+ function toApiError2(status, body, fallback) {
17799
18433
  if (typeof body === "object" && body !== null && "error" in body && typeof body.error === "string") {
17800
18434
  return new CapturePolicyApiError(status, body.error);
17801
18435
  }
@@ -17810,7 +18444,7 @@ function createCapturePolicyApi(httpClient) {
17810
18444
  bearerToken: input.bearerToken
17811
18445
  });
17812
18446
  if (response.status !== 200) {
17813
- throw toApiError(response.status, response.body, "Failed to get capture policy.");
18447
+ throw toApiError2(response.status, response.body, "Failed to get capture policy.");
17814
18448
  }
17815
18449
  const parsed = CapturePolicyResponseSchema.safeParse(response.body);
17816
18450
  if (!parsed.success) {
@@ -17826,7 +18460,7 @@ function createCapturePolicyApi(httpClient) {
17826
18460
  body: input.update
17827
18461
  });
17828
18462
  if (response.status !== 200) {
17829
- throw toApiError(response.status, response.body, "Failed to update capture policy.");
18463
+ throw toApiError2(response.status, response.body, "Failed to update capture policy.");
17830
18464
  }
17831
18465
  const parsed = CapturePolicyResponseSchema.safeParse(response.body);
17832
18466
  if (!parsed.success) {
@@ -17846,7 +18480,7 @@ var ImprovementSettingsApiError = class extends Error {
17846
18480
  this.status = status;
17847
18481
  }
17848
18482
  };
17849
- function toApiError2(status, body, fallback) {
18483
+ function toApiError3(status, body, fallback) {
17850
18484
  if (typeof body === "object" && body !== null && "error" in body && typeof body.error === "string") {
17851
18485
  return new ImprovementSettingsApiError(status, body.error);
17852
18486
  }
@@ -17861,7 +18495,7 @@ function createImprovementSettingsApi(httpClient) {
17861
18495
  bearerToken: input.bearerToken
17862
18496
  });
17863
18497
  if (response.status !== 200) {
17864
- throw toApiError2(response.status, response.body, "Failed to get improvement settings.");
18498
+ throw toApiError3(response.status, response.body, "Failed to get improvement settings.");
17865
18499
  }
17866
18500
  const parsed = ImprovementSettingsResponseSchema.safeParse(response.body);
17867
18501
  if (!parsed.success) {
@@ -17877,7 +18511,7 @@ function createImprovementSettingsApi(httpClient) {
17877
18511
  body: input.update
17878
18512
  });
17879
18513
  if (response.status !== 200) {
17880
- throw toApiError2(response.status, response.body, "Failed to update improvement settings.");
18514
+ throw toApiError3(response.status, response.body, "Failed to update improvement settings.");
17881
18515
  }
17882
18516
  const parsed = ImprovementSettingsResponseSchema.safeParse(response.body);
17883
18517
  if (!parsed.success) {
@@ -17899,7 +18533,7 @@ var MemberApiError = class extends Error {
17899
18533
  this.code = code;
17900
18534
  }
17901
18535
  };
17902
- function toApiError3(status, body) {
18536
+ function toApiError4(status, body) {
17903
18537
  if (typeof body === "object" && body !== null && "error" in body && typeof body.error === "string") {
17904
18538
  return new MemberApiError(status, body.error);
17905
18539
  }
@@ -17914,7 +18548,7 @@ function createMemberApi(httpClient) {
17914
18548
  bearerToken: input.bearerToken
17915
18549
  });
17916
18550
  if (response.status !== 200) {
17917
- throw toApiError3(response.status, response.body);
18551
+ throw toApiError4(response.status, response.body);
17918
18552
  }
17919
18553
  return response.body;
17920
18554
  },
@@ -17925,7 +18559,7 @@ function createMemberApi(httpClient) {
17925
18559
  bearerToken: input.bearerToken
17926
18560
  });
17927
18561
  if (response.status !== 200) {
17928
- throw toApiError3(response.status, response.body);
18562
+ throw toApiError4(response.status, response.body);
17929
18563
  }
17930
18564
  return response.body;
17931
18565
  },
@@ -17937,7 +18571,7 @@ function createMemberApi(httpClient) {
17937
18571
  body: { email: input.email, role: input.role }
17938
18572
  });
17939
18573
  if (response.status !== 201) {
17940
- throw toApiError3(response.status, response.body);
18574
+ throw toApiError4(response.status, response.body);
17941
18575
  }
17942
18576
  return response.body;
17943
18577
  },
@@ -17948,7 +18582,7 @@ function createMemberApi(httpClient) {
17948
18582
  bearerToken: input.bearerToken
17949
18583
  });
17950
18584
  if (response.status !== 200) {
17951
- throw toApiError3(response.status, response.body);
18585
+ throw toApiError4(response.status, response.body);
17952
18586
  }
17953
18587
  return response.body;
17954
18588
  },
@@ -17960,7 +18594,7 @@ function createMemberApi(httpClient) {
17960
18594
  body: { role: input.role }
17961
18595
  });
17962
18596
  if (response.status !== 200) {
17963
- throw toApiError3(response.status, response.body);
18597
+ throw toApiError4(response.status, response.body);
17964
18598
  }
17965
18599
  return response.body;
17966
18600
  },
@@ -17971,7 +18605,7 @@ function createMemberApi(httpClient) {
17971
18605
  bearerToken: input.bearerToken
17972
18606
  });
17973
18607
  if (response.status !== 200) {
17974
- throw toApiError3(response.status, response.body);
18608
+ throw toApiError4(response.status, response.body);
17975
18609
  }
17976
18610
  return response.body;
17977
18611
  }
@@ -17989,7 +18623,7 @@ var ProbeApiError = class extends Error {
17989
18623
  this.code = code;
17990
18624
  }
17991
18625
  };
17992
- function toApiError4(status, body) {
18626
+ function toApiError5(status, body) {
17993
18627
  if (typeof body === "object" && body !== null && "error" in body && typeof body.error === "string") {
17994
18628
  return new ProbeApiError(status, body.error);
17995
18629
  }
@@ -18020,7 +18654,7 @@ function createProbeApi(httpClient) {
18020
18654
  body
18021
18655
  });
18022
18656
  if (response.status !== 201) {
18023
- throw toApiError4(response.status, response.body);
18657
+ throw toApiError5(response.status, response.body);
18024
18658
  }
18025
18659
  return response.body;
18026
18660
  },
@@ -18031,7 +18665,7 @@ function createProbeApi(httpClient) {
18031
18665
  bearerToken: input.bearerToken
18032
18666
  });
18033
18667
  if (response.status !== 200) {
18034
- throw toApiError4(response.status, response.body);
18668
+ throw toApiError5(response.status, response.body);
18035
18669
  }
18036
18670
  return response.body;
18037
18671
  },
@@ -18043,7 +18677,7 @@ function createProbeApi(httpClient) {
18043
18677
  body: { activation_id: input.activationId }
18044
18678
  });
18045
18679
  if (response.status !== 200) {
18046
- throw toApiError4(response.status, response.body);
18680
+ throw toApiError5(response.status, response.body);
18047
18681
  }
18048
18682
  return response.body;
18049
18683
  }
@@ -18166,6 +18800,15 @@ function inferMatchedFields(event) {
18166
18800
  if (event.top_frames.length > 0) {
18167
18801
  matchedFields.push("top_frames");
18168
18802
  }
18803
+ if (event.browser_event_kind != null) {
18804
+ matchedFields.push("browser_event_kind");
18805
+ }
18806
+ if (event.resource_host != null) {
18807
+ matchedFields.push("resource_host");
18808
+ }
18809
+ if (event.resource_path != null) {
18810
+ matchedFields.push("resource_path");
18811
+ }
18169
18812
  if (event.http_method !== null) {
18170
18813
  matchedFields.push("http_method");
18171
18814
  }
@@ -18226,6 +18869,39 @@ function selectTopFrames(stack, limit = 5) {
18226
18869
  const frames = normalizedStack.split("\n").map((line) => line.trim()).filter((line) => line.startsWith("at ")).filter((line) => FRAME_NOISE_PATTERNS.every((pattern) => !line.includes(pattern))).slice(0, limit);
18227
18870
  return frames;
18228
18871
  }
18872
+ function normalizeResourceIdentity(value) {
18873
+ if (value === null) {
18874
+ return { host: null, path: null };
18875
+ }
18876
+ const trimmed = value.trim();
18877
+ if (trimmed.length === 0) {
18878
+ return { host: null, path: null };
18879
+ }
18880
+ if (trimmed.startsWith("/")) {
18881
+ return {
18882
+ host: null,
18883
+ path: normalizeRoute(trimmed)
18884
+ };
18885
+ }
18886
+ try {
18887
+ const parsed = new URL(trimmed);
18888
+ if (parsed.protocol === "http:" || parsed.protocol === "https:") {
18889
+ return {
18890
+ host: parsed.hostname.length > 0 ? parsed.hostname.toLowerCase() : null,
18891
+ path: normalizeRoute(parsed.pathname)
18892
+ };
18893
+ }
18894
+ return {
18895
+ host: null,
18896
+ path: parsed.protocol.replace(/:$/, "")
18897
+ };
18898
+ } catch {
18899
+ return {
18900
+ host: null,
18901
+ path: normalizeRoute(trimmed) ?? trimmed
18902
+ };
18903
+ }
18904
+ }
18229
18905
  function stableJson(value) {
18230
18906
  if (value === null || typeof value !== "object") {
18231
18907
  return JSON.stringify(value);
@@ -18253,6 +18929,9 @@ function normalizeEvent(event) {
18253
18929
  http_method: event.payload.request.method,
18254
18930
  http_status: event.payload.response.status_code,
18255
18931
  top_frames: selectTopFrames(event.payload.stack),
18932
+ browser_event_kind: null,
18933
+ resource_host: null,
18934
+ resource_path: null,
18256
18935
  payload: redactedPayload
18257
18936
  };
18258
18937
  }
@@ -18266,6 +18945,9 @@ function normalizeEvent(event) {
18266
18945
  http_method: event.payload.method,
18267
18946
  http_status: event.payload.response_status,
18268
18947
  top_frames: [],
18948
+ browser_event_kind: null,
18949
+ resource_host: null,
18950
+ resource_path: null,
18269
18951
  payload: redactedPayload
18270
18952
  };
18271
18953
  }
@@ -18279,6 +18961,28 @@ function normalizeEvent(event) {
18279
18961
  http_method: null,
18280
18962
  http_status: null,
18281
18963
  top_frames: [],
18964
+ browser_event_kind: null,
18965
+ resource_host: null,
18966
+ resource_path: null,
18967
+ payload: redactedPayload
18968
+ };
18969
+ }
18970
+ if (event.event_type === "frontend_exception") {
18971
+ const browserEvent = event.payload.browser_event;
18972
+ const resourceIdentity = browserEvent?.kind === "resource_error" ? normalizeResourceIdentity(browserEvent.target?.source_url ?? browserEvent.file_name) : { host: null, path: null };
18973
+ const topFrames = browserEvent?.opaque === true ? [] : selectTopFrames(event.payload.stack);
18974
+ return {
18975
+ event_type: event.event_type,
18976
+ environment: event.service.environment,
18977
+ error_type: event.payload.name,
18978
+ normalized_message: normalizeMessage(event.payload.message),
18979
+ route_template: normalizeRoute(event.payload.route ?? null),
18980
+ http_method: null,
18981
+ http_status: null,
18982
+ top_frames: topFrames,
18983
+ browser_event_kind: browserEvent?.kind ?? null,
18984
+ resource_host: resourceIdentity.host,
18985
+ resource_path: resourceIdentity.path,
18282
18986
  payload: redactedPayload
18283
18987
  };
18284
18988
  }
@@ -18291,6 +18995,9 @@ function normalizeEvent(event) {
18291
18995
  http_method: null,
18292
18996
  http_status: null,
18293
18997
  top_frames: [],
18998
+ browser_event_kind: null,
18999
+ resource_host: null,
19000
+ resource_path: null,
18294
19001
  payload: redactedPayload
18295
19002
  };
18296
19003
  }
@@ -18300,6 +19007,9 @@ function fingerprint(event) {
18300
19007
  normalized_message: event.normalized_message,
18301
19008
  top_frames: event.top_frames,
18302
19009
  route_template: event.route_template,
19010
+ browser_event_kind: event.browser_event_kind,
19011
+ resource_host: event.resource_host,
19012
+ resource_path: event.resource_path,
18303
19013
  http_method: event.http_method,
18304
19014
  http_status: event.http_status,
18305
19015
  environment: event.environment
@@ -18354,18 +19064,18 @@ var ConnectionConfigSchema = external_exports.object({
18354
19064
  }).strict();
18355
19065
 
18356
19066
  // ../cli/src/doctor-command.ts
18357
- var ProfileSchema2 = external_exports.object({
18358
- debugbundle: external_exports.object({
18359
- last_reviewed_at: external_exports.string(),
18360
- validation_status: external_exports.enum(["static-analysis-only", "agent-validated"])
18361
- })
18362
- });
18363
19067
  var HealthResponseSchema = external_exports.object({
18364
19068
  status: external_exports.literal("ok")
18365
19069
  });
18366
19070
  var IncidentsProbeResponseSchema = external_exports.object({
18367
19071
  incidents: external_exports.array(external_exports.unknown())
18368
19072
  });
19073
+ var DoctorProfileSchema = external_exports.object({
19074
+ debugbundle: external_exports.object({
19075
+ last_reviewed_at: external_exports.string(),
19076
+ validation_status: external_exports.enum(["static-analysis-only", "agent-validated"])
19077
+ })
19078
+ });
18369
19079
  var PROFILE_STALENESS_THRESHOLD_DAYS = 30;
18370
19080
  var LOCAL_RELAY_SPOOL_DIRECTORY_PATH = ".debugbundle/local/browser-relay-spool";
18371
19081
  var RELAY_SPOOL_DELIVERED_MARKER_SUFFIX = ".delivered";
@@ -18508,6 +19218,12 @@ async function buildFileCheck(rootDirectory, name, filePath, stat) {
18508
19218
  message: exists ? `Found ${filePath}` : `Missing ${filePath}`
18509
19219
  };
18510
19220
  }
19221
+ function formatZodErrors2(error) {
19222
+ return error.issues.map((issue) => ({
19223
+ path: issue.path.join("."),
19224
+ message: issue.message
19225
+ }));
19226
+ }
18511
19227
  async function loadProfile(rootDirectory, dependencies) {
18512
19228
  const profilePath = (0, import_node_path4.join)(rootDirectory, PROFILE_FILE_PATH);
18513
19229
  if (!await pathExists3(profilePath, dependencies.stat)) {
@@ -18517,29 +19233,46 @@ async function loadProfile(rootDirectory, dependencies) {
18517
19233
  status: "missing",
18518
19234
  message: `Missing ${PROFILE_FILE_PATH}`
18519
19235
  },
18520
- profile: null
19236
+ profile: null,
19237
+ validationErrors: []
18521
19238
  };
18522
19239
  }
19240
+ let parsedJson;
18523
19241
  try {
18524
- const parsedProfile = ProfileSchema2.parse(JSON.parse(await dependencies.readFile(profilePath)));
19242
+ parsedJson = JSON.parse(await dependencies.readFile(profilePath));
19243
+ } catch {
18525
19244
  return {
18526
19245
  check: {
18527
19246
  name: "profile",
18528
- status: "ok",
18529
- message: `Found ${PROFILE_FILE_PATH}`
19247
+ status: "error",
19248
+ message: `Invalid ${PROFILE_FILE_PATH}`
18530
19249
  },
18531
- profile: parsedProfile
19250
+ profile: null,
19251
+ validationErrors: []
18532
19252
  };
18533
- } catch {
19253
+ }
19254
+ const parsedDoctorProfile = DoctorProfileSchema.safeParse(parsedJson);
19255
+ if (!parsedDoctorProfile.success) {
18534
19256
  return {
18535
19257
  check: {
18536
19258
  name: "profile",
18537
19259
  status: "error",
18538
19260
  message: `Invalid ${PROFILE_FILE_PATH}`
18539
19261
  },
18540
- profile: null
19262
+ profile: null,
19263
+ validationErrors: formatZodErrors2(parsedDoctorProfile.error)
18541
19264
  };
18542
19265
  }
19266
+ const parsedFullProfile = ProfileSchema.safeParse(parsedJson);
19267
+ return {
19268
+ check: {
19269
+ name: "profile",
19270
+ status: "ok",
19271
+ message: `Found ${PROFILE_FILE_PATH}`
19272
+ },
19273
+ profile: parsedDoctorProfile.data,
19274
+ validationErrors: parsedFullProfile.success ? [] : formatZodErrors2(parsedFullProfile.error)
19275
+ };
18543
19276
  }
18544
19277
  async function loadConnection(rootDirectory, dependencies) {
18545
19278
  const connectionPath = (0, import_node_path4.join)(rootDirectory, CONNECTION_FILE_PATH);
@@ -18591,7 +19324,20 @@ function buildProjectModeCheck(connection) {
18591
19324
  message: `Project mode is ${connection.mode}.`
18592
19325
  };
18593
19326
  }
18594
- function buildProfileValidationCheck(profile) {
19327
+ function formatProfileValidationError(errors) {
19328
+ const firstError = errors[0];
19329
+ const path = firstError.path.length === 0 ? PROFILE_FILE_PATH : firstError.path;
19330
+ const totalErrors = errors.length === 1 ? "" : ` (${errors.length} total errors)`;
19331
+ return `Profile schema validation failed at ${path}: ${firstError.message}${totalErrors}.`;
19332
+ }
19333
+ function buildProfileValidationCheck(profile, validationErrors) {
19334
+ if (validationErrors.length > 0) {
19335
+ return {
19336
+ name: "profile-validation",
19337
+ status: "error",
19338
+ message: formatProfileValidationError(validationErrors)
19339
+ };
19340
+ }
18595
19341
  if (profile === null) {
18596
19342
  return {
18597
19343
  name: "profile-validation",
@@ -18840,7 +19586,7 @@ async function doctorCommand(input, dependencies = {}) {
18840
19586
  const stat = dependencies.stat ?? import_promises4.stat;
18841
19587
  const rootDirectory = cwd();
18842
19588
  const currentTime = now();
18843
- const { check: profileCheck, profile } = await loadProfile(rootDirectory, { readFile, stat });
19589
+ const { check: profileCheck, profile, validationErrors } = await loadProfile(rootDirectory, { readFile, stat });
18844
19590
  const { check: connectionCheck, connection } = await loadConnection(rootDirectory, { readFile, stat });
18845
19591
  const { check: authCheck, authState } = await buildAuthCheck(input, readAuthStateImpl);
18846
19592
  const connectedApiCheck = await buildConnectedApiCheck({
@@ -18855,7 +19601,7 @@ async function doctorCommand(input, dependencies = {}) {
18855
19601
  authCheck,
18856
19602
  buildProjectModeCheck(connection),
18857
19603
  ...connectedApiCheck === null ? [] : [connectedApiCheck],
18858
- buildProfileValidationCheck(profile),
19604
+ buildProfileValidationCheck(profile, validationErrors),
18859
19605
  buildProfileFreshnessCheck(profile, currentTime),
18860
19606
  ...input.checkRelay === true ? [await buildRelaySpoolCheck(rootDirectory, currentTime, { readdir, stat })] : []
18861
19607
  ];
@@ -18867,6 +19613,7 @@ async function doctorCommand(input, dependencies = {}) {
18867
19613
  }
18868
19614
 
18869
19615
  // ../cli/src/verify-command.ts
19616
+ var import_node_crypto6 = require("node:crypto");
18870
19617
  var import_promises7 = require("node:fs/promises");
18871
19618
  var import_node_path9 = require("node:path");
18872
19619
 
@@ -19167,6 +19914,868 @@ var import_ioredis4 = __toESM(require_built3(), 1);
19167
19914
 
19168
19915
  // ../../packages/storage/src/schema-migrations.ts
19169
19916
  var import_node_crypto3 = require("node:crypto");
19917
+
19918
+ // ../../packages/storage/src/migrations.ts
19919
+ var STORAGE_BOOTSTRAP_STATEMENTS = [
19920
+ `
19921
+ CREATE TABLE users (
19922
+ id uuid PRIMARY KEY,
19923
+ email text NOT NULL UNIQUE,
19924
+ accepted_terms_at timestamptz,
19925
+ created_at timestamptz NOT NULL DEFAULT now(),
19926
+ updated_at timestamptz NOT NULL DEFAULT now(),
19927
+ email_verified_at timestamptz,
19928
+ avatar_source text,
19929
+ avatar_object_key text,
19930
+ avatar_content_type text,
19931
+ avatar_updated_at timestamptz
19932
+ )
19933
+ `,
19934
+ `
19935
+ CREATE TABLE organizations (
19936
+ id uuid PRIMARY KEY,
19937
+ name text NOT NULL,
19938
+ slug text NOT NULL UNIQUE,
19939
+ created_at timestamptz NOT NULL DEFAULT now(),
19940
+ updated_at timestamptz NOT NULL DEFAULT now(),
19941
+ suspended_at timestamptz,
19942
+ plan text NOT NULL DEFAULT 'free',
19943
+ stripe_customer_id text,
19944
+ additional_capacity_units integer NOT NULL DEFAULT 0,
19945
+ stripe_subscription_id text,
19946
+ billing_state text,
19947
+ billing_period_ends_at timestamptz,
19948
+ last_billing_sync_at timestamptz,
19949
+ last_billing_event_id text,
19950
+ billing_period_starts_at timestamptz
19951
+ )
19952
+ `,
19953
+ `
19954
+ CREATE UNIQUE INDEX organizations_stripe_customer_id_key
19955
+ ON organizations (stripe_customer_id)
19956
+ WHERE stripe_customer_id IS NOT NULL
19957
+ `,
19958
+ `
19959
+ CREATE TABLE projects (
19960
+ id uuid PRIMARY KEY,
19961
+ organization_id uuid NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
19962
+ owner_user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE,
19963
+ name text NOT NULL,
19964
+ slug text NOT NULL,
19965
+ environment_default text NOT NULL DEFAULT 'production',
19966
+ automated_improvement_bundles_enabled boolean NOT NULL DEFAULT true,
19967
+ improvement_bundle_sensitivity text NOT NULL DEFAULT 'high_confidence'
19968
+ CHECK (improvement_bundle_sensitivity IN ('high_confidence', 'balanced', 'verbose')),
19969
+ created_at timestamptz NOT NULL DEFAULT now(),
19970
+ updated_at timestamptz NOT NULL DEFAULT now(),
19971
+ plan text NOT NULL DEFAULT 'free'
19972
+ )
19973
+ `,
19974
+ `
19975
+ CREATE UNIQUE INDEX projects_organization_id_slug_key
19976
+ ON projects (organization_id, slug)
19977
+ `,
19978
+ `
19979
+ CREATE TABLE services (
19980
+ id uuid PRIMARY KEY,
19981
+ project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
19982
+ name text NOT NULL,
19983
+ runtime text,
19984
+ framework text,
19985
+ environment text NOT NULL DEFAULT 'production',
19986
+ created_at timestamptz NOT NULL DEFAULT now(),
19987
+ updated_at timestamptz NOT NULL DEFAULT now(),
19988
+ UNIQUE (project_id, name, environment)
19989
+ )
19990
+ `,
19991
+ `
19992
+ CREATE TABLE project_tokens (
19993
+ id uuid PRIMARY KEY,
19994
+ project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
19995
+ token_hash text UNIQUE NOT NULL,
19996
+ label text NOT NULL,
19997
+ allowed_origins jsonb NOT NULL DEFAULT '[]'::jsonb,
19998
+ last_used_at timestamptz,
19999
+ created_at timestamptz NOT NULL DEFAULT now(),
20000
+ revoked_at timestamptz,
20001
+ expires_at timestamptz
20002
+ )
20003
+ `,
20004
+ `
20005
+ CREATE TABLE member_tokens (
20006
+ id uuid PRIMARY KEY,
20007
+ user_id uuid NOT NULL,
20008
+ organization_id uuid NOT NULL,
20009
+ token_hash text UNIQUE NOT NULL,
20010
+ label text NOT NULL,
20011
+ last_used_at timestamptz,
20012
+ created_at timestamptz NOT NULL DEFAULT now(),
20013
+ revoked_at timestamptz,
20014
+ expires_at timestamptz
20015
+ )
20016
+ `,
20017
+ `
20018
+ CREATE INDEX member_tokens_org_idx
20019
+ ON member_tokens (organization_id)
20020
+ `,
20021
+ `
20022
+ CREATE TABLE audit_logs (
20023
+ id uuid PRIMARY KEY,
20024
+ organization_id uuid,
20025
+ actor_user_id uuid,
20026
+ actor_type text NOT NULL,
20027
+ action text NOT NULL,
20028
+ target_type text NOT NULL,
20029
+ target_id text,
20030
+ status text NOT NULL,
20031
+ ip_address text,
20032
+ metadata jsonb NOT NULL DEFAULT '{}'::jsonb,
20033
+ occurred_at timestamptz NOT NULL,
20034
+ created_at timestamptz NOT NULL DEFAULT now()
20035
+ )
20036
+ `,
20037
+ `
20038
+ CREATE INDEX audit_logs_organization_occurred_at_idx
20039
+ ON audit_logs (organization_id, occurred_at DESC, created_at DESC)
20040
+ `,
20041
+ `
20042
+ CREATE INDEX audit_logs_action_occurred_at_idx
20043
+ ON audit_logs (action, occurred_at DESC, created_at DESC)
20044
+ `,
20045
+ `
20046
+ CREATE TABLE organization_members (
20047
+ id uuid PRIMARY KEY,
20048
+ organization_id uuid NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
20049
+ user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE,
20050
+ role text NOT NULL DEFAULT 'member',
20051
+ suspended_at timestamptz,
20052
+ created_at timestamptz NOT NULL DEFAULT now(),
20053
+ UNIQUE (organization_id, user_id)
20054
+ )
20055
+ `,
20056
+ `
20057
+ CREATE INDEX organization_members_org_idx
20058
+ ON organization_members (organization_id)
20059
+ `,
20060
+ `
20061
+ CREATE INDEX organization_members_user_idx
20062
+ ON organization_members (user_id)
20063
+ `,
20064
+ `
20065
+ CREATE TABLE sessions (
20066
+ id uuid PRIMARY KEY,
20067
+ user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE,
20068
+ organization_id uuid NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
20069
+ session_token_hash text UNIQUE NOT NULL,
20070
+ created_at timestamptz NOT NULL DEFAULT now(),
20071
+ expires_at timestamptz NOT NULL,
20072
+ revoked_at timestamptz
20073
+ )
20074
+ `,
20075
+ `
20076
+ CREATE INDEX sessions_token_hash_idx
20077
+ ON sessions (session_token_hash)
20078
+ `,
20079
+ `
20080
+ CREATE INDEX sessions_user_org_idx
20081
+ ON sessions (user_id, organization_id)
20082
+ `,
20083
+ `
20084
+ CREATE TABLE email_auth_challenges (
20085
+ id uuid PRIMARY KEY,
20086
+ email text NOT NULL,
20087
+ code_hash text NOT NULL,
20088
+ accepted_terms_at timestamptz,
20089
+ created_at timestamptz NOT NULL DEFAULT now(),
20090
+ expires_at timestamptz NOT NULL,
20091
+ used_at timestamptz
20092
+ )
20093
+ `,
20094
+ `
20095
+ CREATE INDEX email_auth_challenges_email_idx
20096
+ ON email_auth_challenges (lower(email), created_at DESC)
20097
+ `,
20098
+ `
20099
+ CREATE INDEX email_auth_challenges_code_hash_idx
20100
+ ON email_auth_challenges (code_hash)
20101
+ `,
20102
+ `
20103
+ CREATE TABLE github_device_authorizations (
20104
+ id uuid PRIMARY KEY,
20105
+ device_code text NOT NULL UNIQUE,
20106
+ user_code text NOT NULL,
20107
+ verification_uri text NOT NULL,
20108
+ interval_seconds integer NOT NULL,
20109
+ expires_at timestamptz NOT NULL,
20110
+ accepted_terms_at timestamptz,
20111
+ created_at timestamptz NOT NULL DEFAULT now(),
20112
+ completed_at timestamptz,
20113
+ claimed_at timestamptz,
20114
+ terminal_error text,
20115
+ user_id uuid REFERENCES users(id) ON DELETE SET NULL,
20116
+ organization_id uuid REFERENCES organizations(id) ON DELETE SET NULL
20117
+ )
20118
+ `,
20119
+ `
20120
+ CREATE INDEX github_device_authorizations_user_code_idx
20121
+ ON github_device_authorizations (user_code, created_at DESC)
20122
+ `,
20123
+ `
20124
+ CREATE INDEX github_device_authorizations_expires_at_idx
20125
+ ON github_device_authorizations (expires_at)
20126
+ `,
20127
+ `
20128
+ CREATE TABLE project_members (
20129
+ id uuid PRIMARY KEY,
20130
+ project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
20131
+ user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE,
20132
+ role text NOT NULL,
20133
+ invited_by_user_id uuid REFERENCES users(id) ON DELETE SET NULL,
20134
+ created_at timestamptz NOT NULL DEFAULT now(),
20135
+ updated_at timestamptz NOT NULL DEFAULT now(),
20136
+ UNIQUE (project_id, user_id)
20137
+ )
20138
+ `,
20139
+ `
20140
+ CREATE INDEX project_members_project_id_idx
20141
+ ON project_members (project_id, created_at DESC)
20142
+ `,
20143
+ `
20144
+ CREATE INDEX project_members_user_id_idx
20145
+ ON project_members (user_id, created_at DESC)
20146
+ `,
20147
+ `
20148
+ CREATE TABLE project_invites (
20149
+ id uuid PRIMARY KEY,
20150
+ project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
20151
+ email text NOT NULL,
20152
+ role text NOT NULL,
20153
+ invited_by_user_id uuid REFERENCES users(id) ON DELETE SET NULL,
20154
+ invite_token_hash text NOT NULL,
20155
+ accepted_at timestamptz,
20156
+ canceled_at timestamptz,
20157
+ expires_at timestamptz NOT NULL,
20158
+ created_at timestamptz NOT NULL DEFAULT now()
20159
+ )
20160
+ `,
20161
+ `
20162
+ CREATE INDEX project_invites_project_id_idx
20163
+ ON project_invites (project_id, created_at DESC)
20164
+ `,
20165
+ `
20166
+ CREATE UNIQUE INDEX project_invites_pending_project_email_key
20167
+ ON project_invites (project_id, lower(email))
20168
+ WHERE accepted_at IS NULL AND canceled_at IS NULL
20169
+ `,
20170
+ `
20171
+ CREATE UNIQUE INDEX project_invites_invite_token_hash_key
20172
+ ON project_invites (invite_token_hash)
20173
+ `,
20174
+ `
20175
+ CREATE TABLE oauth_identities (
20176
+ id uuid PRIMARY KEY,
20177
+ provider text NOT NULL,
20178
+ provider_user_id text NOT NULL,
20179
+ user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE,
20180
+ created_at timestamptz NOT NULL DEFAULT now(),
20181
+ updated_at timestamptz NOT NULL DEFAULT now(),
20182
+ UNIQUE (provider, provider_user_id)
20183
+ )
20184
+ `,
20185
+ `
20186
+ CREATE INDEX oauth_identities_user_id_idx
20187
+ ON oauth_identities (user_id, provider)
20188
+ `,
20189
+ `
20190
+ CREATE TABLE probe_activations (
20191
+ id uuid PRIMARY KEY,
20192
+ project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
20193
+ created_by_member_id uuid NOT NULL,
20194
+ label_pattern text NOT NULL,
20195
+ service text NOT NULL DEFAULT '*',
20196
+ environment text NOT NULL DEFAULT '*',
20197
+ trigger_expires_at timestamptz NOT NULL,
20198
+ expires_at timestamptz NOT NULL,
20199
+ deactivated_at timestamptz,
20200
+ created_at timestamptz NOT NULL DEFAULT now()
20201
+ )
20202
+ `,
20203
+ `
20204
+ CREATE INDEX probe_activations_project_active_idx
20205
+ ON probe_activations (project_id, expires_at DESC)
20206
+ WHERE deactivated_at IS NULL
20207
+ `,
20208
+ `
20209
+ CREATE TABLE capture_policies (
20210
+ project_id uuid PRIMARY KEY REFERENCES projects(id) ON DELETE CASCADE,
20211
+ preset text NOT NULL DEFAULT 'minimal',
20212
+ capture_logs text,
20213
+ capture_request_events text,
20214
+ capture_breadcrumbs text,
20215
+ capture_probe_events text,
20216
+ immediate_client_error_statuses jsonb,
20217
+ updated_at timestamptz NOT NULL DEFAULT now()
20218
+ )
20219
+ `,
20220
+ `
20221
+ CREATE TABLE capture_rules (
20222
+ id uuid PRIMARY KEY,
20223
+ project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
20224
+ name text NOT NULL,
20225
+ description text,
20226
+ enabled boolean NOT NULL DEFAULT true,
20227
+ action text NOT NULL,
20228
+ matcher jsonb NOT NULL,
20229
+ sample_rate double precision,
20230
+ sample_event_class text,
20231
+ created_by_user_id uuid REFERENCES users(id) ON DELETE SET NULL,
20232
+ created_from_incident_id text,
20233
+ created_from_event_id text,
20234
+ expires_at timestamptz,
20235
+ hit_count bigint NOT NULL DEFAULT 0,
20236
+ last_matched_at timestamptz,
20237
+ created_at timestamptz NOT NULL DEFAULT now(),
20238
+ updated_at timestamptz NOT NULL DEFAULT now()
20239
+ )
20240
+ `,
20241
+ `
20242
+ CREATE INDEX capture_rules_project_enabled_idx
20243
+ ON capture_rules (project_id, enabled)
20244
+ `,
20245
+ `
20246
+ CREATE INDEX capture_rules_project_updated_idx
20247
+ ON capture_rules (project_id, updated_at DESC)
20248
+ `,
20249
+ `
20250
+ CREATE TABLE deployments (
20251
+ id uuid PRIMARY KEY,
20252
+ project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
20253
+ service_id uuid REFERENCES services(id) ON DELETE SET NULL,
20254
+ environment text NOT NULL,
20255
+ source_event_id uuid UNIQUE NOT NULL,
20256
+ commit_sha text,
20257
+ version text,
20258
+ branch text,
20259
+ deployed_at timestamptz NOT NULL,
20260
+ metadata jsonb NOT NULL DEFAULT '{}'::jsonb,
20261
+ created_at timestamptz NOT NULL DEFAULT now(),
20262
+ updated_at timestamptz NOT NULL DEFAULT now()
20263
+ )
20264
+ `,
20265
+ `
20266
+ CREATE INDEX deployments_project_service_env_deployed_idx
20267
+ ON deployments (project_id, service_id, environment, deployed_at DESC)
20268
+ `,
20269
+ `
20270
+ CREATE TABLE incidents (
20271
+ id uuid PRIMARY KEY,
20272
+ project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
20273
+ service_id uuid REFERENCES services(id) ON DELETE SET NULL,
20274
+ environment text NOT NULL DEFAULT 'production',
20275
+ fingerprint text NOT NULL,
20276
+ fingerprint_version text NOT NULL DEFAULT 'v1',
20277
+ title text NOT NULL,
20278
+ severity text NOT NULL,
20279
+ status text NOT NULL DEFAULT 'open',
20280
+ first_seen_at timestamptz NOT NULL,
20281
+ last_seen_at timestamptz NOT NULL,
20282
+ occurrence_count integer NOT NULL DEFAULT 1,
20283
+ matched_fields text[],
20284
+ created_at timestamptz NOT NULL DEFAULT now(),
20285
+ updated_at timestamptz NOT NULL DEFAULT now(),
20286
+ regressed_at timestamptz,
20287
+ spike_detected_at timestamptz,
20288
+ frequency_occurrences_1m integer,
20289
+ frequency_occurrences_5m integer,
20290
+ frequency_occurrences_1h integer,
20291
+ frequency_occurrences_24h integer,
20292
+ frequency_baseline_1h_per_5m double precision,
20293
+ frequency_spike_ratio_5m_to_1h double precision,
20294
+ frequency_has_sufficient_baseline boolean,
20295
+ frequency_is_spiking boolean,
20296
+ frequency_snapshot_at timestamptz,
20297
+ latest_deployment_id uuid REFERENCES deployments(id) ON DELETE SET NULL,
20298
+ bundle_generation_number integer NOT NULL DEFAULT 0,
20299
+ bundle_created_at timestamptz,
20300
+ bundle_updated_at timestamptz,
20301
+ bundle_source_event_id uuid,
20302
+ bundle_source_occurred_at timestamptz,
20303
+ bundle_trigger text,
20304
+ bundle_failure_reason text,
20305
+ resolved_at timestamptz,
20306
+ resolved_by_member_id uuid REFERENCES users(id) ON DELETE SET NULL,
20307
+ UNIQUE (project_id, environment, service_id, fingerprint)
20308
+ )
20309
+ `,
20310
+ `
20311
+ CREATE TABLE processed_events (
20312
+ event_id uuid PRIMARY KEY,
20313
+ project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
20314
+ event_type text NOT NULL,
20315
+ fingerprint text NOT NULL,
20316
+ normalized_message text NOT NULL,
20317
+ processed_at timestamptz NOT NULL DEFAULT now()
20318
+ )
20319
+ `,
20320
+ `
20321
+ CREATE TABLE improvement_opportunities (
20322
+ id uuid PRIMARY KEY,
20323
+ project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
20324
+ service_id uuid REFERENCES services(id) ON DELETE SET NULL,
20325
+ service_name text NOT NULL,
20326
+ environment text NOT NULL DEFAULT 'production',
20327
+ kind text NOT NULL,
20328
+ status text NOT NULL DEFAULT 'open',
20329
+ severity text NOT NULL,
20330
+ confidence numeric NOT NULL,
20331
+ fingerprint text NOT NULL,
20332
+ title text NOT NULL,
20333
+ summary text NOT NULL,
20334
+ occurrence_count integer NOT NULL DEFAULT 1,
20335
+ evidence jsonb NOT NULL,
20336
+ first_detected_at timestamptz NOT NULL,
20337
+ last_detected_at timestamptz NOT NULL,
20338
+ last_source_event_id uuid,
20339
+ related_incident_ids uuid[] NOT NULL DEFAULT '{}',
20340
+ bundle_generation_number integer NOT NULL DEFAULT 0,
20341
+ bundle_created_at timestamptz,
20342
+ bundle_updated_at timestamptz,
20343
+ bundle_source_event_id uuid,
20344
+ bundle_failure_reason text,
20345
+ resolved_at timestamptz,
20346
+ resolved_by_user_id uuid REFERENCES users(id) ON DELETE SET NULL,
20347
+ snoozed_until timestamptz,
20348
+ created_at timestamptz NOT NULL DEFAULT now(),
20349
+ updated_at timestamptz NOT NULL DEFAULT now(),
20350
+ UNIQUE (project_id, fingerprint)
20351
+ )
20352
+ `,
20353
+ `
20354
+ CREATE INDEX improvement_opportunities_project_status_detected_idx
20355
+ ON improvement_opportunities (project_id, status, last_detected_at DESC)
20356
+ `,
20357
+ `
20358
+ CREATE INDEX improvement_opportunities_project_kind_detected_idx
20359
+ ON improvement_opportunities (project_id, kind, last_detected_at DESC)
20360
+ `,
20361
+ `
20362
+ CREATE INDEX improvement_opportunities_project_service_env_idx
20363
+ ON improvement_opportunities (project_id, service_id, environment)
20364
+ `,
20365
+ `
20366
+ CREATE TABLE improvement_opportunity_events (
20367
+ improvement_opportunity_id uuid NOT NULL REFERENCES improvement_opportunities(id) ON DELETE CASCADE,
20368
+ event_id uuid NOT NULL,
20369
+ event_type text NOT NULL,
20370
+ occurred_at timestamptz NOT NULL,
20371
+ PRIMARY KEY (improvement_opportunity_id, event_id)
20372
+ )
20373
+ `,
20374
+ `
20375
+ CREATE INDEX improvement_opportunity_events_detected_idx
20376
+ ON improvement_opportunity_events (improvement_opportunity_id, occurred_at DESC, event_id DESC)
20377
+ `,
20378
+ `
20379
+ CREATE TABLE bundle_generations (
20380
+ id uuid PRIMARY KEY,
20381
+ project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
20382
+ incident_id uuid REFERENCES incidents(id) ON DELETE CASCADE,
20383
+ improvement_opportunity_id uuid REFERENCES improvement_opportunities(id) ON DELETE CASCADE,
20384
+ bundle_type text NOT NULL,
20385
+ generation_number integer NOT NULL,
20386
+ source_event_id uuid NOT NULL,
20387
+ source_occurred_at timestamptz NOT NULL,
20388
+ trigger text NOT NULL,
20389
+ created_at timestamptz NOT NULL,
20390
+ updated_at timestamptz NOT NULL,
20391
+ CHECK (
20392
+ (incident_id IS NOT NULL AND improvement_opportunity_id IS NULL AND bundle_type = 'failure')
20393
+ OR (incident_id IS NULL AND improvement_opportunity_id IS NOT NULL AND bundle_type = 'improvement')
20394
+ )
20395
+ )
20396
+ `,
20397
+ `
20398
+ CREATE UNIQUE INDEX bundle_generations_incident_source_idx
20399
+ ON bundle_generations (incident_id, source_event_id)
20400
+ WHERE incident_id IS NOT NULL
20401
+ `,
20402
+ `
20403
+ CREATE UNIQUE INDEX bundle_generations_improvement_source_idx
20404
+ ON bundle_generations (improvement_opportunity_id, source_event_id)
20405
+ WHERE improvement_opportunity_id IS NOT NULL
20406
+ `,
20407
+ `
20408
+ CREATE INDEX bundle_generations_project_created_idx
20409
+ ON bundle_generations (project_id, created_at DESC, bundle_type)
20410
+ `,
20411
+ `
20412
+ CREATE INDEX bundle_generations_incident_generation_idx
20413
+ ON bundle_generations (incident_id, generation_number DESC)
20414
+ `,
20415
+ `
20416
+ CREATE INDEX bundle_generations_improvement_generation_idx
20417
+ ON bundle_generations (improvement_opportunity_id, generation_number DESC)
20418
+ WHERE improvement_opportunity_id IS NOT NULL
20419
+ `,
20420
+ `
20421
+ CREATE TABLE incident_events (
20422
+ incident_id uuid NOT NULL REFERENCES incidents(id) ON DELETE CASCADE,
20423
+ event_id uuid NOT NULL,
20424
+ event_type text NOT NULL,
20425
+ event_class text NOT NULL DEFAULT 'context_signal',
20426
+ occurred_at timestamptz NOT NULL,
20427
+ is_sampled boolean NOT NULL DEFAULT false,
20428
+ level text,
20429
+ retain_first boolean NOT NULL DEFAULT false,
20430
+ retain_latest boolean NOT NULL DEFAULT false,
20431
+ retain_after_deploy boolean NOT NULL DEFAULT false,
20432
+ retain_highest_severity boolean NOT NULL DEFAULT false,
20433
+ retain_deploy_metadata boolean NOT NULL DEFAULT false,
20434
+ severity_rank integer NOT NULL DEFAULT 0,
20435
+ PRIMARY KEY (incident_id, event_id)
20436
+ )
20437
+ `,
20438
+ `
20439
+ CREATE INDEX incident_events_incident_occurred_event_idx
20440
+ ON incident_events (incident_id, occurred_at DESC, event_id DESC)
20441
+ `,
20442
+ `
20443
+ CREATE INDEX incident_events_incident_level_occurred_event_idx
20444
+ ON incident_events (incident_id, level, occurred_at DESC, event_id DESC)
20445
+ `,
20446
+ `
20447
+ CREATE INDEX incident_events_incident_sampled_idx
20448
+ ON incident_events (incident_id, is_sampled, occurred_at ASC, event_id ASC)
20449
+ `,
20450
+ `
20451
+ CREATE TABLE weekly_report_channels (
20452
+ id uuid PRIMARY KEY,
20453
+ project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
20454
+ channel text NOT NULL,
20455
+ config jsonb NOT NULL DEFAULT '{}'::jsonb,
20456
+ schedule_day_of_week text NOT NULL,
20457
+ schedule_hour_of_day integer NOT NULL,
20458
+ schedule_timezone text NOT NULL,
20459
+ is_enabled boolean NOT NULL DEFAULT true,
20460
+ created_at timestamptz NOT NULL DEFAULT now(),
20461
+ updated_at timestamptz NOT NULL DEFAULT now()
20462
+ )
20463
+ `,
20464
+ `
20465
+ CREATE INDEX weekly_report_channels_project_created_idx
20466
+ ON weekly_report_channels (project_id, created_at ASC)
20467
+ `,
20468
+ `
20469
+ CREATE UNIQUE INDEX weekly_report_channels_project_email_unique_idx
20470
+ ON weekly_report_channels (project_id)
20471
+ WHERE channel = 'email'
20472
+ `,
20473
+ `
20474
+ CREATE TABLE weekly_report_deliveries (
20475
+ id uuid PRIMARY KEY,
20476
+ project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
20477
+ weekly_report_channel_id uuid REFERENCES weekly_report_channels(id) ON DELETE CASCADE,
20478
+ window_start timestamptz NOT NULL,
20479
+ window_end timestamptz NOT NULL,
20480
+ channel text NOT NULL,
20481
+ status text NOT NULL,
20482
+ last_error text,
20483
+ delivered_at timestamptz,
20484
+ created_at timestamptz NOT NULL DEFAULT now(),
20485
+ updated_at timestamptz NOT NULL DEFAULT now()
20486
+ )
20487
+ `,
20488
+ `
20489
+ CREATE INDEX weekly_report_deliveries_project_window_idx
20490
+ ON weekly_report_deliveries (project_id, window_end DESC, channel)
20491
+ `,
20492
+ `
20493
+ CREATE UNIQUE INDEX weekly_report_deliveries_channel_window_idx
20494
+ ON weekly_report_deliveries (weekly_report_channel_id, window_start, window_end)
20495
+ WHERE weekly_report_channel_id IS NOT NULL
20496
+ `,
20497
+ `
20498
+ CREATE INDEX weekly_report_deliveries_channel_idx
20499
+ ON weekly_report_deliveries (weekly_report_channel_id, window_end DESC)
20500
+ `,
20501
+ `
20502
+ CREATE TABLE alert_rules (
20503
+ id uuid PRIMARY KEY,
20504
+ project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
20505
+ created_by_user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE,
20506
+ service_id uuid REFERENCES services(id) ON DELETE CASCADE,
20507
+ channel text NOT NULL,
20508
+ condition_type text NOT NULL,
20509
+ severity_min text,
20510
+ config jsonb NOT NULL DEFAULT '{}'::jsonb,
20511
+ is_enabled boolean NOT NULL DEFAULT true,
20512
+ created_at timestamptz NOT NULL DEFAULT now(),
20513
+ updated_at timestamptz NOT NULL DEFAULT now()
20514
+ )
20515
+ `,
20516
+ `
20517
+ CREATE INDEX alert_rules_project_enabled_idx
20518
+ ON alert_rules (project_id, is_enabled)
20519
+ `,
20520
+ `
20521
+ CREATE TABLE slack_destinations (
20522
+ id uuid PRIMARY KEY,
20523
+ organization_id uuid NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
20524
+ slack_team_id text NOT NULL,
20525
+ slack_team_name text,
20526
+ slack_channel_id text NOT NULL,
20527
+ slack_channel_name text,
20528
+ webhook_url_ciphertext text NOT NULL,
20529
+ installed_by_member_id uuid REFERENCES users(id) ON DELETE SET NULL,
20530
+ is_active boolean NOT NULL DEFAULT true,
20531
+ created_at timestamptz NOT NULL DEFAULT now(),
20532
+ updated_at timestamptz NOT NULL DEFAULT now(),
20533
+ UNIQUE (organization_id, slack_team_id, slack_channel_id)
20534
+ )
20535
+ `,
20536
+ `
20537
+ CREATE INDEX slack_destinations_org_active_idx
20538
+ ON slack_destinations (organization_id, is_active, created_at)
20539
+ `,
20540
+ `
20541
+ CREATE TABLE alert_deliveries (
20542
+ id uuid PRIMARY KEY,
20543
+ alert_id uuid NOT NULL REFERENCES alert_rules(id) ON DELETE CASCADE,
20544
+ project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
20545
+ incident_id uuid NOT NULL REFERENCES incidents(id) ON DELETE CASCADE,
20546
+ condition_type text NOT NULL,
20547
+ dedupe_key text NOT NULL,
20548
+ channel text NOT NULL,
20549
+ status text NOT NULL,
20550
+ payload jsonb NOT NULL,
20551
+ last_error text,
20552
+ delivered_at timestamptz,
20553
+ created_at timestamptz NOT NULL DEFAULT now(),
20554
+ updated_at timestamptz NOT NULL DEFAULT now(),
20555
+ UNIQUE (alert_id, incident_id, dedupe_key)
20556
+ )
20557
+ `,
20558
+ `
20559
+ CREATE INDEX alert_deliveries_project_status_idx
20560
+ ON alert_deliveries (project_id, status, created_at DESC)
20561
+ `,
20562
+ `
20563
+ CREATE TABLE alert_email_digests (
20564
+ id uuid PRIMARY KEY,
20565
+ project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
20566
+ recipient text NOT NULL,
20567
+ status text NOT NULL,
20568
+ next_attempt_at timestamptz,
20569
+ claimed_at timestamptz,
20570
+ last_error text,
20571
+ delivered_at timestamptz,
20572
+ created_at timestamptz NOT NULL DEFAULT now(),
20573
+ updated_at timestamptz NOT NULL DEFAULT now()
20574
+ )
20575
+ `,
20576
+ `
20577
+ CREATE UNIQUE INDEX alert_email_digests_project_recipient_pending_idx
20578
+ ON alert_email_digests (project_id, recipient)
20579
+ WHERE status = 'pending' AND claimed_at IS NULL
20580
+ `,
20581
+ `
20582
+ CREATE INDEX alert_email_digests_status_next_attempt_idx
20583
+ ON alert_email_digests (status, next_attempt_at)
20584
+ `,
20585
+ `
20586
+ CREATE TABLE alert_email_digest_items (
20587
+ id uuid PRIMARY KEY,
20588
+ digest_id uuid NOT NULL REFERENCES alert_email_digests(id) ON DELETE CASCADE,
20589
+ alert_id uuid NOT NULL REFERENCES alert_rules(id) ON DELETE CASCADE,
20590
+ project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
20591
+ incident_id uuid NOT NULL REFERENCES incidents(id) ON DELETE CASCADE,
20592
+ condition_type text NOT NULL,
20593
+ dedupe_key text NOT NULL,
20594
+ payload jsonb NOT NULL,
20595
+ created_at timestamptz NOT NULL DEFAULT now(),
20596
+ UNIQUE (alert_id, incident_id, dedupe_key)
20597
+ )
20598
+ `,
20599
+ `
20600
+ CREATE INDEX alert_email_digest_items_digest_created_idx
20601
+ ON alert_email_digest_items (digest_id, created_at ASC)
20602
+ `,
20603
+ `
20604
+ CREATE TABLE agent_webhooks (
20605
+ id uuid PRIMARY KEY,
20606
+ project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
20607
+ created_by_user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE,
20608
+ url text NOT NULL,
20609
+ secret_hash text NOT NULL,
20610
+ events text[] NOT NULL,
20611
+ filters jsonb NOT NULL DEFAULT '{}'::jsonb,
20612
+ is_enabled boolean NOT NULL DEFAULT true,
20613
+ created_at timestamptz NOT NULL DEFAULT now(),
20614
+ updated_at timestamptz NOT NULL DEFAULT now()
20615
+ )
20616
+ `,
20617
+ `
20618
+ CREATE INDEX agent_webhooks_project_enabled_idx
20619
+ ON agent_webhooks (project_id, is_enabled)
20620
+ `,
20621
+ `
20622
+ CREATE TABLE webhook_deliveries (
20623
+ id uuid PRIMARY KEY,
20624
+ webhook_id uuid REFERENCES agent_webhooks(id) ON DELETE CASCADE,
20625
+ project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
20626
+ incident_id uuid REFERENCES incidents(id) ON DELETE CASCADE,
20627
+ event_type text NOT NULL,
20628
+ target_url text NOT NULL,
20629
+ signing_secret text NOT NULL,
20630
+ status text NOT NULL DEFAULT 'pending',
20631
+ attempt_count integer NOT NULL DEFAULT 0,
20632
+ occurred_at timestamptz NOT NULL,
20633
+ next_attempt_at timestamptz,
20634
+ last_response_code integer,
20635
+ last_attempted_at timestamptz,
20636
+ last_error text,
20637
+ payload jsonb NOT NULL,
20638
+ created_at timestamptz NOT NULL DEFAULT now(),
20639
+ updated_at timestamptz NOT NULL DEFAULT now()
20640
+ )
20641
+ `,
20642
+ `
20643
+ CREATE INDEX webhook_deliveries_status_next_attempt_idx
20644
+ ON webhook_deliveries (status, next_attempt_at)
20645
+ `,
20646
+ `
20647
+ CREATE TABLE processed_billing_events (
20648
+ event_id text PRIMARY KEY,
20649
+ event_type text NOT NULL,
20650
+ organization_id uuid,
20651
+ processed_at timestamptz NOT NULL DEFAULT now()
20652
+ )
20653
+ `,
20654
+ `
20655
+ CREATE TABLE github_installations (
20656
+ id uuid PRIMARY KEY,
20657
+ organization_id uuid NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
20658
+ installation_id bigint NOT NULL UNIQUE,
20659
+ account_login text NOT NULL,
20660
+ account_type text NOT NULL CHECK (account_type IN ('Organization', 'User')),
20661
+ status text NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'suspended', 'removed')),
20662
+ created_at timestamptz NOT NULL DEFAULT now(),
20663
+ updated_at timestamptz NOT NULL DEFAULT now(),
20664
+ UNIQUE (organization_id)
20665
+ )
20666
+ `,
20667
+ `
20668
+ CREATE INDEX github_installations_status_idx
20669
+ ON github_installations (status)
20670
+ `,
20671
+ `
20672
+ CREATE TABLE project_github_repos (
20673
+ id uuid PRIMARY KEY,
20674
+ project_id uuid NOT NULL UNIQUE REFERENCES projects(id) ON DELETE CASCADE,
20675
+ installation_id uuid NOT NULL REFERENCES github_installations(id) ON DELETE CASCADE,
20676
+ repo_owner text NOT NULL,
20677
+ repo_name text NOT NULL,
20678
+ default_branch text NOT NULL DEFAULT 'main',
20679
+ created_at timestamptz NOT NULL DEFAULT now(),
20680
+ updated_at timestamptz NOT NULL DEFAULT now()
20681
+ )
20682
+ `,
20683
+ `
20684
+ CREATE TABLE github_dispatch_rules (
20685
+ id uuid PRIMARY KEY,
20686
+ project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
20687
+ created_by_user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE,
20688
+ name text NOT NULL,
20689
+ enabled boolean NOT NULL DEFAULT true,
20690
+ event_types text[] NOT NULL,
20691
+ environments text[],
20692
+ services text[],
20693
+ severity_min text CHECK (severity_min IN ('low', 'medium', 'high', 'critical')),
20694
+ bundle_type text CHECK (bundle_type IN ('failure', 'improvement')),
20695
+ incident_status text NOT NULL DEFAULT 'new_or_reopened'
20696
+ CHECK (incident_status IN ('new_only', 'reopened_only', 'new_or_reopened')),
20697
+ cooldown_seconds integer NOT NULL DEFAULT 300,
20698
+ created_at timestamptz NOT NULL DEFAULT now(),
20699
+ updated_at timestamptz NOT NULL DEFAULT now()
20700
+ )
20701
+ `,
20702
+ `
20703
+ CREATE INDEX github_dispatch_rules_project_enabled_idx
20704
+ ON github_dispatch_rules (project_id, enabled)
20705
+ `,
20706
+ `
20707
+ CREATE TABLE github_dispatch_deliveries (
20708
+ id uuid PRIMARY KEY,
20709
+ rule_id uuid NOT NULL REFERENCES github_dispatch_rules(id) ON DELETE CASCADE,
20710
+ project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
20711
+ incident_id uuid REFERENCES incidents(id) ON DELETE CASCADE,
20712
+ improvement_opportunity_id uuid REFERENCES improvement_opportunities(id) ON DELETE CASCADE,
20713
+ target_fingerprint text NOT NULL,
20714
+ installation_id bigint NOT NULL,
20715
+ repo_owner text NOT NULL,
20716
+ repo_name text NOT NULL,
20717
+ status text NOT NULL DEFAULT 'pending'
20718
+ CHECK (status IN ('pending', 'retrying', 'delivered', 'failed', 'skipped')),
20719
+ attempt_count integer NOT NULL DEFAULT 0,
20720
+ next_attempt_at timestamptz,
20721
+ last_attempt_at timestamptz,
20722
+ last_error text,
20723
+ github_status_code integer,
20724
+ dispatch_payload jsonb NOT NULL DEFAULT '{}'::jsonb,
20725
+ created_at timestamptz NOT NULL DEFAULT now(),
20726
+ updated_at timestamptz NOT NULL DEFAULT now(),
20727
+ dedupe_key text NOT NULL,
20728
+ CHECK (
20729
+ (incident_id IS NOT NULL AND improvement_opportunity_id IS NULL)
20730
+ OR (incident_id IS NULL AND improvement_opportunity_id IS NOT NULL)
20731
+ )
20732
+ )
20733
+ `,
20734
+ `
20735
+ CREATE INDEX github_dispatch_deliveries_status_next_attempt_idx
20736
+ ON github_dispatch_deliveries (status, next_attempt_at)
20737
+ `,
20738
+ `
20739
+ CREATE UNIQUE INDEX github_dispatch_deliveries_rule_dedupe_key_idx
20740
+ ON github_dispatch_deliveries (rule_id, target_fingerprint, dedupe_key)
20741
+ `,
20742
+ `
20743
+ CREATE TABLE org_usage_counters (
20744
+ organization_id uuid NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
20745
+ period_starts_at timestamptz NOT NULL,
20746
+ raw_ingested_events integer NOT NULL DEFAULT 0,
20747
+ updated_at timestamptz NOT NULL DEFAULT now(),
20748
+ PRIMARY KEY (organization_id, period_starts_at)
20749
+ )
20750
+ `,
20751
+ `
20752
+ CREATE TABLE operational_email_deliveries (
20753
+ id uuid PRIMARY KEY,
20754
+ organization_id uuid NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
20755
+ project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
20756
+ kind text NOT NULL
20757
+ CHECK (kind IN ('webhook_auto_disabled', 'allowance_warning_80', 'allowance_limit_reached', 'retention_rotation_notice')),
20758
+ dedupe_key text NOT NULL,
20759
+ payload jsonb NOT NULL DEFAULT '{}'::jsonb,
20760
+ status text NOT NULL DEFAULT 'pending'
20761
+ CHECK (status IN ('pending', 'retrying', 'delivered', 'failed')),
20762
+ attempt_count integer NOT NULL DEFAULT 0,
20763
+ next_attempt_at timestamptz,
20764
+ last_error text,
20765
+ delivered_at timestamptz,
20766
+ created_at timestamptz NOT NULL DEFAULT now(),
20767
+ updated_at timestamptz NOT NULL DEFAULT now(),
20768
+ UNIQUE (organization_id, kind, dedupe_key)
20769
+ )
20770
+ `,
20771
+ `
20772
+ CREATE INDEX operational_email_deliveries_status_next_attempt_idx
20773
+ ON operational_email_deliveries (status, next_attempt_at, created_at)
20774
+ `
20775
+ ];
20776
+ var STORAGE_BOOTSTRAP_SQL = STORAGE_BOOTSTRAP_STATEMENTS.join(";\n\n");
20777
+
20778
+ // ../../packages/storage/src/schema-migrations.ts
19170
20779
  function computeMigrationChecksum(input) {
19171
20780
  return (0, import_node_crypto3.createHash)("sha256").update(JSON.stringify(input)).digest("hex");
19172
20781
  }
@@ -19430,6 +21039,234 @@ var STORAGE_SCHEMA_MIGRATIONS = [
19430
21039
  )
19431
21040
  `
19432
21041
  ]
21042
+ }),
21043
+ defineStorageSchemaMigration({
21044
+ id: "202605180004_add_operational_email_deliveries",
21045
+ description: "Add durable operational email delivery queue with retries and dedupe.",
21046
+ statements: [
21047
+ `
21048
+ CREATE TABLE IF NOT EXISTS operational_email_deliveries (
21049
+ id uuid PRIMARY KEY,
21050
+ organization_id uuid NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
21051
+ project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
21052
+ kind text NOT NULL,
21053
+ dedupe_key text NOT NULL,
21054
+ payload jsonb NOT NULL DEFAULT '{}'::jsonb,
21055
+ status text NOT NULL DEFAULT 'pending',
21056
+ attempt_count integer NOT NULL DEFAULT 0,
21057
+ next_attempt_at timestamptz,
21058
+ last_error text,
21059
+ delivered_at timestamptz,
21060
+ created_at timestamptz NOT NULL DEFAULT now(),
21061
+ updated_at timestamptz NOT NULL DEFAULT now(),
21062
+ UNIQUE (organization_id, kind, dedupe_key)
21063
+ )
21064
+ `,
21065
+ `
21066
+ ALTER TABLE operational_email_deliveries
21067
+ DROP CONSTRAINT IF EXISTS operational_email_deliveries_kind_check
21068
+ `,
21069
+ `
21070
+ ALTER TABLE operational_email_deliveries
21071
+ ADD CONSTRAINT operational_email_deliveries_kind_check
21072
+ CHECK (kind IN ('webhook_auto_disabled', 'allowance_warning_80', 'allowance_limit_reached', 'retention_rotation_notice'))
21073
+ `,
21074
+ `
21075
+ ALTER TABLE operational_email_deliveries
21076
+ DROP CONSTRAINT IF EXISTS operational_email_deliveries_status_check
21077
+ `,
21078
+ `
21079
+ ALTER TABLE operational_email_deliveries
21080
+ ADD CONSTRAINT operational_email_deliveries_status_check
21081
+ CHECK (status IN ('pending', 'retrying', 'delivered', 'failed'))
21082
+ `,
21083
+ `
21084
+ CREATE INDEX IF NOT EXISTS operational_email_deliveries_status_next_attempt_idx
21085
+ ON operational_email_deliveries (status, next_attempt_at, created_at)
21086
+ `
21087
+ ]
21088
+ }),
21089
+ defineStorageSchemaMigration({
21090
+ id: "202605180005_add_github_improvement_dispatch_targets",
21091
+ description: "Allow GitHub dispatch deliveries to target either incidents or hosted improvements.",
21092
+ statements: [
21093
+ "ALTER TABLE github_dispatch_deliveries ALTER COLUMN incident_id DROP NOT NULL",
21094
+ "ALTER TABLE github_dispatch_deliveries RENAME COLUMN incident_fingerprint TO target_fingerprint",
21095
+ "ALTER TABLE github_dispatch_deliveries ADD COLUMN IF NOT EXISTS improvement_opportunity_id uuid REFERENCES improvement_opportunities(id) ON DELETE CASCADE",
21096
+ "DROP INDEX IF EXISTS github_dispatch_deliveries_rule_dedupe_key_idx",
21097
+ `
21098
+ CREATE UNIQUE INDEX IF NOT EXISTS github_dispatch_deliveries_rule_dedupe_key_idx
21099
+ ON github_dispatch_deliveries (rule_id, target_fingerprint, dedupe_key)
21100
+ `,
21101
+ "ALTER TABLE github_dispatch_deliveries DROP CONSTRAINT IF EXISTS github_dispatch_deliveries_check",
21102
+ `
21103
+ ALTER TABLE github_dispatch_deliveries
21104
+ ADD CONSTRAINT github_dispatch_deliveries_check CHECK (
21105
+ (incident_id IS NOT NULL AND improvement_opportunity_id IS NULL)
21106
+ OR (incident_id IS NULL AND improvement_opportunity_id IS NOT NULL)
21107
+ )
21108
+ `
21109
+ ]
21110
+ }),
21111
+ defineStorageSchemaMigration({
21112
+ id: "202605180006_add_missing_bundle_and_creator_columns",
21113
+ description: "Backfill creator ownership columns and incident bundle tracking columns that existed only in bootstrap schema.",
21114
+ statements: [
21115
+ "ALTER TABLE incidents ADD COLUMN IF NOT EXISTS bundle_generation_number integer NOT NULL DEFAULT 0",
21116
+ "ALTER TABLE incidents ADD COLUMN IF NOT EXISTS bundle_created_at timestamptz",
21117
+ "ALTER TABLE incidents ADD COLUMN IF NOT EXISTS bundle_updated_at timestamptz",
21118
+ "ALTER TABLE incidents ADD COLUMN IF NOT EXISTS bundle_source_event_id uuid",
21119
+ "ALTER TABLE incidents ADD COLUMN IF NOT EXISTS bundle_source_occurred_at timestamptz",
21120
+ "ALTER TABLE incidents ADD COLUMN IF NOT EXISTS bundle_trigger text",
21121
+ "ALTER TABLE incidents ADD COLUMN IF NOT EXISTS bundle_failure_reason text",
21122
+ "ALTER TABLE alert_rules ADD COLUMN IF NOT EXISTS created_by_user_id uuid",
21123
+ `
21124
+ UPDATE alert_rules ar
21125
+ SET created_by_user_id = p.owner_user_id
21126
+ FROM projects p
21127
+ WHERE ar.project_id = p.id
21128
+ AND ar.created_by_user_id IS NULL
21129
+ `,
21130
+ "ALTER TABLE alert_rules DROP CONSTRAINT IF EXISTS alert_rules_created_by_user_id_fkey",
21131
+ `
21132
+ ALTER TABLE alert_rules
21133
+ ADD CONSTRAINT alert_rules_created_by_user_id_fkey
21134
+ FOREIGN KEY (created_by_user_id) REFERENCES users(id) ON DELETE CASCADE
21135
+ `,
21136
+ "ALTER TABLE alert_rules ALTER COLUMN created_by_user_id SET NOT NULL",
21137
+ "ALTER TABLE agent_webhooks ADD COLUMN IF NOT EXISTS created_by_user_id uuid",
21138
+ `
21139
+ UPDATE agent_webhooks aw
21140
+ SET created_by_user_id = p.owner_user_id
21141
+ FROM projects p
21142
+ WHERE aw.project_id = p.id
21143
+ AND aw.created_by_user_id IS NULL
21144
+ `,
21145
+ "ALTER TABLE agent_webhooks DROP CONSTRAINT IF EXISTS agent_webhooks_created_by_user_id_fkey",
21146
+ `
21147
+ ALTER TABLE agent_webhooks
21148
+ ADD CONSTRAINT agent_webhooks_created_by_user_id_fkey
21149
+ FOREIGN KEY (created_by_user_id) REFERENCES users(id) ON DELETE CASCADE
21150
+ `,
21151
+ "ALTER TABLE agent_webhooks ALTER COLUMN created_by_user_id SET NOT NULL",
21152
+ "ALTER TABLE github_dispatch_rules ADD COLUMN IF NOT EXISTS created_by_user_id uuid",
21153
+ `
21154
+ UPDATE github_dispatch_rules gdr
21155
+ SET created_by_user_id = p.owner_user_id
21156
+ FROM projects p
21157
+ WHERE gdr.project_id = p.id
21158
+ AND gdr.created_by_user_id IS NULL
21159
+ `,
21160
+ "ALTER TABLE github_dispatch_rules DROP CONSTRAINT IF EXISTS github_dispatch_rules_created_by_user_id_fkey",
21161
+ `
21162
+ ALTER TABLE github_dispatch_rules
21163
+ ADD CONSTRAINT github_dispatch_rules_created_by_user_id_fkey
21164
+ FOREIGN KEY (created_by_user_id) REFERENCES users(id) ON DELETE CASCADE
21165
+ `,
21166
+ "ALTER TABLE github_dispatch_rules ALTER COLUMN created_by_user_id SET NOT NULL"
21167
+ ]
21168
+ }),
21169
+ defineStorageSchemaMigration({
21170
+ id: "202605200001_limit_weekly_report_email_channel_per_project",
21171
+ description: "Keep weekly email reports singular per project.",
21172
+ statements: [
21173
+ `
21174
+ DELETE FROM weekly_report_channels
21175
+ WHERE id IN (
21176
+ SELECT id
21177
+ FROM (
21178
+ SELECT
21179
+ id,
21180
+ row_number() OVER (PARTITION BY project_id ORDER BY created_at ASC, id ASC) AS row_number
21181
+ FROM weekly_report_channels
21182
+ WHERE channel = 'email'
21183
+ ) ranked
21184
+ WHERE ranked.row_number > 1
21185
+ )
21186
+ `,
21187
+ `
21188
+ CREATE UNIQUE INDEX IF NOT EXISTS weekly_report_channels_project_email_unique_idx
21189
+ ON weekly_report_channels (project_id)
21190
+ WHERE channel = 'email'
21191
+ `
21192
+ ]
21193
+ }),
21194
+ defineStorageSchemaMigration({
21195
+ id: "202605220001_add_project_token_allowed_origins",
21196
+ description: "Add optional browser-origin allowlists to project ingestion tokens.",
21197
+ statements: [
21198
+ "ALTER TABLE project_tokens ADD COLUMN IF NOT EXISTS allowed_origins jsonb NOT NULL DEFAULT '[]'::jsonb"
21199
+ ]
21200
+ }),
21201
+ defineStorageSchemaMigration({
21202
+ id: "202605260001_fix_weekly_report_delivery_conflict_index",
21203
+ description: "Ensure weekly report delivery dedupe rows and partial unique index exist for conflict claims.",
21204
+ statements: [
21205
+ `
21206
+ DELETE FROM weekly_report_deliveries
21207
+ WHERE id IN (
21208
+ SELECT id
21209
+ FROM (
21210
+ SELECT
21211
+ id,
21212
+ row_number() OVER (
21213
+ PARTITION BY weekly_report_channel_id, window_start, window_end
21214
+ ORDER BY created_at ASC, id ASC
21215
+ ) AS row_number
21216
+ FROM weekly_report_deliveries
21217
+ WHERE weekly_report_channel_id IS NOT NULL
21218
+ ) ranked
21219
+ WHERE ranked.row_number > 1
21220
+ )
21221
+ `,
21222
+ `
21223
+ CREATE UNIQUE INDEX IF NOT EXISTS weekly_report_deliveries_channel_window_idx
21224
+ ON weekly_report_deliveries (weekly_report_channel_id, window_start, window_end)
21225
+ WHERE weekly_report_channel_id IS NOT NULL
21226
+ `
21227
+ ]
21228
+ }),
21229
+ defineStorageSchemaMigration({
21230
+ id: "202605260001_set_high_confidence_as_project_improvement_default",
21231
+ description: "Make high-confidence the default hosted improvement sensitivity for new projects.",
21232
+ statements: [
21233
+ "ALTER TABLE projects ALTER COLUMN improvement_bundle_sensitivity SET DEFAULT 'high_confidence'"
21234
+ ]
21235
+ }),
21236
+ defineStorageSchemaMigration({
21237
+ id: "202605260002_add_capture_rules",
21238
+ description: "Add persisted project capture rules for dynamic demote/sample/drop handling.",
21239
+ statements: [
21240
+ `
21241
+ CREATE TABLE IF NOT EXISTS capture_rules (
21242
+ id uuid PRIMARY KEY,
21243
+ project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
21244
+ name text NOT NULL,
21245
+ description text,
21246
+ enabled boolean NOT NULL DEFAULT true,
21247
+ action text NOT NULL,
21248
+ matcher jsonb NOT NULL,
21249
+ sample_rate double precision,
21250
+ sample_event_class text,
21251
+ created_by_user_id uuid REFERENCES users(id) ON DELETE SET NULL,
21252
+ created_from_incident_id text,
21253
+ created_from_event_id text,
21254
+ expires_at timestamptz,
21255
+ hit_count bigint NOT NULL DEFAULT 0,
21256
+ last_matched_at timestamptz,
21257
+ created_at timestamptz NOT NULL DEFAULT now(),
21258
+ updated_at timestamptz NOT NULL DEFAULT now()
21259
+ )
21260
+ `,
21261
+ `
21262
+ CREATE INDEX IF NOT EXISTS capture_rules_project_enabled_idx
21263
+ ON capture_rules (project_id, enabled)
21264
+ `,
21265
+ `
21266
+ CREATE INDEX IF NOT EXISTS capture_rules_project_updated_idx
21267
+ ON capture_rules (project_id, updated_at DESC)
21268
+ `
21269
+ ]
19433
21270
  })
19434
21271
  ];
19435
21272
 
@@ -19552,11 +21389,17 @@ function normalizeRouteTemplate(path) {
19552
21389
  const normalizedSegments = pathWithoutQueryOrFragment.split("/").filter((segment) => segment.length > 0).map((segment) => isDynamicRouteSegment2(segment) ? "{param}" : segment);
19553
21390
  return normalizedSegments.length === 0 ? "/" : `/${normalizedSegments.join("/")}`;
19554
21391
  }
21392
+ function isBrowserSdkFallbackFrame(frame) {
21393
+ return frame.includes("debugbundle-browser-sdk") && frame.includes("onError");
21394
+ }
19555
21395
  function deriveFirstApplicationFrame(errorContext) {
19556
21396
  const firstFrame = errorContext?.top_frames[0];
19557
21397
  if (firstFrame === void 0) {
19558
21398
  return null;
19559
21399
  }
21400
+ if (isBrowserSdkFallbackFrame(firstFrame)) {
21401
+ return null;
21402
+ }
19560
21403
  const match = /at\s+(.*?)\s+\((.*?):(\d+):(\d+)\)$/.exec(firstFrame) ?? /at\s+(.*?):(\d+):(\d+)$/.exec(firstFrame);
19561
21404
  if (match === null) {
19562
21405
  return {
@@ -19578,6 +21421,20 @@ function deriveFirstApplicationFrame(errorContext) {
19578
21421
  line: Number(match[2])
19579
21422
  };
19580
21423
  }
21424
+ function getPrimaryBrowserExceptionEvent(envelopes, primarySignalEnvelope) {
21425
+ if (primarySignalEnvelope !== null && isFrontendExceptionEnvelope(primarySignalEnvelope)) {
21426
+ return primarySignalEnvelope.payload.browser_event ?? null;
21427
+ }
21428
+ const envelope = selectLatestEnvelopeByType(envelopes, isFrontendExceptionEnvelope);
21429
+ return envelope?.payload.browser_event ?? null;
21430
+ }
21431
+ function isOpaqueBrowserError(errorContext, browserEvent) {
21432
+ if (browserEvent?.opaque === true) {
21433
+ return true;
21434
+ }
21435
+ const firstFrame = errorContext?.top_frames[0];
21436
+ return errorContext?.message === "Window error" && firstFrame !== void 0 && isBrowserSdkFallbackFrame(firstFrame);
21437
+ }
19581
21438
  function buildErrorContext(envelopes, incident, primarySignalEnvelope) {
19582
21439
  if (primarySignalEnvelope !== null && isBackendExceptionEnvelope(primarySignalEnvelope)) {
19583
21440
  return {
@@ -19700,6 +21557,20 @@ function buildSummaryGuidance(input) {
19700
21557
  recommended_action: null
19701
21558
  };
19702
21559
  }
21560
+ if (input.opaqueBrowserError) {
21561
+ if (input.browserEvent?.kind === "resource_error") {
21562
+ return {
21563
+ likely_cause: "The browser reported a resource load error without a usable application stack.",
21564
+ confidence: 0.35,
21565
+ recommended_action: "Inspect the captured resource target, browser network failures, CSP rules, and cross-origin asset configuration."
21566
+ };
21567
+ }
21568
+ return {
21569
+ likely_cause: "The browser reported an opaque window error without a usable application stack.",
21570
+ confidence: 0.35,
21571
+ recommended_action: "Inspect browser console output, resource loading, cross-origin script settings, and framework-level error boundaries for the affected route."
21572
+ };
21573
+ }
19703
21574
  const route = input.requestContext?.route_template ?? input.requestContext?.path ?? null;
19704
21575
  const requestDescription = input.requestContext !== null ? `${input.requestContext.method} ${route ?? input.requestContext.path}` : null;
19705
21576
  const firstDependency = input.dependenciesContext?.items[0] ?? null;
@@ -19886,7 +21757,8 @@ function buildFrontendContext(envelopes) {
19886
21757
  message: envelope.payload.message,
19887
21758
  route: envelope.payload.route ?? null,
19888
21759
  browser: envelope.payload.browser,
19889
- ts: toIsoTimestamp(envelope.occurred_at)
21760
+ ts: toIsoTimestamp(envelope.occurred_at),
21761
+ ...envelope.payload.browser_event !== void 0 ? { browser_event: envelope.payload.browser_event } : {}
19890
21762
  });
19891
21763
  }
19892
21764
  }
@@ -20029,6 +21901,8 @@ function buildBundle(input) {
20029
21901
  const gitContext = buildGitContext(sourceEnvelopes, input.configuredDeploy);
20030
21902
  const deviceContext = buildDeviceContext(sourceEnvelopes);
20031
21903
  const dependenciesContext = buildDependenciesContext(input.incident, errorContext, requestContext);
21904
+ const browserEvent = getPrimaryBrowserExceptionEvent(sourceEnvelopes, primarySignalEnvelope);
21905
+ const opaqueBrowserError = isOpaqueBrowserError(errorContext, browserEvent);
20032
21906
  const primarySignalType = primarySignalEnvelope !== null ? mapSignalType(primarySignalEnvelope.event_type) : inferSignalTypeFromSourceEventTypes(sourceEventTypes);
20033
21907
  const primarySourceEvent = errorContext?.name ?? sourceEventTypes[0] ?? "backend_exception";
20034
21908
  const firstSeenAt = new Date(input.incident.first_seen_at).toISOString();
@@ -20043,7 +21917,9 @@ function buildBundle(input) {
20043
21917
  requestContext,
20044
21918
  responseContext,
20045
21919
  dependenciesContext,
20046
- firstApplicationFrame
21920
+ firstApplicationFrame,
21921
+ browserEvent,
21922
+ opaqueBrowserError
20047
21923
  });
20048
21924
  const candidate = {
20049
21925
  bundle_version: 1,
@@ -21181,7 +23057,7 @@ async function processCommand(input, dependencies = {}) {
21181
23057
 
21182
23058
  // ../cli/src/ingest-command.ts
21183
23059
  var LOCAL_EVENTS_DIRECTORY_PATH2 = ".debugbundle/local/events";
21184
- var ProfileSchema3 = external_exports.object({
23060
+ var ProfileSchema2 = external_exports.object({
21185
23061
  project: external_exports.object({
21186
23062
  name: external_exports.string().min(1),
21187
23063
  repo_url: external_exports.string()
@@ -21206,7 +23082,7 @@ function buildEventFileName(events, filePath) {
21206
23082
  return `${lastOccurredAt}-${digest}-${slugify(events[0]?.service.name ?? (0, import_node_path7.basename)(filePath))}.events.json`;
21207
23083
  }
21208
23084
  async function readProfile(rootDirectory, readFile) {
21209
- const parsedProfile = ProfileSchema3.safeParse(JSON.parse(await readFile((0, import_node_path7.join)(rootDirectory, PROFILE_FILE_PATH))));
23085
+ const parsedProfile = ProfileSchema2.safeParse(JSON.parse(await readFile((0, import_node_path7.join)(rootDirectory, PROFILE_FILE_PATH))));
21210
23086
  if (!parsedProfile.success) {
21211
23087
  throw new Error(`Invalid ${PROFILE_FILE_PATH}`);
21212
23088
  }
@@ -21526,19 +23402,32 @@ function formatResult(input, exitCode, checks, errors, incidentId) {
21526
23402
  output: input.json ? buildJsonOutput(checks, errors, incidentId) : formatHumanOutput(checks, incidentId)
21527
23403
  };
21528
23404
  }
21529
- function buildCloudSuggestedActions(status, incidentId, mode = "passive_recent_incident") {
23405
+ function buildCloudSuggestedActions(status, incidentId, verification) {
23406
+ const mode = verification?.mode ?? "passive_recent_incident";
21530
23407
  if (status === "healthy" && incidentId !== void 0 && (mode === "active_5xx" || mode === "active_4xx")) {
21531
23408
  return [
21532
23409
  `Run debugbundle inspect ${incidentId} --source cloud to inspect why the incident fired.`,
21533
23410
  `Run debugbundle bundle ${incidentId} --source cloud to fetch the generated debug bundle.`
21534
23411
  ];
21535
23412
  }
23413
+ if (status === "healthy" && incidentId !== void 0 && mode === "app_event") {
23414
+ return [
23415
+ `Run debugbundle inspect ${incidentId} --source cloud to inspect the captured app event.`,
23416
+ "Re-run debugbundle verify cloud --expect-app-event after instrumentation or deploy changes, using the same service, environment, and correlation hints when available."
23417
+ ];
23418
+ }
21536
23419
  if (status === "healthy" && incidentId !== void 0) {
21537
23420
  return [
21538
23421
  `Review incident ${incidentId} if you want to inspect the latest production bundle.`,
21539
23422
  "Re-run debugbundle verify cloud after a fresh deploy or instrumentation change."
21540
23423
  ];
21541
23424
  }
23425
+ if (mode === "app_event") {
23426
+ return [
23427
+ "Trigger a real SDK event from the target app, then re-run debugbundle verify cloud --expect-app-event with the same service and environment filters.",
23428
+ "Add --trace-id or --request-id when you have a correlation hint so the verification can match the hosted bundle deterministically."
23429
+ ];
23430
+ }
21542
23431
  return [
21543
23432
  "Run debugbundle login to choose an auth flow, or use debugbundle login --github, debugbundle login --github-device, or debugbundle login <dbundle_mem_...> to create ~/.debugbundle/auth.json before verifying cloud traffic.",
21544
23433
  "Generate a live cloud request, then re-run debugbundle verify cloud with the correct project and service filters."
@@ -21551,7 +23440,7 @@ function buildCloudJsonOutput(checks, errors, incidentId, verification) {
21551
23440
  checks,
21552
23441
  warnings: collectWarnings(checks),
21553
23442
  errors,
21554
- suggested_actions: buildCloudSuggestedActions(status, incidentId, verification?.mode),
23443
+ suggested_actions: buildCloudSuggestedActions(status, incidentId, verification),
21555
23444
  auto_fix_available: false
21556
23445
  };
21557
23446
  if (verification !== void 0) {
@@ -21566,7 +23455,7 @@ function formatCloudHumanOutput(checks, incidentId, verification) {
21566
23455
  "Checks:",
21567
23456
  ...checks.map((check) => `- ${check.name}: ${check.status} - ${check.message}`),
21568
23457
  "Suggested actions:",
21569
- ...buildCloudSuggestedActions(status, incidentId, verification?.mode).map((action) => `- ${action}`)
23458
+ ...buildCloudSuggestedActions(status, incidentId, verification).map((action) => `- ${action}`)
21570
23459
  ].join("\n");
21571
23460
  }
21572
23461
  function formatCloudResult(input, exitCode, checks, errors, incidentId, verification) {
@@ -21597,6 +23486,19 @@ function localFailureStepName(checks) {
21597
23486
  function cloudVerificationRunId(now) {
21598
23487
  return now.toISOString().replace(/[-:.TZ]/g, "").slice(0, 14);
21599
23488
  }
23489
+ function defaultCloudVerificationSuffix() {
23490
+ return (0, import_node_crypto6.randomUUID)().replace(/-/g, "").slice(0, 12);
23491
+ }
23492
+ function normalizeCloudVerificationSuffix(suffix) {
23493
+ const normalized = suffix.toLowerCase().replace(/[^a-z0-9]/g, "").slice(0, 12);
23494
+ if (normalized.length > 0) {
23495
+ return normalized;
23496
+ }
23497
+ return defaultCloudVerificationSuffix();
23498
+ }
23499
+ function buildCloudVerificationRunId(now, suffix) {
23500
+ return `${cloudVerificationRunId(now)}-${normalizeCloudVerificationSuffix(suffix)}`;
23501
+ }
21600
23502
  function requestFailureReason(responseStatus) {
21601
23503
  const incidentReason = deriveIncidentReasonFromSignal({
21602
23504
  event_type: "request_event",
@@ -21609,7 +23511,6 @@ function requestFailureReason(responseStatus) {
21609
23511
  return incidentReason;
21610
23512
  }
21611
23513
  function buildCloudVerificationEvent(input) {
21612
- const runId = cloudVerificationRunId(input.now);
21613
23514
  const is5xxVerification = input.responseStatus >= 500;
21614
23515
  const routeTemplate = is5xxVerification ? "/debugbundle/verify/cloud" : `/debugbundle/verify/cloud/client-error/${input.responseStatus}`;
21615
23516
  const verificationLabel = is5xxVerification ? "true" : `client-error-${input.responseStatus}`;
@@ -21630,7 +23531,7 @@ function buildCloudVerificationEvent(input) {
21630
23531
  route_template: routeTemplate,
21631
23532
  query: {
21632
23533
  debugbundle_verification: true,
21633
- run_id: runId,
23534
+ run_id: input.runId,
21634
23535
  synthetic_status: input.responseStatus
21635
23536
  },
21636
23537
  headers: {
@@ -21644,7 +23545,7 @@ function buildCloudVerificationEvent(input) {
21644
23545
  response_body: {
21645
23546
  error: is5xxVerification ? "debugbundle_cloud_verification" : "debugbundle_cloud_client_error_verification",
21646
23547
  synthetic: true,
21647
- run_id: runId,
23548
+ run_id: input.runId,
21648
23549
  response_status: input.responseStatus
21649
23550
  }
21650
23551
  }
@@ -21659,6 +23560,45 @@ function validateActiveCloudVerificationInput(input) {
21659
23560
  }
21660
23561
  return null;
21661
23562
  }
23563
+ function validateCloudVerificationInput(input) {
23564
+ const activeInputError = validateActiveCloudVerificationInput(input);
23565
+ if (activeInputError !== null) {
23566
+ return activeInputError;
23567
+ }
23568
+ const appEventVerificationEnabled = input.expectAppEvent === true || input.traceId !== void 0 || input.requestId !== void 0;
23569
+ if (appEventVerificationEnabled && (input.trigger5xx === true || input.trigger4xxStatus !== void 0)) {
23570
+ return "Choose either a synthetic trigger run or --expect-app-event, not both.";
23571
+ }
23572
+ if (appEventVerificationEnabled && input.service === void 0 && input.traceId === void 0 && input.requestId === void 0) {
23573
+ return "App-event verification requires --service, --trace-id, or --request-id so the check stays scoped.";
23574
+ }
23575
+ return null;
23576
+ }
23577
+ function buildCorrelationHints(input) {
23578
+ return {
23579
+ ...input.service === void 0 ? {} : { service: input.service },
23580
+ environment: input.environment,
23581
+ ...input.traceId === void 0 ? {} : { trace_id: input.traceId },
23582
+ ...input.requestId === void 0 ? {} : { request_id: input.requestId }
23583
+ };
23584
+ }
23585
+ function collectBundleHintMatches(bundle, input) {
23586
+ const serializedBundle = JSON.stringify(bundle);
23587
+ const matches = [];
23588
+ if (input.traceId !== void 0 && serializedBundle.includes(input.traceId)) {
23589
+ matches.push("trace_id");
23590
+ }
23591
+ if (input.requestId !== void 0 && serializedBundle.includes(input.requestId)) {
23592
+ matches.push("request_id");
23593
+ }
23594
+ return matches;
23595
+ }
23596
+ function requestedBundleHints(input) {
23597
+ return [
23598
+ ...input.traceId === void 0 ? [] : ["trace_id"],
23599
+ ...input.requestId === void 0 ? [] : ["request_id"]
23600
+ ];
23601
+ }
21662
23602
  async function sendEventsToApi(input, dependencies = {}) {
21663
23603
  const fetchImpl = dependencies.fetchImpl ?? fetch;
21664
23604
  const baseUrl = input.baseUrl.endsWith("/") ? input.baseUrl.slice(0, -1) : input.baseUrl;
@@ -21850,7 +23790,7 @@ async function verifyCloudCommand(input, dependencies = {}) {
21850
23790
  const checks = [];
21851
23791
  const environment = input.environment ?? "production";
21852
23792
  const maxAgeMinutes = input.maxAgeMinutes ?? 15;
21853
- const activeInputError = validateActiveCloudVerificationInput(input);
23793
+ const activeInputError = validateCloudVerificationInput(input);
21854
23794
  if (activeInputError !== null) {
21855
23795
  checks.push({
21856
23796
  name: "trigger-input",
@@ -21891,9 +23831,124 @@ async function verifyCloudCommand(input, dependencies = {}) {
21891
23831
  requestInput,
21892
23832
  dependencies.fetchImpl === void 0 ? {} : { fetchImpl: dependencies.fetchImpl }
21893
23833
  ));
23834
+ const appEventVerificationEnabled = input.expectAppEvent === true || input.traceId !== void 0 || input.requestId !== void 0;
23835
+ if (appEventVerificationEnabled) {
23836
+ const verificationStartedAt = now();
23837
+ const pollAttempts = dependencies.pollAttempts ?? 6;
23838
+ const pollIntervalMs = dependencies.pollIntervalMs ?? 2e3;
23839
+ const sleep = dependencies.sleep ?? ((milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds)));
23840
+ const requestedHints = requestedBundleHints(input);
23841
+ const verification = {
23842
+ mode: "app_event",
23843
+ bundle_status: "unknown",
23844
+ correlation_hints: buildCorrelationHints({
23845
+ environment,
23846
+ ...input.service === void 0 ? {} : { service: input.service },
23847
+ ...input.traceId === void 0 ? {} : { traceId: input.traceId },
23848
+ ...input.requestId === void 0 ? {} : { requestId: input.requestId }
23849
+ })
23850
+ };
23851
+ const oldestAcceptedIncidentTimestamp = verificationStartedAt.getTime() - maxAgeMinutes * 6e4;
23852
+ let incidentId;
23853
+ let exitCode = 0;
23854
+ let activeStep = "app-event-visibility";
23855
+ const errors = [];
23856
+ try {
23857
+ for (let attempt = 1; attempt <= pollAttempts; attempt += 1) {
23858
+ const result = await listIncidents({
23859
+ bearerToken: authState.bearer_token,
23860
+ projectId: input.projectId,
23861
+ environment,
23862
+ ...input.service === void 0 ? {} : { service: input.service },
23863
+ limit: 5
23864
+ });
23865
+ const recentIncidents = result.incidents.filter((candidate) => {
23866
+ const lastSeenAt = new Date(candidate.last_seen_at);
23867
+ return !Number.isNaN(lastSeenAt.getTime()) && lastSeenAt.getTime() >= oldestAcceptedIncidentTimestamp;
23868
+ });
23869
+ for (const candidate of recentIncidents) {
23870
+ const lastSeenAt = new Date(candidate.last_seen_at);
23871
+ if (requestedHints.length === 0 && lastSeenAt.getTime() < verificationStartedAt.getTime()) {
23872
+ continue;
23873
+ }
23874
+ if (requestedHints.length === 0) {
23875
+ incidentId = candidate.incident_id;
23876
+ verification.incident_id = candidate.incident_id;
23877
+ break;
23878
+ }
23879
+ activeStep = "bundle-status";
23880
+ const bundle = await getBundle({
23881
+ bearerToken: authState.bearer_token,
23882
+ incidentId: candidate.incident_id
23883
+ });
23884
+ verification.bundle_status = "status" in bundle && bundle.status === "pending" ? "pending" : "ready";
23885
+ if (verification.bundle_status !== "ready") {
23886
+ continue;
23887
+ }
23888
+ const matchedHints = collectBundleHintMatches(bundle, input);
23889
+ verification.matched_hints = matchedHints;
23890
+ if (requestedHints.every((hint) => matchedHints.includes(hint))) {
23891
+ incidentId = candidate.incident_id;
23892
+ verification.incident_id = candidate.incident_id;
23893
+ verification.suggested_next_command = `debugbundle inspect ${candidate.incident_id} --source cloud`;
23894
+ break;
23895
+ }
23896
+ }
23897
+ if (incidentId !== void 0) {
23898
+ break;
23899
+ }
23900
+ if (attempt < pollAttempts) {
23901
+ await sleep(pollIntervalMs);
23902
+ }
23903
+ }
23904
+ if (incidentId === void 0) {
23905
+ if (requestedHints.length > 0) {
23906
+ throw new Error(`No recent cloud incident matched the requested ${requestedHints.join(" and ")} hints within the ${maxAgeMinutes} minute verification window.`);
23907
+ }
23908
+ throw new Error(`No new ${environment} app event was visible within the ${maxAgeMinutes} minute verification window.`);
23909
+ }
23910
+ checks.push({
23911
+ name: "app-event-visibility",
23912
+ status: "ok",
23913
+ message: `Observed cloud incident ${incidentId} for the requested app-driven verification window.`
23914
+ });
23915
+ if (requestedHints.length > 0) {
23916
+ checks.push({
23917
+ name: "bundle-hint-match",
23918
+ status: "ok",
23919
+ message: `Matched ${verification.matched_hints?.join(" and ")} in bundle ${incidentId}.`
23920
+ });
23921
+ } else {
23922
+ const bundle = await getBundle({
23923
+ bearerToken: authState.bearer_token,
23924
+ incidentId
23925
+ });
23926
+ verification.bundle_status = "status" in bundle && bundle.status === "pending" ? "pending" : "ready";
23927
+ verification.suggested_next_command = `debugbundle inspect ${incidentId} --source cloud`;
23928
+ checks.push({
23929
+ name: "bundle-status",
23930
+ status: verification.bundle_status === "ready" ? "ok" : "warning",
23931
+ message: verification.bundle_status === "ready" ? `Bundle for incident ${incidentId} is ready.` : `Bundle for incident ${incidentId} is still pending.`
23932
+ });
23933
+ }
23934
+ } catch (error) {
23935
+ const message = error instanceof Error ? error.message : String(error);
23936
+ checks.push({
23937
+ name: activeStep,
23938
+ status: "error",
23939
+ message
23940
+ });
23941
+ errors.push(message);
23942
+ exitCode = 1;
23943
+ }
23944
+ return formatCloudResult(input, exitCode, checks, errors, incidentId, verification);
23945
+ }
21894
23946
  if (input.trigger5xx === true || input.trigger4xxStatus !== void 0) {
21895
23947
  const verificationStartedAt = now();
21896
- const runId = cloudVerificationRunId(verificationStartedAt);
23948
+ const runId = buildCloudVerificationRunId(
23949
+ verificationStartedAt,
23950
+ (dependencies.randomId ?? defaultCloudVerificationSuffix)()
23951
+ );
21897
23952
  const serviceName = input.service ?? `debugbundle-verify-cloud-${runId}`;
21898
23953
  const tokenLabel = `debugbundle verify cloud ${runId}`;
21899
23954
  const pollAttempts = dependencies.pollAttempts ?? 6;
@@ -21925,6 +23980,7 @@ async function verifyCloudCommand(input, dependencies = {}) {
21925
23980
  }
21926
23981
  const event = buildCloudVerificationEvent({
21927
23982
  now: verificationStartedAt,
23983
+ runId,
21928
23984
  serviceName,
21929
23985
  environment,
21930
23986
  responseStatus
@@ -22231,53 +24287,62 @@ var import_node_path10 = require("node:path");
22231
24287
  var SUGGESTED_ACTIONS2 = [
22232
24288
  "Run debugbundle setup if .debugbundle/profile.json is missing.",
22233
24289
  "Run debugbundle profile validate for field-level profile errors.",
22234
- "Run debugbundle validate --fix to recreate missing local DebugBundle stubs when safe."
24290
+ "Run debugbundle validate --fix to recreate missing or stale local DebugBundle stubs when safe."
22235
24291
  ];
22236
24292
  var FIXABLE_FILES = [
22237
24293
  {
22238
24294
  name: "connection-config",
22239
24295
  filePath: CONNECTION_FILE_PATH,
22240
- buildContent: buildConnectionConfig
24296
+ buildContent: buildConnectionConfig,
24297
+ checkContent: false
22241
24298
  },
22242
24299
  {
22243
24300
  name: "agent-skill",
22244
24301
  filePath: SKILL_FILE_PATH,
22245
- buildContent: buildSkill
24302
+ buildContent: buildSkill,
24303
+ checkContent: true
22246
24304
  },
22247
24305
  {
22248
24306
  name: "cli-reference",
22249
24307
  filePath: CLI_REFERENCE_FILE_PATH,
22250
- buildContent: buildCliReference
24308
+ buildContent: buildCliReference,
24309
+ checkContent: true
22251
24310
  },
22252
24311
  {
22253
24312
  name: "mcp-reference",
22254
24313
  filePath: MCP_REFERENCE_FILE_PATH,
22255
- buildContent: buildMcpReference
24314
+ buildContent: buildMcpReference,
24315
+ checkContent: true
22256
24316
  },
22257
24317
  {
22258
24318
  name: "bundle-schema-reference",
22259
24319
  filePath: BUNDLE_SCHEMA_REFERENCE_FILE_PATH,
22260
- buildContent: buildBundleSchemaReference
24320
+ buildContent: buildBundleSchemaReference,
24321
+ checkContent: true
22261
24322
  },
22262
24323
  {
22263
24324
  name: "profile-enrichment-reference",
22264
24325
  filePath: PROFILE_ENRICHMENT_REFERENCE_FILE_PATH,
22265
- buildContent: buildProfileEnrichmentReference
24326
+ buildContent: buildProfileEnrichmentReference,
24327
+ checkContent: true
22266
24328
  },
22267
24329
  {
22268
24330
  name: "improvement-analysis-recipe",
22269
24331
  filePath: IMPROVEMENT_ANALYSIS_RECIPE_FILE_PATH,
22270
- buildContent: buildImprovementAnalysisRecipe
24332
+ buildContent: buildImprovementAnalysisRecipe,
24333
+ checkContent: true
22271
24334
  },
22272
24335
  {
22273
24336
  name: "performance-analysis-recipe",
22274
24337
  filePath: PERFORMANCE_ANALYSIS_RECIPE_FILE_PATH,
22275
- buildContent: buildPerformanceAnalysisRecipe
24338
+ buildContent: buildPerformanceAnalysisRecipe,
24339
+ checkContent: true
22276
24340
  },
22277
24341
  {
22278
24342
  name: "skill-evals",
22279
24343
  filePath: EVALS_FILE_PATH,
22280
- buildContent: buildSkillEvals
24344
+ buildContent: buildSkillEvals,
24345
+ checkContent: true
22281
24346
  }
22282
24347
  ];
22283
24348
  async function pathExists5(path, stat) {
@@ -22391,6 +24456,28 @@ async function validateCommand(input, dependencies = {}) {
22391
24456
  for (const fixableFile of FIXABLE_FILES) {
22392
24457
  const absoluteFilePath = (0, import_node_path10.join)(rootDirectory, fixableFile.filePath);
22393
24458
  if (await pathExists5(absoluteFilePath, stat)) {
24459
+ if (fixableFile.checkContent) {
24460
+ const currentContents = await readFile(absoluteFilePath);
24461
+ const expectedContents = fixableFile.buildContent();
24462
+ if (currentContents !== expectedContents) {
24463
+ if (input.fix === true) {
24464
+ await writeFile(absoluteFilePath, expectedContents);
24465
+ checks.push({
24466
+ name: fixableFile.name,
24467
+ status: "ok",
24468
+ message: `Updated stale ${fixableFile.filePath}`
24469
+ });
24470
+ continue;
24471
+ }
24472
+ autoFixAvailable = true;
24473
+ checks.push({
24474
+ name: fixableFile.name,
24475
+ status: "warning",
24476
+ message: `Stale ${fixableFile.filePath}; run debugbundle validate --fix to refresh it.`
24477
+ });
24478
+ continue;
24479
+ }
24480
+ }
22394
24481
  checks.push({
22395
24482
  name: fixableFile.name,
22396
24483
  status: "ok",
@@ -22613,8 +24700,85 @@ function createBillingMcpTools(api) {
22613
24700
  };
22614
24701
  }
22615
24702
 
22616
- // src/capture-policy-tools.ts
24703
+ // src/capture-rule-tools.ts
22617
24704
  function mapMcpError4(error) {
24705
+ if (error instanceof CaptureRuleApiError) {
24706
+ throw new Error(`mcp_tool_error:${error.message}`);
24707
+ }
24708
+ throw new Error("mcp_tool_error:unknown_error");
24709
+ }
24710
+ function createCaptureRuleMcpTools(api) {
24711
+ return {
24712
+ async list_capture_rules(input) {
24713
+ try {
24714
+ return await api.listCaptureRules({
24715
+ bearerToken: String(input["bearerToken"]),
24716
+ projectId: String(input["projectId"])
24717
+ });
24718
+ } catch (error) {
24719
+ mapMcpError4(error);
24720
+ }
24721
+ },
24722
+ async create_capture_rule(input) {
24723
+ try {
24724
+ return await api.createCaptureRule({
24725
+ bearerToken: String(input["bearerToken"]),
24726
+ projectId: String(input["projectId"]),
24727
+ create: typeof input["create"] === "object" && input["create"] !== null ? input["create"] : {}
24728
+ });
24729
+ } catch (error) {
24730
+ mapMcpError4(error);
24731
+ }
24732
+ },
24733
+ async update_capture_rule(input) {
24734
+ try {
24735
+ return await api.updateCaptureRule({
24736
+ bearerToken: String(input["bearerToken"]),
24737
+ projectId: String(input["projectId"]),
24738
+ ruleId: String(input["ruleId"]),
24739
+ update: typeof input["update"] === "object" && input["update"] !== null ? input["update"] : {}
24740
+ });
24741
+ } catch (error) {
24742
+ mapMcpError4(error);
24743
+ }
24744
+ },
24745
+ async delete_capture_rule(input) {
24746
+ try {
24747
+ return await api.deleteCaptureRule({
24748
+ bearerToken: String(input["bearerToken"]),
24749
+ projectId: String(input["projectId"]),
24750
+ ruleId: String(input["ruleId"])
24751
+ });
24752
+ } catch (error) {
24753
+ mapMcpError4(error);
24754
+ }
24755
+ },
24756
+ async suggest_capture_rules_from_incident(input) {
24757
+ try {
24758
+ return await api.suggestCaptureRulesFromIncident({
24759
+ bearerToken: String(input["bearerToken"]),
24760
+ incidentId: String(input["incidentId"])
24761
+ });
24762
+ } catch (error) {
24763
+ mapMcpError4(error);
24764
+ }
24765
+ },
24766
+ async create_capture_rule_from_incident_suggestion(input) {
24767
+ try {
24768
+ return await api.createCaptureRuleFromIncidentSuggestion({
24769
+ bearerToken: String(input["bearerToken"]),
24770
+ incidentId: String(input["incidentId"]),
24771
+ create: typeof input["create"] === "object" && input["create"] !== null ? input["create"] : {}
24772
+ });
24773
+ } catch (error) {
24774
+ mapMcpError4(error);
24775
+ }
24776
+ }
24777
+ };
24778
+ }
24779
+
24780
+ // src/capture-policy-tools.ts
24781
+ function mapMcpError5(error) {
22618
24782
  if (error instanceof CapturePolicyApiError) {
22619
24783
  throw new Error(`mcp_tool_error:${error.message}`);
22620
24784
  }
@@ -22629,7 +24793,7 @@ function createCapturePolicyMcpTools(api) {
22629
24793
  projectId: String(input["projectId"])
22630
24794
  });
22631
24795
  } catch (error) {
22632
- mapMcpError4(error);
24796
+ mapMcpError5(error);
22633
24797
  }
22634
24798
  },
22635
24799
  async update_capture_policy(input) {
@@ -22641,14 +24805,14 @@ function createCapturePolicyMcpTools(api) {
22641
24805
  update
22642
24806
  });
22643
24807
  } catch (error) {
22644
- mapMcpError4(error);
24808
+ mapMcpError5(error);
22645
24809
  }
22646
24810
  }
22647
24811
  };
22648
24812
  }
22649
24813
 
22650
24814
  // src/github-tools.ts
22651
- function mapMcpError5(error) {
24815
+ function mapMcpError6(error) {
22652
24816
  if (error instanceof GitHubManagementApiError) {
22653
24817
  throw new Error(`mcp_tool_error:${error.code}`);
22654
24818
  }
@@ -22667,7 +24831,7 @@ function createGitHubMcpTools(api) {
22667
24831
  const repo = projectId === void 0 || api.getProjectRepo === void 0 ? void 0 : await api.getProjectRepo({ bearerToken, projectId });
22668
24832
  return repo === void 0 ? { installation } : { installation, repo };
22669
24833
  } catch (error) {
22670
- mapMcpError5(error);
24834
+ mapMcpError6(error);
22671
24835
  }
22672
24836
  },
22673
24837
  async list_github_repositories(input) {
@@ -22679,7 +24843,7 @@ function createGitHubMcpTools(api) {
22679
24843
  })
22680
24844
  };
22681
24845
  } catch (error) {
22682
- mapMcpError5(error);
24846
+ mapMcpError6(error);
22683
24847
  }
22684
24848
  },
22685
24849
  async list_github_dispatch_rules(input) {
@@ -22691,7 +24855,7 @@ function createGitHubMcpTools(api) {
22691
24855
  })
22692
24856
  };
22693
24857
  } catch (error) {
22694
- mapMcpError5(error);
24858
+ mapMcpError6(error);
22695
24859
  }
22696
24860
  },
22697
24861
  async create_github_dispatch_rule(input) {
@@ -22712,7 +24876,7 @@ function createGitHubMcpTools(api) {
22712
24876
  })
22713
24877
  };
22714
24878
  } catch (error) {
22715
- mapMcpError5(error);
24879
+ mapMcpError6(error);
22716
24880
  }
22717
24881
  },
22718
24882
  async update_github_dispatch_rule(input) {
@@ -22734,7 +24898,7 @@ function createGitHubMcpTools(api) {
22734
24898
  })
22735
24899
  };
22736
24900
  } catch (error) {
22737
- mapMcpError5(error);
24901
+ mapMcpError6(error);
22738
24902
  }
22739
24903
  },
22740
24904
  async delete_github_dispatch_rule(input) {
@@ -22750,7 +24914,7 @@ function createGitHubMcpTools(api) {
22750
24914
  rule_id: String(input["ruleId"])
22751
24915
  };
22752
24916
  } catch (error) {
22753
- mapMcpError5(error);
24917
+ mapMcpError6(error);
22754
24918
  }
22755
24919
  },
22756
24920
  async list_github_deliveries(input) {
@@ -22764,7 +24928,7 @@ function createGitHubMcpTools(api) {
22764
24928
  })
22765
24929
  };
22766
24930
  } catch (error) {
22767
- mapMcpError5(error);
24931
+ mapMcpError6(error);
22768
24932
  }
22769
24933
  },
22770
24934
  async retry_github_delivery(input) {
@@ -22777,7 +24941,7 @@ function createGitHubMcpTools(api) {
22777
24941
  })
22778
24942
  };
22779
24943
  } catch (error) {
22780
- mapMcpError5(error);
24944
+ mapMcpError6(error);
22781
24945
  }
22782
24946
  },
22783
24947
  async set_project_github_repo(input) {
@@ -22791,7 +24955,7 @@ function createGitHubMcpTools(api) {
22791
24955
  })
22792
24956
  };
22793
24957
  } catch (error) {
22794
- mapMcpError5(error);
24958
+ mapMcpError6(error);
22795
24959
  }
22796
24960
  },
22797
24961
  async remove_project_github_repo(input) {
@@ -22805,14 +24969,14 @@ function createGitHubMcpTools(api) {
22805
24969
  project_id: String(input["projectId"])
22806
24970
  };
22807
24971
  } catch (error) {
22808
- mapMcpError5(error);
24972
+ mapMcpError6(error);
22809
24973
  }
22810
24974
  }
22811
24975
  };
22812
24976
  }
22813
24977
 
22814
24978
  // src/improvement-tools.ts
22815
- function mapMcpError6(error) {
24979
+ function mapMcpError7(error) {
22816
24980
  if (error instanceof RetrievalApiError) {
22817
24981
  throw new Error(`mcp_tool_error:${error.code}`);
22818
24982
  }
@@ -22845,7 +25009,7 @@ function createImprovementMcpTools(api) {
22845
25009
  ...typeof input["limit"] === "number" ? { limit: input["limit"] } : {}
22846
25010
  });
22847
25011
  } catch (error) {
22848
- mapMcpError6(error);
25012
+ mapMcpError7(error);
22849
25013
  }
22850
25014
  },
22851
25015
  async get_improvement(input) {
@@ -22855,7 +25019,7 @@ function createImprovementMcpTools(api) {
22855
25019
  improvementId: readString2(input, "improvementId")
22856
25020
  });
22857
25021
  } catch (error) {
22858
- mapMcpError6(error);
25022
+ mapMcpError7(error);
22859
25023
  }
22860
25024
  },
22861
25025
  async get_improvement_bundle(input) {
@@ -22866,7 +25030,7 @@ function createImprovementMcpTools(api) {
22866
25030
  improvementId: readString2(input, "improvementId")
22867
25031
  });
22868
25032
  } catch (error) {
22869
- mapMcpError6(error);
25033
+ mapMcpError7(error);
22870
25034
  }
22871
25035
  },
22872
25036
  async resolve_improvement(input) {
@@ -22876,7 +25040,7 @@ function createImprovementMcpTools(api) {
22876
25040
  improvementId: readString2(input, "improvementId")
22877
25041
  });
22878
25042
  } catch (error) {
22879
- mapMcpError6(error);
25043
+ mapMcpError7(error);
22880
25044
  }
22881
25045
  },
22882
25046
  async reopen_improvement(input) {
@@ -22886,7 +25050,7 @@ function createImprovementMcpTools(api) {
22886
25050
  improvementId: readString2(input, "improvementId")
22887
25051
  });
22888
25052
  } catch (error) {
22889
- mapMcpError6(error);
25053
+ mapMcpError7(error);
22890
25054
  }
22891
25055
  },
22892
25056
  async snooze_improvement(input) {
@@ -22897,14 +25061,14 @@ function createImprovementMcpTools(api) {
22897
25061
  snoozedUntil: readString2(input, "snoozedUntil")
22898
25062
  });
22899
25063
  } catch (error) {
22900
- mapMcpError6(error);
25064
+ mapMcpError7(error);
22901
25065
  }
22902
25066
  }
22903
25067
  };
22904
25068
  }
22905
25069
 
22906
25070
  // src/improvement-settings-tools.ts
22907
- function mapMcpError7(error) {
25071
+ function mapMcpError8(error) {
22908
25072
  if (error instanceof ImprovementSettingsApiError) {
22909
25073
  throw new Error(`mcp_tool_error:${error.message}`);
22910
25074
  }
@@ -22919,7 +25083,7 @@ function createImprovementSettingsMcpTools(api) {
22919
25083
  projectId: String(input["projectId"])
22920
25084
  });
22921
25085
  } catch (error) {
22922
- mapMcpError7(error);
25086
+ mapMcpError8(error);
22923
25087
  }
22924
25088
  },
22925
25089
  async update_improvement_settings(input) {
@@ -22931,14 +25095,14 @@ function createImprovementSettingsMcpTools(api) {
22931
25095
  update
22932
25096
  });
22933
25097
  } catch (error) {
22934
- mapMcpError7(error);
25098
+ mapMcpError8(error);
22935
25099
  }
22936
25100
  }
22937
25101
  };
22938
25102
  }
22939
25103
 
22940
25104
  // src/member-tools.ts
22941
- function mapMcpError8(error) {
25105
+ function mapMcpError9(error) {
22942
25106
  if (error instanceof MemberApiError) {
22943
25107
  throw new Error(`mcp_tool_error:${error.code}`);
22944
25108
  }
@@ -22953,7 +25117,7 @@ function createMemberMcpTools(api) {
22953
25117
  projectId: String(input["projectId"])
22954
25118
  });
22955
25119
  } catch (error) {
22956
- mapMcpError8(error);
25120
+ mapMcpError9(error);
22957
25121
  }
22958
25122
  },
22959
25123
  async list_project_member_invites(input) {
@@ -22963,7 +25127,7 @@ function createMemberMcpTools(api) {
22963
25127
  projectId: String(input["projectId"])
22964
25128
  });
22965
25129
  } catch (error) {
22966
- mapMcpError8(error);
25130
+ mapMcpError9(error);
22967
25131
  }
22968
25132
  },
22969
25133
  async invite_project_member(input) {
@@ -22975,7 +25139,7 @@ function createMemberMcpTools(api) {
22975
25139
  role: String(input["role"])
22976
25140
  });
22977
25141
  } catch (error) {
22978
- mapMcpError8(error);
25142
+ mapMcpError9(error);
22979
25143
  }
22980
25144
  },
22981
25145
  async cancel_project_member_invite(input) {
@@ -22986,7 +25150,7 @@ function createMemberMcpTools(api) {
22986
25150
  inviteId: String(input["inviteId"])
22987
25151
  });
22988
25152
  } catch (error) {
22989
- mapMcpError8(error);
25153
+ mapMcpError9(error);
22990
25154
  }
22991
25155
  },
22992
25156
  async update_project_member_role(input) {
@@ -22998,7 +25162,7 @@ function createMemberMcpTools(api) {
22998
25162
  role: String(input["role"])
22999
25163
  });
23000
25164
  } catch (error) {
23001
- mapMcpError8(error);
25165
+ mapMcpError9(error);
23002
25166
  }
23003
25167
  },
23004
25168
  async remove_project_member(input) {
@@ -23009,14 +25173,14 @@ function createMemberMcpTools(api) {
23009
25173
  userId: String(input["userId"])
23010
25174
  });
23011
25175
  } catch (error) {
23012
- mapMcpError8(error);
25176
+ mapMcpError9(error);
23013
25177
  }
23014
25178
  }
23015
25179
  };
23016
25180
  }
23017
25181
 
23018
25182
  // src/probe-tools.ts
23019
- function mapMcpError9(error) {
25183
+ function mapMcpError10(error) {
23020
25184
  if (error instanceof ProbeApiError) {
23021
25185
  throw new Error(`mcp_tool_error:${error.code}`);
23022
25186
  }
@@ -23045,7 +25209,7 @@ function createProbeMcpTools(api) {
23045
25209
  }
23046
25210
  return await api.activateProbe(requestInput);
23047
25211
  } catch (error) {
23048
- mapMcpError9(error);
25212
+ mapMcpError10(error);
23049
25213
  }
23050
25214
  },
23051
25215
  async list_active_probes(input) {
@@ -23055,7 +25219,7 @@ function createProbeMcpTools(api) {
23055
25219
  projectId: String(input["projectId"])
23056
25220
  });
23057
25221
  } catch (error) {
23058
- mapMcpError9(error);
25222
+ mapMcpError10(error);
23059
25223
  }
23060
25224
  },
23061
25225
  async deactivate_probe(input) {
@@ -23066,14 +25230,14 @@ function createProbeMcpTools(api) {
23066
25230
  activationId: String(input["activationId"])
23067
25231
  });
23068
25232
  } catch (error) {
23069
- mapMcpError9(error);
25233
+ mapMcpError10(error);
23070
25234
  }
23071
25235
  }
23072
25236
  };
23073
25237
  }
23074
25238
 
23075
25239
  // src/project-tools.ts
23076
- function mapMcpError10(error) {
25240
+ function mapMcpError11(error) {
23077
25241
  if (error instanceof ProjectManagementApiError) {
23078
25242
  throw new Error(`mcp_tool_error:${error.code}`);
23079
25243
  }
@@ -23091,7 +25255,7 @@ function createProjectMcpTools(api) {
23091
25255
  }
23092
25256
  return { projects: await api.listProjects(requestInput) };
23093
25257
  } catch (error) {
23094
- mapMcpError10(error);
25258
+ mapMcpError11(error);
23095
25259
  }
23096
25260
  },
23097
25261
  async create_project(input) {
@@ -23106,7 +25270,7 @@ function createProjectMcpTools(api) {
23106
25270
  }
23107
25271
  return { project: await api.createProject(requestInput) };
23108
25272
  } catch (error) {
23109
- mapMcpError10(error);
25273
+ mapMcpError11(error);
23110
25274
  }
23111
25275
  },
23112
25276
  async update_project(input) {
@@ -23126,7 +25290,7 @@ function createProjectMcpTools(api) {
23126
25290
  }
23127
25291
  return { project: await api.updateProject(requestInput) };
23128
25292
  } catch (error) {
23129
- mapMcpError10(error);
25293
+ mapMcpError11(error);
23130
25294
  }
23131
25295
  },
23132
25296
  async delete_project(input) {
@@ -23138,7 +25302,7 @@ function createProjectMcpTools(api) {
23138
25302
  })
23139
25303
  };
23140
25304
  } catch (error) {
23141
- mapMcpError10(error);
25305
+ mapMcpError11(error);
23142
25306
  }
23143
25307
  }
23144
25308
  };
@@ -23345,7 +25509,7 @@ async function persistCloudArtifact(directoryPath, fileName, payload, dependenci
23345
25509
  }
23346
25510
 
23347
25511
  // src/retrieval-tools.ts
23348
- function mapMcpError11(error) {
25512
+ function mapMcpError12(error) {
23349
25513
  if (error instanceof RetrievalApiError) {
23350
25514
  throw new Error(`mcp_tool_error:${error.code}`);
23351
25515
  }
@@ -23527,7 +25691,7 @@ function createRetrievalMcpTools(api) {
23527
25691
  incidents: incidents.incidents.map((incident) => attachSourceToRecord(incident, "cloud"))
23528
25692
  };
23529
25693
  } catch (error) {
23530
- mapMcpError11(error);
25694
+ mapMcpError12(error);
23531
25695
  }
23532
25696
  },
23533
25697
  async get_incident(input) {
@@ -23562,7 +25726,7 @@ function createRetrievalMcpTools(api) {
23562
25726
  )
23563
25727
  };
23564
25728
  } catch (error) {
23565
- mapMcpError11(error);
25729
+ mapMcpError12(error);
23566
25730
  }
23567
25731
  },
23568
25732
  async get_incident_context(input) {
@@ -23591,7 +25755,7 @@ function createRetrievalMcpTools(api) {
23591
25755
  "cloud"
23592
25756
  );
23593
25757
  } catch (error) {
23594
- mapMcpError11(error);
25758
+ mapMcpError12(error);
23595
25759
  }
23596
25760
  },
23597
25761
  async resolve_incident(input) {
@@ -23636,7 +25800,7 @@ function createRetrievalMcpTools(api) {
23636
25800
  })()
23637
25801
  };
23638
25802
  } catch (error) {
23639
- mapMcpError11(error);
25803
+ mapMcpError12(error);
23640
25804
  }
23641
25805
  },
23642
25806
  async reopen_incident(input) {
@@ -23681,7 +25845,7 @@ function createRetrievalMcpTools(api) {
23681
25845
  })()
23682
25846
  };
23683
25847
  } catch (error) {
23684
- mapMcpError11(error);
25848
+ mapMcpError12(error);
23685
25849
  }
23686
25850
  },
23687
25851
  async get_bundle(input) {
@@ -23712,7 +25876,7 @@ function createRetrievalMcpTools(api) {
23712
25876
  }
23713
25877
  );
23714
25878
  } catch (error) {
23715
- mapMcpError11(error);
25879
+ mapMcpError12(error);
23716
25880
  }
23717
25881
  },
23718
25882
  async get_logs(input) {
@@ -23732,7 +25896,7 @@ function createRetrievalMcpTools(api) {
23732
25896
  }
23733
25897
  return await api.getLogs(requestInput);
23734
25898
  } catch (error) {
23735
- mapMcpError11(error);
25899
+ mapMcpError12(error);
23736
25900
  }
23737
25901
  },
23738
25902
  async get_reproduction(input) {
@@ -23763,14 +25927,14 @@ function createRetrievalMcpTools(api) {
23763
25927
  }
23764
25928
  );
23765
25929
  } catch (error) {
23766
- mapMcpError11(error);
25930
+ mapMcpError12(error);
23767
25931
  }
23768
25932
  }
23769
25933
  };
23770
25934
  }
23771
25935
 
23772
25936
  // src/services-tools.ts
23773
- function mapMcpError12(error) {
25937
+ function mapMcpError13(error) {
23774
25938
  if (error instanceof RetrievalApiError) {
23775
25939
  throw new Error(`mcp_tool_error:${error.code}`);
23776
25940
  }
@@ -23791,14 +25955,14 @@ function createServicesMcpTools(api) {
23791
25955
  services: await api.listServices(requestInput)
23792
25956
  };
23793
25957
  } catch (error) {
23794
- mapMcpError12(error);
25958
+ mapMcpError13(error);
23795
25959
  }
23796
25960
  }
23797
25961
  };
23798
25962
  }
23799
25963
 
23800
25964
  // src/setup-tools.ts
23801
- function mapMcpError13() {
25965
+ function mapMcpError14() {
23802
25966
  throw new Error("mcp_tool_error:unknown_error");
23803
25967
  }
23804
25968
  function parseJsonOutput2(output) {
@@ -23813,7 +25977,7 @@ async function runJsonCommand2(command) {
23813
25977
  const result = await command();
23814
25978
  return parseJsonOutput2(result.output);
23815
25979
  } catch {
23816
- mapMcpError13();
25980
+ mapMcpError14();
23817
25981
  }
23818
25982
  }
23819
25983
  function createSetupMcpTools(commands) {
@@ -23872,7 +26036,7 @@ function createSetupMcpTools(commands) {
23872
26036
  }
23873
26037
 
23874
26038
  // src/slack-tools.ts
23875
- function mapMcpError14(error) {
26039
+ function mapMcpError15(error) {
23876
26040
  if (error instanceof SlackApiError) {
23877
26041
  throw new Error(`mcp_tool_error:${error.code}`);
23878
26042
  }
@@ -23889,7 +26053,7 @@ function createSlackMcpTools(api) {
23889
26053
  })
23890
26054
  };
23891
26055
  } catch (error) {
23892
- mapMcpError14(error);
26056
+ mapMcpError15(error);
23893
26057
  }
23894
26058
  },
23895
26059
  async get_slack_connect_url(input) {
@@ -23902,7 +26066,7 @@ function createSlackMcpTools(api) {
23902
26066
  })
23903
26067
  };
23904
26068
  } catch (error) {
23905
- mapMcpError14(error);
26069
+ mapMcpError15(error);
23906
26070
  }
23907
26071
  },
23908
26072
  async test_slack_destination(input) {
@@ -23915,7 +26079,7 @@ function createSlackMcpTools(api) {
23915
26079
  })
23916
26080
  };
23917
26081
  } catch (error) {
23918
- mapMcpError14(error);
26082
+ mapMcpError15(error);
23919
26083
  }
23920
26084
  },
23921
26085
  async delete_slack_destination(input) {
@@ -23928,14 +26092,14 @@ function createSlackMcpTools(api) {
23928
26092
  })
23929
26093
  };
23930
26094
  } catch (error) {
23931
- mapMcpError14(error);
26095
+ mapMcpError15(error);
23932
26096
  }
23933
26097
  }
23934
26098
  };
23935
26099
  }
23936
26100
 
23937
26101
  // src/token-tools.ts
23938
- function mapMcpError15(error) {
26102
+ function mapMcpError16(error) {
23939
26103
  if (error instanceof TokenManagementApiError) {
23940
26104
  throw new Error(`mcp_tool_error:${error.code}`);
23941
26105
  }
@@ -23956,20 +26120,22 @@ function createTokenMcpTools(api) {
23956
26120
  tokens: await api.listProjectTokens(requestInput)
23957
26121
  };
23958
26122
  } catch (error) {
23959
- mapMcpError15(error);
26123
+ mapMcpError16(error);
23960
26124
  }
23961
26125
  },
23962
26126
  async create_project_token(input) {
23963
26127
  try {
26128
+ const allowedOrigins = Array.isArray(input["allowedOrigins"]) ? input["allowedOrigins"].map((value) => String(value)) : void 0;
23964
26129
  return {
23965
26130
  token: await api.createProjectToken({
23966
26131
  bearerToken: String(input["bearerToken"]),
23967
26132
  projectId: String(input["projectId"]),
23968
- label: String(input["label"])
26133
+ label: String(input["label"]),
26134
+ ...allowedOrigins === void 0 ? {} : { allowedOrigins }
23969
26135
  })
23970
26136
  };
23971
26137
  } catch (error) {
23972
- mapMcpError15(error);
26138
+ mapMcpError16(error);
23973
26139
  }
23974
26140
  },
23975
26141
  async revoke_project_token(input) {
@@ -23982,7 +26148,7 @@ function createTokenMcpTools(api) {
23982
26148
  })
23983
26149
  };
23984
26150
  } catch (error) {
23985
- mapMcpError15(error);
26151
+ mapMcpError16(error);
23986
26152
  }
23987
26153
  },
23988
26154
  async list_member_tokens(input) {
@@ -23997,7 +26163,7 @@ function createTokenMcpTools(api) {
23997
26163
  tokens: await api.listMemberTokens(requestInput)
23998
26164
  };
23999
26165
  } catch (error) {
24000
- mapMcpError15(error);
26166
+ mapMcpError16(error);
24001
26167
  }
24002
26168
  },
24003
26169
  async create_member_token(input) {
@@ -24009,7 +26175,7 @@ function createTokenMcpTools(api) {
24009
26175
  })
24010
26176
  };
24011
26177
  } catch (error) {
24012
- mapMcpError15(error);
26178
+ mapMcpError16(error);
24013
26179
  }
24014
26180
  },
24015
26181
  async revoke_member_token(input) {
@@ -24021,14 +26187,14 @@ function createTokenMcpTools(api) {
24021
26187
  })
24022
26188
  };
24023
26189
  } catch (error) {
24024
- mapMcpError15(error);
26190
+ mapMcpError16(error);
24025
26191
  }
24026
26192
  }
24027
26193
  };
24028
26194
  }
24029
26195
 
24030
26196
  // src/webhook-tools.ts
24031
- function mapMcpError16(error) {
26197
+ function mapMcpError17(error) {
24032
26198
  if (error instanceof WebhookApiError) {
24033
26199
  throw new Error(`mcp_tool_error:${error.code}`);
24034
26200
  }
@@ -24049,7 +26215,7 @@ function createWebhookMcpTools(api) {
24049
26215
  webhooks: await api.listWebhooks(requestInput)
24050
26216
  };
24051
26217
  } catch (error) {
24052
- mapMcpError16(error);
26218
+ mapMcpError17(error);
24053
26219
  }
24054
26220
  },
24055
26221
  async create_webhook(input) {
@@ -24070,7 +26236,7 @@ function createWebhookMcpTools(api) {
24070
26236
  webhook: await api.createWebhook(requestInput)
24071
26237
  };
24072
26238
  } catch (error) {
24073
- mapMcpError16(error);
26239
+ mapMcpError17(error);
24074
26240
  }
24075
26241
  },
24076
26242
  async update_webhook(input) {
@@ -24096,7 +26262,7 @@ function createWebhookMcpTools(api) {
24096
26262
  webhook: await api.updateWebhook(requestInput)
24097
26263
  };
24098
26264
  } catch (error) {
24099
- mapMcpError16(error);
26265
+ mapMcpError17(error);
24100
26266
  }
24101
26267
  },
24102
26268
  async delete_webhook(input) {
@@ -24109,7 +26275,7 @@ function createWebhookMcpTools(api) {
24109
26275
  })
24110
26276
  };
24111
26277
  } catch (error) {
24112
- mapMcpError16(error);
26278
+ mapMcpError17(error);
24113
26279
  }
24114
26280
  },
24115
26281
  async test_webhook(input) {
@@ -24126,7 +26292,7 @@ function createWebhookMcpTools(api) {
24126
26292
  delivery: await api.testWebhook(requestInput)
24127
26293
  };
24128
26294
  } catch (error) {
24129
- mapMcpError16(error);
26295
+ mapMcpError17(error);
24130
26296
  }
24131
26297
  },
24132
26298
  async list_webhook_deliveries(input) {
@@ -24143,7 +26309,7 @@ function createWebhookMcpTools(api) {
24143
26309
  deliveries: await api.listWebhookDeliveries(requestInput)
24144
26310
  };
24145
26311
  } catch (error) {
24146
- mapMcpError16(error);
26312
+ mapMcpError17(error);
24147
26313
  }
24148
26314
  },
24149
26315
  async retry_webhook_delivery(input) {
@@ -24155,14 +26321,14 @@ function createWebhookMcpTools(api) {
24155
26321
  deliveryId: String(input["deliveryId"])
24156
26322
  });
24157
26323
  } catch (error) {
24158
- mapMcpError16(error);
26324
+ mapMcpError17(error);
24159
26325
  }
24160
26326
  }
24161
26327
  };
24162
26328
  }
24163
26329
 
24164
26330
  // src/weekly-report-tools.ts
24165
- function mapMcpError17(error) {
26331
+ function mapMcpError18(error) {
24166
26332
  if (error instanceof WeeklyReportApiError) {
24167
26333
  throw new Error(`mcp_tool_error:${error.code}`);
24168
26334
  }
@@ -24180,7 +26346,7 @@ function createWeeklyReportMcpTools(api) {
24180
26346
  })
24181
26347
  };
24182
26348
  } catch (error) {
24183
- mapMcpError17(error);
26349
+ mapMcpError18(error);
24184
26350
  }
24185
26351
  },
24186
26352
  async create_weekly_report_channel(input) {
@@ -24196,7 +26362,7 @@ function createWeeklyReportMcpTools(api) {
24196
26362
  })
24197
26363
  };
24198
26364
  } catch (error) {
24199
- mapMcpError17(error);
26365
+ mapMcpError18(error);
24200
26366
  }
24201
26367
  },
24202
26368
  async update_weekly_report_channel(input) {
@@ -24211,7 +26377,7 @@ function createWeeklyReportMcpTools(api) {
24211
26377
  })
24212
26378
  };
24213
26379
  } catch (error) {
24214
- mapMcpError17(error);
26380
+ mapMcpError18(error);
24215
26381
  }
24216
26382
  },
24217
26383
  async delete_weekly_report_channel(input) {
@@ -24223,7 +26389,7 @@ function createWeeklyReportMcpTools(api) {
24223
26389
  })
24224
26390
  };
24225
26391
  } catch (error) {
24226
- mapMcpError17(error);
26392
+ mapMcpError18(error);
24227
26393
  }
24228
26394
  }
24229
26395
  };
@@ -24279,6 +26445,7 @@ async function createDefaultMcpTools(input = {}) {
24279
26445
  ...createWeeklyReportMcpTools(createWeeklyReportApi(httpClient)),
24280
26446
  ...createAlertMcpTools(createAlertApi(httpClient)),
24281
26447
  ...createProjectMcpTools(createProjectManagementApi(httpClient)),
26448
+ ...createCaptureRuleMcpTools(createCaptureRuleApi(httpClient)),
24282
26449
  ...createCapturePolicyMcpTools(createCapturePolicyApi(httpClient)),
24283
26450
  ...createImprovementSettingsMcpTools(createImprovementSettingsApi(httpClient)),
24284
26451
  ...createProbeMcpTools(createProbeApi(httpClient)),
@@ -25906,7 +28073,8 @@ var MCP_TOOL_CATALOG = [
25906
28073
  inputSchema: external_exports.object({
25907
28074
  bearerToken: external_exports.string(),
25908
28075
  projectId: external_exports.string(),
25909
- label: external_exports.string()
28076
+ label: external_exports.string(),
28077
+ allowedOrigins: external_exports.array(external_exports.string()).optional()
25910
28078
  })
25911
28079
  },
25912
28080
  {
@@ -26078,7 +28246,7 @@ var MCP_TOOL_CATALOG = [
26078
28246
  {
26079
28247
  name: "create_weekly_report_channel",
26080
28248
  group: "weekly_reports",
26081
- description: "Create a weekly report delivery channel.",
28249
+ description: "Create a weekly report delivery channel. Email channel config supports up to 3 recipients in config.to.",
26082
28250
  inputSchema: external_exports.object({
26083
28251
  bearerToken: external_exports.string(),
26084
28252
  projectId: external_exports.string(),
@@ -26091,7 +28259,7 @@ var MCP_TOOL_CATALOG = [
26091
28259
  {
26092
28260
  name: "update_weekly_report_channel",
26093
28261
  group: "weekly_reports",
26094
- description: "Update a weekly report delivery channel.",
28262
+ description: "Update a weekly report delivery channel. Email channel config supports up to 3 recipients in config.to.",
26095
28263
  inputSchema: external_exports.object({
26096
28264
  bearerToken: external_exports.string(),
26097
28265
  channelId: external_exports.string(),
@@ -26201,6 +28369,65 @@ var MCP_TOOL_CATALOG = [
26201
28369
  projectId: external_exports.string()
26202
28370
  })
26203
28371
  },
28372
+ {
28373
+ name: "list_capture_rules",
28374
+ group: "capture_rules",
28375
+ description: "List project capture rules.",
28376
+ inputSchema: external_exports.object({
28377
+ bearerToken: external_exports.string(),
28378
+ projectId: external_exports.string()
28379
+ })
28380
+ },
28381
+ {
28382
+ name: "create_capture_rule",
28383
+ group: "capture_rules",
28384
+ description: "Create a project capture rule.",
28385
+ inputSchema: external_exports.object({
28386
+ bearerToken: external_exports.string(),
28387
+ projectId: external_exports.string(),
28388
+ create: jsonObjectSchema
28389
+ })
28390
+ },
28391
+ {
28392
+ name: "update_capture_rule",
28393
+ group: "capture_rules",
28394
+ description: "Update a project capture rule.",
28395
+ inputSchema: external_exports.object({
28396
+ bearerToken: external_exports.string(),
28397
+ projectId: external_exports.string(),
28398
+ ruleId: external_exports.string(),
28399
+ update: jsonObjectSchema
28400
+ })
28401
+ },
28402
+ {
28403
+ name: "delete_capture_rule",
28404
+ group: "capture_rules",
28405
+ description: "Delete a project capture rule.",
28406
+ inputSchema: external_exports.object({
28407
+ bearerToken: external_exports.string(),
28408
+ projectId: external_exports.string(),
28409
+ ruleId: external_exports.string()
28410
+ })
28411
+ },
28412
+ {
28413
+ name: "suggest_capture_rules_from_incident",
28414
+ group: "capture_rules",
28415
+ description: "Generate deterministic capture rule suggestions from an incident bundle.",
28416
+ inputSchema: external_exports.object({
28417
+ bearerToken: external_exports.string(),
28418
+ incidentId: external_exports.string()
28419
+ })
28420
+ },
28421
+ {
28422
+ name: "create_capture_rule_from_incident_suggestion",
28423
+ group: "capture_rules",
28424
+ description: "Create a capture rule from an incident-derived suggestion.",
28425
+ inputSchema: external_exports.object({
28426
+ bearerToken: external_exports.string(),
28427
+ incidentId: external_exports.string(),
28428
+ create: jsonObjectSchema
28429
+ })
28430
+ },
26204
28431
  {
26205
28432
  name: "get_capture_policy",
26206
28433
  group: "capture_policy",