@debugbundle/cli 1.8.1 → 1.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/main.cjs +685 -435
  2. package/package.json +1 -1
package/dist/main.cjs CHANGED
@@ -39659,17 +39659,17 @@ var require_dns = __commonJS({
39659
39659
  const timestamp = Date.now();
39660
39660
  const records = { records: { 4: null, 6: null } };
39661
39661
  let minTTL = this.#maxTTL;
39662
- for (const record of addresses) {
39663
- record.timestamp = timestamp;
39664
- if (typeof record.ttl === "number") {
39665
- record.ttl = Math.min(record.ttl, this.#maxTTL);
39666
- minTTL = Math.min(minTTL, record.ttl);
39662
+ for (const record2 of addresses) {
39663
+ record2.timestamp = timestamp;
39664
+ if (typeof record2.ttl === "number") {
39665
+ record2.ttl = Math.min(record2.ttl, this.#maxTTL);
39666
+ minTTL = Math.min(minTTL, record2.ttl);
39667
39667
  } else {
39668
- record.ttl = this.#maxTTL;
39668
+ record2.ttl = this.#maxTTL;
39669
39669
  }
39670
- const familyRecords = records.records[record.family] ?? { ips: [] };
39671
- familyRecords.ips.push(record);
39672
- records.records[record.family] = familyRecords;
39670
+ const familyRecords = records.records[record2.family] ?? { ips: [] };
39671
+ familyRecords.ips.push(record2);
39672
+ records.records[record2.family] = familyRecords;
39673
39673
  }
39674
39674
  this.storage.set(origin.hostname, records, { ttl: minTTL });
39675
39675
  }
@@ -55119,15 +55119,15 @@ var require_util7 = __commonJS({
55119
55119
  exports2.getUniqueHostnamesFromOptions = getUniqueHostnamesFromOptions;
55120
55120
  function groupSrvRecords(records) {
55121
55121
  const recordsByPriority = {};
55122
- for (const record of records) {
55123
- if (!recordsByPriority.hasOwnProperty(record.priority)) {
55124
- recordsByPriority[record.priority] = {
55125
- totalWeight: record.weight,
55126
- records: [record]
55122
+ for (const record2 of records) {
55123
+ if (!recordsByPriority.hasOwnProperty(record2.priority)) {
55124
+ recordsByPriority[record2.priority] = {
55125
+ totalWeight: record2.weight,
55126
+ records: [record2]
55127
55127
  };
55128
55128
  } else {
55129
- recordsByPriority[record.priority].totalWeight += record.weight;
55130
- recordsByPriority[record.priority].records.push(record);
55129
+ recordsByPriority[record2.priority].totalWeight += record2.weight;
55130
+ recordsByPriority[record2.priority].records.push(record2);
55131
55131
  }
55132
55132
  }
55133
55133
  return recordsByPriority;
@@ -55140,12 +55140,12 @@ var require_util7 = __commonJS({
55140
55140
  }
55141
55141
  const random = Math.floor(Math.random() * (recordsGroup.totalWeight + recordsGroup.records.length));
55142
55142
  let total = 0;
55143
- for (const [i, record] of recordsGroup.records.entries()) {
55144
- total += 1 + record.weight;
55143
+ for (const [i, record2] of recordsGroup.records.entries()) {
55144
+ total += 1 + record2.weight;
55145
55145
  if (total > random) {
55146
- recordsGroup.totalWeight -= record.weight;
55146
+ recordsGroup.totalWeight -= record2.weight;
55147
55147
  recordsGroup.records.splice(i, 1);
55148
- return record;
55148
+ return record2;
55149
55149
  }
55150
55150
  }
55151
55151
  }
@@ -57067,13 +57067,13 @@ var require_cluster = __commonJS({
57067
57067
  if (!sortedKeys.length) {
57068
57068
  return reject(err2);
57069
57069
  }
57070
- const key = sortedKeys[0], group = groupedRecords[key], record = (0, util_1.weightSrvRecords)(group);
57070
+ const key = sortedKeys[0], group = groupedRecords[key], record2 = (0, util_1.weightSrvRecords)(group);
57071
57071
  if (!group.records.length) {
57072
57072
  sortedKeys.shift();
57073
57073
  }
57074
- self.dnsLookup(record.name).then((host) => resolve2({
57074
+ self.dnsLookup(record2.name).then((host) => resolve2({
57075
57075
  host,
57076
- port: record.port
57076
+ port: record2.port
57077
57077
  }), tryFirstOne);
57078
57078
  }
57079
57079
  tryFirstOne();
@@ -63786,6 +63786,73 @@ function createEventEnvelope(input3) {
63786
63786
  return EventEnvelopeSchema.parse(candidate);
63787
63787
  }
63788
63788
 
63789
+ // ../../packages/shared-types/src/browser-resource-routes.ts
63790
+ var BrowserResourceRoutesSchema = external_exports.object({
63791
+ items: external_exports.array(external_exports.object({ route: external_exports.string().max(1024), occurrences: external_exports.number().int().positive() })).max(20),
63792
+ recorded_occurrences: external_exports.number().int().nonnegative(),
63793
+ unattributed_occurrences: external_exports.number().int().nonnegative(),
63794
+ omitted_routes: external_exports.number().int().nonnegative(),
63795
+ coverage: external_exports.enum(["occurrence_metadata", "retained_samples"])
63796
+ });
63797
+ function normalizeResourceRoute(value) {
63798
+ if (typeof value !== "string" || !value.startsWith("/") || value.startsWith("//") || value.length > 4096)
63799
+ return null;
63800
+ const path = value.split(/[?#]/, 1)[0];
63801
+ if (/[\u0000-\u0020\u007f\\]/.test(path)) return null;
63802
+ const segments = path.split("/").filter(Boolean).map((segment) => {
63803
+ let decoded;
63804
+ try {
63805
+ decoded = decodeURIComponent(segment);
63806
+ } catch {
63807
+ return "{param}";
63808
+ }
63809
+ if (decoded === "{param}" || /^:[A-Za-z_][A-Za-z_0-9]*$/.test(decoded)) return "{param}";
63810
+ return /^\d+$/.test(decoded) || /^[a-f0-9-]{16,}$/i.test(decoded) || /^[A-Za-z0-9_-]{24,}$/.test(decoded) || decoded.length >= 16 && /[A-Za-z]/.test(decoded) && /\d/.test(decoded) || /[@/\\\u0000-\u0020\u007f]/.test(decoded) ? "{param}" : segment;
63811
+ });
63812
+ const route = `/${segments.join("/")}`;
63813
+ return route.length <= 1024 ? route : null;
63814
+ }
63815
+ function summarizeResourceRoutes(routes, occurrenceCount) {
63816
+ const counts = /* @__PURE__ */ new Map();
63817
+ for (const raw of routes) {
63818
+ const route = normalizeResourceRoute(raw);
63819
+ if (route !== null) counts.set(route, (counts.get(route) ?? 0) + 1);
63820
+ }
63821
+ const items = [...counts].map(([route, occurrences]) => ({ route, occurrences })).sort(
63822
+ (a, b) => b.occurrences - a.occurrences || (a.route < b.route ? -1 : a.route > b.route ? 1 : 0)
63823
+ );
63824
+ const recorded = items.reduce((total, item) => total + item.occurrences, 0);
63825
+ return {
63826
+ items: items.slice(0, 20),
63827
+ recorded_occurrences: recorded,
63828
+ unattributed_occurrences: Math.max(0, occurrenceCount - recorded),
63829
+ omitted_routes: Math.max(0, items.length - 20),
63830
+ coverage: "retained_samples"
63831
+ };
63832
+ }
63833
+
63834
+ // ../../packages/shared-types/src/browser-resource-context.ts
63835
+ var BrowserResourceContextSchema = external_exports.object({
63836
+ version: external_exports.literal(1),
63837
+ host: external_exports.string().max(255).nullable(),
63838
+ path: external_exports.string().max(1024),
63839
+ type: external_exports.string().nullable(),
63840
+ first_party: external_exports.boolean().nullable(),
63841
+ role: external_exports.enum([
63842
+ "analytics",
63843
+ "advertising",
63844
+ "tag_manager",
63845
+ "authentication",
63846
+ "application_asset",
63847
+ "unknown"
63848
+ ]),
63849
+ provider: external_exports.string().nullable(),
63850
+ title: external_exports.string(),
63851
+ optional_candidate: external_exports.boolean(),
63852
+ diagnosis: external_exports.string(),
63853
+ routes: BrowserResourceRoutesSchema
63854
+ });
63855
+
63789
63856
  // ../../packages/shared-types/src/tier-capabilities.ts
63790
63857
  var TIER_CAPABILITIES = {
63791
63858
  free: {
@@ -64620,6 +64687,93 @@ var CaptureRulesFileSchema = external_exports.object({
64620
64687
  rules: external_exports.array(CaptureRuleSchema)
64621
64688
  });
64622
64689
 
64690
+ // ../../packages/shared-types/src/browser-resource.ts
64691
+ function record(value) {
64692
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
64693
+ }
64694
+ function httpUrl(value, base2) {
64695
+ if (typeof value !== "string" || value.length === 0 || value.length > 4096 || /[\u0000-\u0020\u007f]/.test(value))
64696
+ return null;
64697
+ try {
64698
+ const url = base2 === void 0 ? new URL(value) : new URL(value, base2);
64699
+ return url.protocol === "http:" || url.protocol === "https:" ? url : null;
64700
+ } catch {
64701
+ return null;
64702
+ }
64703
+ }
64704
+ function browserResourceLocation(source, page) {
64705
+ if (typeof source !== "string") return null;
64706
+ const pageUrl = httpUrl(page);
64707
+ const url = httpUrl(source, pageUrl ?? void 0);
64708
+ if (url !== null) {
64709
+ if (url.pathname.length > 1024 || url.hostname.length > 255) return null;
64710
+ return {
64711
+ host: url.hostname.toLowerCase(),
64712
+ path: url.pathname || "/",
64713
+ first_party: pageUrl === null ? null : url.origin === pageUrl.origin
64714
+ };
64715
+ }
64716
+ if (source.startsWith("/") && !source.startsWith("//") && !source.includes("\\") && !/[\u0000-\u0020\u007f]/.test(source)) {
64717
+ const path = source.split(/[?#]/, 1)[0];
64718
+ if (path.length <= 1024) return { host: null, path, first_party: true };
64719
+ }
64720
+ return null;
64721
+ }
64722
+ function describeBrowserResource(value) {
64723
+ const event = record(value);
64724
+ if (event?.["kind"] !== "resource_error") return null;
64725
+ const target = record(event["target"]);
64726
+ const page = record(event["page"]);
64727
+ const location = browserResourceLocation(
64728
+ target?.["source_url"] ?? event["file_name"],
64729
+ page?.["url"]
64730
+ );
64731
+ if (location === null) return null;
64732
+ const tag2 = typeof target?.["tag_name"] === "string" ? target["tag_name"].toLowerCase() : null;
64733
+ const type = tag2 !== null && [
64734
+ "script",
64735
+ "link",
64736
+ "img",
64737
+ "audio",
64738
+ "video",
64739
+ "source",
64740
+ "iframe",
64741
+ "object",
64742
+ "embed",
64743
+ "input"
64744
+ ].includes(tag2) ? tag2 : null;
64745
+ let role = location.first_party === true ? "application_asset" : "unknown";
64746
+ let provider = null;
64747
+ if (type === "script") {
64748
+ if (location.host === "www.googletagmanager.com" && location.path === "/gtm.js") {
64749
+ provider = "Google Tag Manager";
64750
+ role = "tag_manager";
64751
+ } else if (location.host === "connect.facebook.net" && /^\/[a-z]{2}_[A-Z]{2}\/fbevents\.js$/.test(location.path)) {
64752
+ provider = "Meta Pixel";
64753
+ role = "advertising";
64754
+ } else if (location.host === "www.clarity.ms" && /^\/tag\/[A-Za-z0-9]+$/.test(location.path)) {
64755
+ provider = "Microsoft Clarity";
64756
+ role = "analytics";
64757
+ } else if (location.host === "accounts.google.com" && location.path === "/gsi/client") {
64758
+ provider = "Google sign-in";
64759
+ role = "authentication";
64760
+ }
64761
+ }
64762
+ const file = location.path.split("/").filter(Boolean).at(-1) ?? location.path;
64763
+ const kind = type === "img" ? "Image" : type === "script" || /\.m?js$/i.test(file) ? "JavaScript asset" : "Resource";
64764
+ return {
64765
+ ...location,
64766
+ type,
64767
+ role,
64768
+ provider,
64769
+ title: provider !== null ? `${provider} script failed to load` : `${kind} failed to load: ${file}${location.host === null ? "" : ` (${location.host})`}`,
64770
+ optional_candidate: event["opaque"] === true && location.first_party !== true && ["analytics", "advertising", "tag_manager"].includes(role)
64771
+ };
64772
+ }
64773
+ function browserResourceDiagnosis(resource) {
64774
+ return resource?.optional_candidate === true ? "The browser reported that this script failed to load. Possibly blocked by privacy tools; network, CSP or provider failures are also possible. The captured event does not identify the cause." : "The browser reported that this resource failed to load. The captured event does not identify the cause or establish whether application functionality was affected.";
64775
+ }
64776
+
64623
64777
  // ../../packages/shared-types/src/capture-rule-evaluation.ts
64624
64778
  var CaptureRuleEvaluationUrlSchema = external_exports.object({
64625
64779
  host: external_exports.string().min(1).transform((value) => value.toLowerCase()).optional(),
@@ -64687,6 +64841,7 @@ var CaptureRuleSuggestionSchema = external_exports.object({
64687
64841
  rule: CaptureRuleCreateSchema
64688
64842
  });
64689
64843
  var CaptureRuleSuggestionsResponseSchema = external_exports.object({
64844
+ access_mode: external_exports.enum(["manage", "preview"]).optional(),
64690
64845
  suggestions: external_exports.array(CaptureRuleSuggestionSchema),
64691
64846
  bundle_status: external_exports.enum(["ready", "pending", "failed"]).optional(),
64692
64847
  bundle_reason: external_exports.string().nullable().optional()
@@ -65182,6 +65337,14 @@ function looksSensitiveCustomDimensionValue(value) {
65182
65337
  return /https?:\/\//i.test(value) || /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value) || /\bBearer\s+[A-Za-z0-9._~+/=-]+/i.test(value) || /\b(?:token|password|secret|api_key)=/i.test(value);
65183
65338
  }
65184
65339
 
65340
+ // ../../packages/shared-types/src/frontend-severity.ts
65341
+ function inferFrontendExceptionSeverity(event) {
65342
+ const browserEvent = event.payload.browser_event;
65343
+ if (browserEvent?.opaque === true)
65344
+ return browserEvent.kind === "resource_error" ? "medium" : "low";
65345
+ return "high";
65346
+ }
65347
+
65185
65348
  // ../../packages/shared-types/src/analytics-product.ts
65186
65349
  var AnalyticsOpportunityStatusValues = ["open", "resolved", "snoozed"];
65187
65350
  var AnalyticsOpportunityStatusSchema = external_exports.enum(AnalyticsOpportunityStatusValues);
@@ -65756,6 +65919,7 @@ var ContextDeviceSchema = external_exports.object({
65756
65919
  color_scheme_preference: external_exports.enum(["light", "dark", "no-preference"]).nullable()
65757
65920
  });
65758
65921
  var BundleContextSchema = external_exports.object({
65922
+ resource_failure: BrowserResourceContextSchema.optional(),
65759
65923
  error: ContextErrorSchema.nullable().optional(),
65760
65924
  request: ContextRequestSchema.nullable().optional(),
65761
65925
  response: ContextResponseSchema.nullable().optional(),
@@ -65980,6 +66144,15 @@ function buildSkill() {
65980
66144
  "- Scope frontend noise by structured evidence such as service, environment, `browser_event_kind`, `browser_event_opaque`, `client_kind`, `bot_family`, and message fields. Do not broadly demote generic `Unhandled promise rejection` incidents without bot-scoped or otherwise narrow evidence.",
65981
66145
  "- For expected or intentionally promoted 4xx responses on known routes, use capture-policy client-error path rules instead of promoting all client errors: `debugbundle capture-policy set --client-error-path-rule <status=/path/*@GET>`.",
65982
66146
  "",
66147
+ "### Browser resource failures",
66148
+ "",
66149
+ "- Inspect the primary failure and routes: one resource can span pages, route coverage may be incomplete, and historical incidents stay separate. Related tracker evidence does not explain an application exception.",
66150
+ '- If the cause is unknown, say "possibly blocked by privacy tools." Network/CSP/provider failures remain possible; provider recognition proves neither Pi-hole blocking nor optionality.',
66151
+ "- Review exact host/path, service, environment and opaque resource-error scope; never widen to a whole host. Google sign-in, app assets and unknown dependencies have no automatic resource noise recommendation.",
66152
+ "- For confirmed optional dependencies, context (demote) retains diagnostics without new incidents/alerts/automation and may remain billable. Drop discards future matches; choose it only when evidence has no diagnostic value. Explain the tradeoff; neither deletes history. Use the returned suggestion ID.",
66153
+ "- Check existing/disabled rules. Empty suggestions or pending/failed bundles do not justify broader rules. Applying requires user authorization and owner/admin access; preview is read-only. Verify subsequent matching and protected captures before claiming improvement; live tests need authorization.",
66154
+ "- The official OpenAI connection cannot suggest or apply rules. When it is the only connection, review returned evidence and hand off through a returned safe dashboard URL; never invent tools or switch credentials.",
66155
+ "",
65983
66156
  "## Notification Delivery",
65984
66157
  "",
65985
66158
  "When notification or automation delivery is the reported failure, inspect configuration and delivery records before changing incident logic.",
@@ -66087,6 +66260,7 @@ function buildCliReference() {
66087
66260
  "- `debugbundle capture-policy set [--project <id>] --client-error-path-rule <404=/path/*@GET,POST> [--json]`",
66088
66261
  "",
66089
66262
  "Use capture-rule suggestions for repeated operational noise after inspecting an incident bundle. Use capture-policy client-error path rules for route-scoped 4xx incidents instead of promoting all client errors.",
66263
+ "For browser resource failures, follow the skill's Browser resource failures guidance. Use the current response's suggestion ID, inspect `bundle_status`, `access_mode` and existing-rule state, and review the complete matcher before applying.",
66090
66264
  "",
66091
66265
  "## Probes",
66092
66266
  "",
@@ -66235,6 +66409,7 @@ function buildMcpReference() {
66235
66409
  "- `get_capture_policy`, `update_capture_policy` \u2014 review or update capture policy, including path-scoped client-error incident rules.",
66236
66410
  "",
66237
66411
  "Use these tools for repeated low-value operational noise only after inspecting incident evidence. Keep frontend suppression scoped by structured browser and client signals, and use path-scoped capture policy for known 4xx routes.",
66412
+ "For browser resource failures, follow the skill's Browser resource failures guidance and use the suggestion ID returned by the server. These management tools belong to the member-authenticated MCP surface; the official OpenAI connection is read-only and cannot suggest or apply capture rules.",
66238
66413
  "",
66239
66414
  "## Product Analytics Tools",
66240
66415
  "",
@@ -66430,6 +66605,43 @@ function buildSkillEvals() {
66430
66605
  "Use capture-policy path rules for known route-scoped 4xx incidents."
66431
66606
  ]
66432
66607
  },
66608
+ {
66609
+ name: "browser_resource_noise_review",
66610
+ prompt: "GTM failed on four routes. The owner says analytics is optional and asks to keep useful diagnostics without repeated alerts. The suggestion response offers resource_context and resource_drop with exact host/path/service/environment matchers. Explain and apply the appropriate authorized choice.",
66611
+ expected_behavior: [
66612
+ "Inspect the primary failure and bounded route coverage; do not count each route as a separate application defect.",
66613
+ "Describe privacy blocking as possible, not proven; do not diagnose Pi-hole.",
66614
+ "Review the exact matcher and use the returned context suggestion ID; explain retained evidence and possible paid usage.",
66615
+ "Verify the returned rule without claiming historical incidents were removed or live noise reduction was already observed."
66616
+ ]
66617
+ },
66618
+ {
66619
+ name: "protected_resource_noise_review",
66620
+ prompt: "A checkout TypeError has related GTM evidence. Separate incidents show Google sign-in and an unknown script failing. The user asks whether ignoring all Google or third-party hosts would clean this up.",
66621
+ expected_behavior: [
66622
+ "Keep the primary checkout exception separate from related tracker evidence.",
66623
+ "Do not broaden rules to whole hosts or infer that sign-in, app assets or unknown dependencies are optional.",
66624
+ "Inspect evidence and available suggestions before proposing any narrowly scoped change."
66625
+ ]
66626
+ },
66627
+ {
66628
+ name: "unavailable_resource_noise_suggestions",
66629
+ prompt: "Resource suggestions are pending or empty, the current member has preview access, and a matching rule is disabled. The user asks why the resource still opens incidents.",
66630
+ expected_behavior: [
66631
+ "Explain the missing evidence and access limits without inventing a rule or bypassing authorization.",
66632
+ "Inspect the disabled rule; do not assume it is active, create a duplicate or silently enable it.",
66633
+ "Do not claim noise was reduced or resolve historical incidents based solely on a policy proposal."
66634
+ ]
66635
+ },
66636
+ {
66637
+ name: "readonly_resource_noise_handoff",
66638
+ prompt: "Only the official read-only OpenAI connection is available. The user asks to ignore recurring tracker failures.",
66639
+ expected_behavior: [
66640
+ "Inspect only available incident evidence; distinguish possible privacy blocking from a proven cause.",
66641
+ "Explain that this connection cannot fetch capture-rule suggestions or apply rules; do not invent a management tool or switch credentials.",
66642
+ "Use a returned safe dashboard URL for a reviewed handoff when available, and never claim a rule was applied."
66643
+ ]
66644
+ },
66433
66645
  {
66434
66646
  name: "operational_controls_guidance",
66435
66647
  prompt: "The user reports missing webhook deliveries and asks whether probes or alerts are available. Confirm the skill points the agent to the relevant operational controls and docs.",
@@ -84218,8 +84430,57 @@ var WINDOW_RETENTION_SECONDS = WINDOW_24H_SECONDS + WINDOW_1H_SECONDS;
84218
84430
  // ../../packages/storage/src/ingestion-rate-limiter.ts
84219
84431
  var import_ioredis3 = __toESM(require_built3(), 1);
84220
84432
 
84221
- // ../../packages/event-normalizer/src/index.ts
84222
- var import_node_crypto5 = require("node:crypto");
84433
+ // ../../packages/event-normalizer/src/fingerprints.ts
84434
+ var import_node_crypto4 = require("node:crypto");
84435
+ var FINGERPRINT_VERSION = "v2";
84436
+ var RESOURCE_FINGERPRINT_VERSION = "v3";
84437
+ function fingerprintVersion(event) {
84438
+ return event.resource_type != null ? RESOURCE_FINGERPRINT_VERSION : FINGERPRINT_VERSION;
84439
+ }
84440
+ function stableJson(value) {
84441
+ if (value === null || typeof value !== "object") return JSON.stringify(value);
84442
+ if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`;
84443
+ const object = value;
84444
+ return `{${Object.keys(object).sort().map((key) => `${JSON.stringify(key)}:${stableJson(object[key])}`).join(",")}}`;
84445
+ }
84446
+ function fingerprint(event) {
84447
+ const canonical = event.resource_type != null ? {
84448
+ // A concrete resource is the grouping unit, not proof of a common underlying cause.
84449
+ browser_event_kind: "resource_error",
84450
+ resource_host: event.resource_host,
84451
+ resource_path: event.resource_path,
84452
+ resource_type: event.resource_type,
84453
+ environment: event.environment,
84454
+ fingerprint_version: RESOURCE_FINGERPRINT_VERSION
84455
+ } : {
84456
+ // Preserve exact historical bytes for every non-resource event and explicit legacy normalization.
84457
+ error_type: event.error_type,
84458
+ normalized_message: event.normalized_message,
84459
+ top_frames: event.top_frames,
84460
+ route_template: event.route_template,
84461
+ browser_event_kind: event.browser_event_kind,
84462
+ resource_host: event.resource_host,
84463
+ resource_path: event.resource_path,
84464
+ http_method: event.http_method,
84465
+ http_status: event.http_status,
84466
+ environment: event.environment
84467
+ };
84468
+ return (0, import_node_crypto4.createHash)("sha256").update(stableJson(canonical)).digest("hex");
84469
+ }
84470
+ function inferMatchedFields(event) {
84471
+ if (event.resource_type != null)
84472
+ return ["environment", "browser_event_kind", "resource_host", "resource_path", "resource_type"];
84473
+ const fields = ["environment", "normalized_message"];
84474
+ if (event.error_type !== null) fields.push("error_type");
84475
+ if (event.route_template !== null) fields.push("route_template");
84476
+ if (event.top_frames.length > 0) fields.push("top_frames");
84477
+ if (event.browser_event_kind != null) fields.push("browser_event_kind");
84478
+ if (event.resource_host != null) fields.push("resource_host");
84479
+ if (event.resource_path != null) fields.push("resource_path");
84480
+ if (event.http_method !== null) fields.push("http_method");
84481
+ if (event.http_status !== null) fields.push("http_status");
84482
+ return fields;
84483
+ }
84223
84484
 
84224
84485
  // ../../packages/redaction/src/index.ts
84225
84486
  var DEFAULT_SENSITIVE_KEYS = [
@@ -84318,7 +84579,7 @@ function redact(payload2, options) {
84318
84579
  }
84319
84580
 
84320
84581
  // ../../packages/event-normalizer/src/mobile-event-compatibility.ts
84321
- var import_node_crypto4 = require("node:crypto");
84582
+ var import_node_crypto5 = require("node:crypto");
84322
84583
  var MOBILE_SDKS = /* @__PURE__ */ new Set(["@debugbundle/sdk-android", "@debugbundle/sdk-swift"]);
84323
84584
  function isRecord3(candidate) {
84324
84585
  return typeof candidate === "object" && candidate !== null && !Array.isArray(candidate);
@@ -84345,8 +84606,8 @@ function readInteger(candidate) {
84345
84606
  function readBoolean2(candidate) {
84346
84607
  return typeof candidate === "boolean" ? candidate : null;
84347
84608
  }
84348
- function readField(record, snakeCase, camelCase) {
84349
- return record[snakeCase] ?? record[camelCase];
84609
+ function readField(record2, snakeCase, camelCase) {
84610
+ return record2[snakeCase] ?? record2[camelCase];
84350
84611
  }
84351
84612
  function stableValue(candidate) {
84352
84613
  if (Array.isArray(candidate)) {
@@ -84360,7 +84621,7 @@ function stableValue(candidate) {
84360
84621
  );
84361
84622
  }
84362
84623
  function deterministicEventId(candidate) {
84363
- const digest2 = (0, import_node_crypto4.createHash)("sha256").update(JSON.stringify(stableValue(candidate))).digest("hex");
84624
+ const digest2 = (0, import_node_crypto5.createHash)("sha256").update(JSON.stringify(stableValue(candidate))).digest("hex");
84364
84625
  const chars = digest2.slice(0, 32).split("");
84365
84626
  chars[12] = "5";
84366
84627
  chars[16] = ["8", "9", "a", "b"][Number.parseInt(chars[16] ?? "0", 16) % 4] ?? "8";
@@ -84726,7 +84987,6 @@ function normalizeJavaTimerMessage(message3) {
84726
84987
  }
84727
84988
 
84728
84989
  // ../../packages/event-normalizer/src/index.ts
84729
- var FINGERPRINT_VERSION = "v2";
84730
84990
  var PAYLOAD_ALLOWED_KEYS = {
84731
84991
  backend_exception: /* @__PURE__ */ new Set([
84732
84992
  "name",
@@ -84909,34 +85169,6 @@ function normalizeCompatibleEventCandidate(candidate) {
84909
85169
  }
84910
85170
  return event;
84911
85171
  }
84912
- function inferMatchedFields(event) {
84913
- const matchedFields = ["environment", "normalized_message"];
84914
- if (event.error_type !== null) {
84915
- matchedFields.push("error_type");
84916
- }
84917
- if (event.route_template !== null) {
84918
- matchedFields.push("route_template");
84919
- }
84920
- if (event.top_frames.length > 0) {
84921
- matchedFields.push("top_frames");
84922
- }
84923
- if (event.browser_event_kind != null) {
84924
- matchedFields.push("browser_event_kind");
84925
- }
84926
- if (event.resource_host != null) {
84927
- matchedFields.push("resource_host");
84928
- }
84929
- if (event.resource_path != null) {
84930
- matchedFields.push("resource_path");
84931
- }
84932
- if (event.http_method !== null) {
84933
- matchedFields.push("http_method");
84934
- }
84935
- if (event.http_status !== null) {
84936
- matchedFields.push("http_status");
84937
- }
84938
- return matchedFields;
84939
- }
84940
85172
  var UUID_PATTERN = /\b[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}\b/gi;
84941
85173
  var EMAIL_PATTERN = /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi;
84942
85174
  var ISO_TIMESTAMP_PATTERN = /\b\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z\b/g;
@@ -85140,22 +85372,10 @@ function normalizeResourceIdentity(value) {
85140
85372
  };
85141
85373
  }
85142
85374
  }
85143
- function stableJson(value) {
85144
- if (value === null || typeof value !== "object") {
85145
- return JSON.stringify(value);
85146
- }
85147
- if (Array.isArray(value)) {
85148
- return `[${value.map((entry) => stableJson(entry)).join(",")}]`;
85149
- }
85150
- const record = value;
85151
- const keys2 = Object.keys(record).sort();
85152
- const pairs = keys2.map((key) => `${JSON.stringify(key)}:${stableJson(record[key])}`);
85153
- return `{${pairs.join(",")}}`;
85154
- }
85155
85375
  function validateEvent(candidate) {
85156
85376
  return EventEnvelopeSchema.safeParse(normalizeCompatibleEventCandidate(candidate));
85157
85377
  }
85158
- function normalizeEvent(event, version2 = FINGERPRINT_VERSION) {
85378
+ function normalizeEvent(event, version2 = RESOURCE_FINGERPRINT_VERSION) {
85159
85379
  const redactedPayload = redact(event.payload).redacted;
85160
85380
  if (event.event_type === "backend_exception") {
85161
85381
  return {
@@ -85209,6 +85429,8 @@ function normalizeEvent(event, version2 = FINGERPRINT_VERSION) {
85209
85429
  const browserEvent = event.payload.browser_event;
85210
85430
  const resourceIdentity = browserEvent?.kind === "resource_error" ? normalizeResourceIdentity(browserEvent.target?.source_url ?? browserEvent.file_name) : { host: null, path: null };
85211
85431
  const topFrames = browserEvent?.opaque === true ? [] : selectTopFrames(event.payload.stack);
85432
+ const resource = describeBrowserResource(browserEvent);
85433
+ const concreteResource = version2 === RESOURCE_FINGERPRINT_VERSION && browserEvent?.opaque === true && resource?.host != null && resource.type !== null;
85212
85434
  return {
85213
85435
  event_type: event.event_type,
85214
85436
  environment: event.service.environment,
@@ -85219,8 +85441,10 @@ function normalizeEvent(event, version2 = FINGERPRINT_VERSION) {
85219
85441
  http_status: null,
85220
85442
  top_frames: topFrames,
85221
85443
  browser_event_kind: browserEvent?.kind ?? null,
85222
- resource_host: resourceIdentity.host,
85223
- resource_path: resourceIdentity.path,
85444
+ resource_host: concreteResource ? resource.host : resourceIdentity.host,
85445
+ resource_path: concreteResource ? resource.path : resourceIdentity.path,
85446
+ ...concreteResource ? { resource_type: resource.type } : {},
85447
+ ...resource === null ? {} : { incident_title: resource.title },
85224
85448
  payload: redactedPayload
85225
85449
  };
85226
85450
  }
@@ -85239,21 +85463,6 @@ function normalizeEvent(event, version2 = FINGERPRINT_VERSION) {
85239
85463
  payload: redactedPayload
85240
85464
  };
85241
85465
  }
85242
- function fingerprint(event) {
85243
- const canonical = {
85244
- error_type: event.error_type,
85245
- normalized_message: event.normalized_message,
85246
- top_frames: event.top_frames,
85247
- route_template: event.route_template,
85248
- browser_event_kind: event.browser_event_kind,
85249
- resource_host: event.resource_host,
85250
- resource_path: event.resource_path,
85251
- http_method: event.http_method,
85252
- http_status: event.http_status,
85253
- environment: event.environment
85254
- };
85255
- return (0, import_node_crypto5.createHash)("sha256").update(stableJson(canonical)).digest("hex");
85256
- }
85257
85466
  var INCIDENT_LOG_LEVELS = /* @__PURE__ */ new Set(["error", "fatal", "critical"]);
85258
85467
  function getRequestResponseStatus(payload2) {
85259
85468
  const status = payload2?.["response_status"];
@@ -86080,6 +86289,192 @@ var OPENAI_OAUTH_BOOTSTRAP_STATEMENTS = [
86080
86289
  `CREATE INDEX oauth_provider_artifacts_expires_idx ON oauth_provider_artifacts (expires_at, model, provider_id_hash)`
86081
86290
  ];
86082
86291
 
86292
+ // ../../packages/storage/src/storage-bootstrap-incident-statements.ts
86293
+ var STORAGE_BOOTSTRAP_INCIDENT_STATEMENTS = [
86294
+ `
86295
+ CREATE TABLE incidents (
86296
+ id uuid PRIMARY KEY,
86297
+ project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
86298
+ service_id uuid REFERENCES services(id) ON DELETE SET NULL,
86299
+ environment text NOT NULL DEFAULT 'production',
86300
+ fingerprint text NOT NULL,
86301
+ fingerprint_version text NOT NULL DEFAULT 'v1',
86302
+ title text NOT NULL,
86303
+ severity text NOT NULL,
86304
+ status text NOT NULL DEFAULT 'open',
86305
+ first_seen_at timestamptz NOT NULL,
86306
+ last_seen_at timestamptz NOT NULL,
86307
+ occurrence_count integer NOT NULL DEFAULT 1,
86308
+ matched_fields text[],
86309
+ created_at timestamptz NOT NULL DEFAULT now(),
86310
+ updated_at timestamptz NOT NULL DEFAULT now(),
86311
+ regressed_at timestamptz,
86312
+ spike_detected_at timestamptz,
86313
+ frequency_occurrences_1m integer,
86314
+ frequency_occurrences_5m integer,
86315
+ frequency_occurrences_1h integer,
86316
+ frequency_occurrences_24h integer,
86317
+ frequency_baseline_1h_per_5m double precision,
86318
+ frequency_spike_ratio_5m_to_1h double precision,
86319
+ frequency_has_sufficient_baseline boolean,
86320
+ frequency_is_spiking boolean,
86321
+ frequency_snapshot_at timestamptz,
86322
+ latest_deployment_id uuid REFERENCES deployments(id) ON DELETE SET NULL,
86323
+ bundle_generation_number integer NOT NULL DEFAULT 0,
86324
+ bundle_created_at timestamptz,
86325
+ bundle_updated_at timestamptz,
86326
+ bundle_source_event_id uuid,
86327
+ bundle_source_occurred_at timestamptz,
86328
+ bundle_trigger text,
86329
+ bundle_failure_reason text,
86330
+ resolved_at timestamptz,
86331
+ resolved_by_member_id uuid REFERENCES users(id) ON DELETE SET NULL,
86332
+ UNIQUE (project_id, environment, service_id, fingerprint)
86333
+ )
86334
+ `,
86335
+ `
86336
+ CREATE TABLE processed_events (
86337
+ event_id uuid PRIMARY KEY,
86338
+ project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
86339
+ event_type text NOT NULL,
86340
+ fingerprint text NOT NULL,
86341
+ normalized_message text NOT NULL,
86342
+ processed_at timestamptz NOT NULL DEFAULT now()
86343
+ )
86344
+ `,
86345
+ `
86346
+ CREATE TABLE improvement_opportunities (
86347
+ id uuid PRIMARY KEY,
86348
+ project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
86349
+ service_id uuid REFERENCES services(id) ON DELETE SET NULL,
86350
+ service_name text NOT NULL,
86351
+ environment text NOT NULL DEFAULT 'production',
86352
+ kind text NOT NULL,
86353
+ status text NOT NULL DEFAULT 'open',
86354
+ severity text NOT NULL,
86355
+ confidence numeric NOT NULL,
86356
+ fingerprint text NOT NULL,
86357
+ title text NOT NULL,
86358
+ summary text NOT NULL,
86359
+ occurrence_count integer NOT NULL DEFAULT 1,
86360
+ evidence jsonb NOT NULL,
86361
+ first_detected_at timestamptz NOT NULL,
86362
+ last_detected_at timestamptz NOT NULL,
86363
+ last_source_event_id uuid,
86364
+ related_incident_ids uuid[] NOT NULL DEFAULT '{}',
86365
+ bundle_generation_number integer NOT NULL DEFAULT 0,
86366
+ bundle_created_at timestamptz,
86367
+ bundle_updated_at timestamptz,
86368
+ bundle_source_event_id uuid,
86369
+ bundle_failure_reason text,
86370
+ resolved_at timestamptz,
86371
+ resolved_by_user_id uuid REFERENCES users(id) ON DELETE SET NULL,
86372
+ snoozed_until timestamptz,
86373
+ created_at timestamptz NOT NULL DEFAULT now(),
86374
+ updated_at timestamptz NOT NULL DEFAULT now(),
86375
+ UNIQUE (project_id, fingerprint)
86376
+ )
86377
+ `,
86378
+ `
86379
+ CREATE INDEX improvement_opportunities_project_status_detected_idx
86380
+ ON improvement_opportunities (project_id, status, last_detected_at DESC)
86381
+ `,
86382
+ `
86383
+ CREATE INDEX improvement_opportunities_project_kind_detected_idx
86384
+ ON improvement_opportunities (project_id, kind, last_detected_at DESC)
86385
+ `,
86386
+ `
86387
+ CREATE INDEX improvement_opportunities_project_service_env_idx
86388
+ ON improvement_opportunities (project_id, service_id, environment)
86389
+ `,
86390
+ `
86391
+ CREATE TABLE improvement_opportunity_events (
86392
+ improvement_opportunity_id uuid NOT NULL REFERENCES improvement_opportunities(id) ON DELETE CASCADE,
86393
+ event_id uuid NOT NULL,
86394
+ event_type text NOT NULL,
86395
+ occurred_at timestamptz NOT NULL,
86396
+ PRIMARY KEY (improvement_opportunity_id, event_id)
86397
+ )
86398
+ `,
86399
+ `
86400
+ CREATE INDEX improvement_opportunity_events_detected_idx
86401
+ ON improvement_opportunity_events (improvement_opportunity_id, occurred_at DESC, event_id DESC)
86402
+ `,
86403
+ `
86404
+ CREATE TABLE bundle_generations (
86405
+ id uuid PRIMARY KEY,
86406
+ project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
86407
+ incident_id uuid REFERENCES incidents(id) ON DELETE CASCADE,
86408
+ improvement_opportunity_id uuid REFERENCES improvement_opportunities(id) ON DELETE CASCADE,
86409
+ bundle_type text NOT NULL,
86410
+ generation_number integer NOT NULL,
86411
+ source_event_id uuid NOT NULL,
86412
+ source_occurred_at timestamptz NOT NULL,
86413
+ trigger text NOT NULL,
86414
+ created_at timestamptz NOT NULL,
86415
+ updated_at timestamptz NOT NULL,
86416
+ CHECK (
86417
+ (incident_id IS NOT NULL AND improvement_opportunity_id IS NULL AND bundle_type = 'failure')
86418
+ OR (incident_id IS NULL AND improvement_opportunity_id IS NOT NULL AND bundle_type = 'improvement')
86419
+ )
86420
+ )
86421
+ `,
86422
+ `
86423
+ CREATE UNIQUE INDEX bundle_generations_incident_source_idx
86424
+ ON bundle_generations (incident_id, source_event_id)
86425
+ WHERE incident_id IS NOT NULL
86426
+ `,
86427
+ `
86428
+ CREATE UNIQUE INDEX bundle_generations_improvement_source_idx
86429
+ ON bundle_generations (improvement_opportunity_id, source_event_id)
86430
+ WHERE improvement_opportunity_id IS NOT NULL
86431
+ `,
86432
+ `
86433
+ CREATE INDEX bundle_generations_project_created_idx
86434
+ ON bundle_generations (project_id, created_at DESC, bundle_type)
86435
+ `,
86436
+ `
86437
+ CREATE INDEX bundle_generations_incident_generation_idx
86438
+ ON bundle_generations (incident_id, generation_number DESC)
86439
+ `,
86440
+ `
86441
+ CREATE INDEX bundle_generations_improvement_generation_idx
86442
+ ON bundle_generations (improvement_opportunity_id, generation_number DESC)
86443
+ WHERE improvement_opportunity_id IS NOT NULL
86444
+ `,
86445
+ `
86446
+ CREATE TABLE incident_events (
86447
+ incident_id uuid NOT NULL REFERENCES incidents(id) ON DELETE CASCADE,
86448
+ event_id uuid NOT NULL,
86449
+ event_type text NOT NULL,
86450
+ event_class text NOT NULL DEFAULT 'context_signal',
86451
+ occurred_at timestamptz NOT NULL,
86452
+ is_sampled boolean NOT NULL DEFAULT false,
86453
+ level text,
86454
+ resource_route text,
86455
+ retain_first boolean NOT NULL DEFAULT false,
86456
+ retain_latest boolean NOT NULL DEFAULT false,
86457
+ retain_after_deploy boolean NOT NULL DEFAULT false,
86458
+ retain_highest_severity boolean NOT NULL DEFAULT false,
86459
+ retain_deploy_metadata boolean NOT NULL DEFAULT false,
86460
+ severity_rank integer NOT NULL DEFAULT 0,
86461
+ PRIMARY KEY (incident_id, event_id)
86462
+ )
86463
+ `,
86464
+ `
86465
+ CREATE INDEX incident_events_incident_occurred_event_idx
86466
+ ON incident_events (incident_id, occurred_at DESC, event_id DESC)
86467
+ `,
86468
+ `
86469
+ CREATE INDEX incident_events_incident_level_occurred_event_idx
86470
+ ON incident_events (incident_id, level, occurred_at DESC, event_id DESC)
86471
+ `,
86472
+ `
86473
+ CREATE INDEX incident_events_incident_sampled_idx
86474
+ ON incident_events (incident_id, is_sampled, occurred_at ASC, event_id ASC)
86475
+ `
86476
+ ];
86477
+
86083
86478
  // ../../packages/storage/src/storage-bootstrap-account-analytics-statements.ts
86084
86479
  var STORAGE_BOOTSTRAP_ACCOUNT_ANALYTICS_STATEMENTS = [
86085
86480
  `
@@ -86600,187 +86995,7 @@ var STORAGE_BOOTSTRAP_STATEMENTS = [
86600
86995
  CREATE INDEX deployments_project_service_env_deployed_idx
86601
86996
  ON deployments (project_id, service_id, environment, deployed_at DESC)
86602
86997
  `,
86603
- `
86604
- CREATE TABLE incidents (
86605
- id uuid PRIMARY KEY,
86606
- project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
86607
- service_id uuid REFERENCES services(id) ON DELETE SET NULL,
86608
- environment text NOT NULL DEFAULT 'production',
86609
- fingerprint text NOT NULL,
86610
- fingerprint_version text NOT NULL DEFAULT 'v1',
86611
- title text NOT NULL,
86612
- severity text NOT NULL,
86613
- status text NOT NULL DEFAULT 'open',
86614
- first_seen_at timestamptz NOT NULL,
86615
- last_seen_at timestamptz NOT NULL,
86616
- occurrence_count integer NOT NULL DEFAULT 1,
86617
- matched_fields text[],
86618
- created_at timestamptz NOT NULL DEFAULT now(),
86619
- updated_at timestamptz NOT NULL DEFAULT now(),
86620
- regressed_at timestamptz,
86621
- spike_detected_at timestamptz,
86622
- frequency_occurrences_1m integer,
86623
- frequency_occurrences_5m integer,
86624
- frequency_occurrences_1h integer,
86625
- frequency_occurrences_24h integer,
86626
- frequency_baseline_1h_per_5m double precision,
86627
- frequency_spike_ratio_5m_to_1h double precision,
86628
- frequency_has_sufficient_baseline boolean,
86629
- frequency_is_spiking boolean,
86630
- frequency_snapshot_at timestamptz,
86631
- latest_deployment_id uuid REFERENCES deployments(id) ON DELETE SET NULL,
86632
- bundle_generation_number integer NOT NULL DEFAULT 0,
86633
- bundle_created_at timestamptz,
86634
- bundle_updated_at timestamptz,
86635
- bundle_source_event_id uuid,
86636
- bundle_source_occurred_at timestamptz,
86637
- bundle_trigger text,
86638
- bundle_failure_reason text,
86639
- resolved_at timestamptz,
86640
- resolved_by_member_id uuid REFERENCES users(id) ON DELETE SET NULL,
86641
- UNIQUE (project_id, environment, service_id, fingerprint)
86642
- )
86643
- `,
86644
- `
86645
- CREATE TABLE processed_events (
86646
- event_id uuid PRIMARY KEY,
86647
- project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
86648
- event_type text NOT NULL,
86649
- fingerprint text NOT NULL,
86650
- normalized_message text NOT NULL,
86651
- processed_at timestamptz NOT NULL DEFAULT now()
86652
- )
86653
- `,
86654
- `
86655
- CREATE TABLE improvement_opportunities (
86656
- id uuid PRIMARY KEY,
86657
- project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
86658
- service_id uuid REFERENCES services(id) ON DELETE SET NULL,
86659
- service_name text NOT NULL,
86660
- environment text NOT NULL DEFAULT 'production',
86661
- kind text NOT NULL,
86662
- status text NOT NULL DEFAULT 'open',
86663
- severity text NOT NULL,
86664
- confidence numeric NOT NULL,
86665
- fingerprint text NOT NULL,
86666
- title text NOT NULL,
86667
- summary text NOT NULL,
86668
- occurrence_count integer NOT NULL DEFAULT 1,
86669
- evidence jsonb NOT NULL,
86670
- first_detected_at timestamptz NOT NULL,
86671
- last_detected_at timestamptz NOT NULL,
86672
- last_source_event_id uuid,
86673
- related_incident_ids uuid[] NOT NULL DEFAULT '{}',
86674
- bundle_generation_number integer NOT NULL DEFAULT 0,
86675
- bundle_created_at timestamptz,
86676
- bundle_updated_at timestamptz,
86677
- bundle_source_event_id uuid,
86678
- bundle_failure_reason text,
86679
- resolved_at timestamptz,
86680
- resolved_by_user_id uuid REFERENCES users(id) ON DELETE SET NULL,
86681
- snoozed_until timestamptz,
86682
- created_at timestamptz NOT NULL DEFAULT now(),
86683
- updated_at timestamptz NOT NULL DEFAULT now(),
86684
- UNIQUE (project_id, fingerprint)
86685
- )
86686
- `,
86687
- `
86688
- CREATE INDEX improvement_opportunities_project_status_detected_idx
86689
- ON improvement_opportunities (project_id, status, last_detected_at DESC)
86690
- `,
86691
- `
86692
- CREATE INDEX improvement_opportunities_project_kind_detected_idx
86693
- ON improvement_opportunities (project_id, kind, last_detected_at DESC)
86694
- `,
86695
- `
86696
- CREATE INDEX improvement_opportunities_project_service_env_idx
86697
- ON improvement_opportunities (project_id, service_id, environment)
86698
- `,
86699
- `
86700
- CREATE TABLE improvement_opportunity_events (
86701
- improvement_opportunity_id uuid NOT NULL REFERENCES improvement_opportunities(id) ON DELETE CASCADE,
86702
- event_id uuid NOT NULL,
86703
- event_type text NOT NULL,
86704
- occurred_at timestamptz NOT NULL,
86705
- PRIMARY KEY (improvement_opportunity_id, event_id)
86706
- )
86707
- `,
86708
- `
86709
- CREATE INDEX improvement_opportunity_events_detected_idx
86710
- ON improvement_opportunity_events (improvement_opportunity_id, occurred_at DESC, event_id DESC)
86711
- `,
86712
- `
86713
- CREATE TABLE bundle_generations (
86714
- id uuid PRIMARY KEY,
86715
- project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
86716
- incident_id uuid REFERENCES incidents(id) ON DELETE CASCADE,
86717
- improvement_opportunity_id uuid REFERENCES improvement_opportunities(id) ON DELETE CASCADE,
86718
- bundle_type text NOT NULL,
86719
- generation_number integer NOT NULL,
86720
- source_event_id uuid NOT NULL,
86721
- source_occurred_at timestamptz NOT NULL,
86722
- trigger text NOT NULL,
86723
- created_at timestamptz NOT NULL,
86724
- updated_at timestamptz NOT NULL,
86725
- CHECK (
86726
- (incident_id IS NOT NULL AND improvement_opportunity_id IS NULL AND bundle_type = 'failure')
86727
- OR (incident_id IS NULL AND improvement_opportunity_id IS NOT NULL AND bundle_type = 'improvement')
86728
- )
86729
- )
86730
- `,
86731
- `
86732
- CREATE UNIQUE INDEX bundle_generations_incident_source_idx
86733
- ON bundle_generations (incident_id, source_event_id)
86734
- WHERE incident_id IS NOT NULL
86735
- `,
86736
- `
86737
- CREATE UNIQUE INDEX bundle_generations_improvement_source_idx
86738
- ON bundle_generations (improvement_opportunity_id, source_event_id)
86739
- WHERE improvement_opportunity_id IS NOT NULL
86740
- `,
86741
- `
86742
- CREATE INDEX bundle_generations_project_created_idx
86743
- ON bundle_generations (project_id, created_at DESC, bundle_type)
86744
- `,
86745
- `
86746
- CREATE INDEX bundle_generations_incident_generation_idx
86747
- ON bundle_generations (incident_id, generation_number DESC)
86748
- `,
86749
- `
86750
- CREATE INDEX bundle_generations_improvement_generation_idx
86751
- ON bundle_generations (improvement_opportunity_id, generation_number DESC)
86752
- WHERE improvement_opportunity_id IS NOT NULL
86753
- `,
86754
- `
86755
- CREATE TABLE incident_events (
86756
- incident_id uuid NOT NULL REFERENCES incidents(id) ON DELETE CASCADE,
86757
- event_id uuid NOT NULL,
86758
- event_type text NOT NULL,
86759
- event_class text NOT NULL DEFAULT 'context_signal',
86760
- occurred_at timestamptz NOT NULL,
86761
- is_sampled boolean NOT NULL DEFAULT false,
86762
- level text,
86763
- retain_first boolean NOT NULL DEFAULT false,
86764
- retain_latest boolean NOT NULL DEFAULT false,
86765
- retain_after_deploy boolean NOT NULL DEFAULT false,
86766
- retain_highest_severity boolean NOT NULL DEFAULT false,
86767
- retain_deploy_metadata boolean NOT NULL DEFAULT false,
86768
- severity_rank integer NOT NULL DEFAULT 0,
86769
- PRIMARY KEY (incident_id, event_id)
86770
- )
86771
- `,
86772
- `
86773
- CREATE INDEX incident_events_incident_occurred_event_idx
86774
- ON incident_events (incident_id, occurred_at DESC, event_id DESC)
86775
- `,
86776
- `
86777
- CREATE INDEX incident_events_incident_level_occurred_event_idx
86778
- ON incident_events (incident_id, level, occurred_at DESC, event_id DESC)
86779
- `,
86780
- `
86781
- CREATE INDEX incident_events_incident_sampled_idx
86782
- ON incident_events (incident_id, is_sampled, occurred_at ASC, event_id ASC)
86783
- `,
86998
+ ...STORAGE_BOOTSTRAP_INCIDENT_STATEMENTS,
86784
86999
  `
86785
87000
  CREATE TABLE weekly_report_channels (
86786
87001
  id uuid PRIMARY KEY,
@@ -89369,7 +89584,12 @@ var LATE_STORAGE_SCHEMA_MIGRATIONS = [
89369
89584
  // ../../packages/storage/src/schema-migrations-catalog.ts
89370
89585
  var STORAGE_SCHEMA_MIGRATIONS = [
89371
89586
  ...EARLY_STORAGE_SCHEMA_MIGRATIONS,
89372
- ...LATE_STORAGE_SCHEMA_MIGRATIONS
89587
+ ...LATE_STORAGE_SCHEMA_MIGRATIONS,
89588
+ defineStorageSchemaMigration({
89589
+ id: "202609160001_add_browser_resource_routes",
89590
+ description: "Retain sanitized browser resource routes independently of raw event sampling.",
89591
+ statements: ["ALTER TABLE incident_events ADD COLUMN IF NOT EXISTS resource_route text"]
89592
+ })
89373
89593
  ];
89374
89594
 
89375
89595
  // ../../packages/storage/src/schema-migrations.ts
@@ -93745,6 +93965,27 @@ var import_node_crypto10 = require("node:crypto");
93745
93965
  var import_promises10 = require("node:fs/promises");
93746
93966
  var import_node_path11 = require("node:path");
93747
93967
 
93968
+ // ../../packages/bundle-engine/src/browser-resource-context.ts
93969
+ function buildBrowserResourceContext(browserEvent, incident, events2) {
93970
+ const resource = describeBrowserResource(browserEvent);
93971
+ if (resource === null) return void 0;
93972
+ const routes = [];
93973
+ for (const event of events2) {
93974
+ if (event.event_type !== "frontend_exception" || event.service.name !== incident.service_name || event.service.environment !== incident.environment)
93975
+ continue;
93976
+ const candidate = describeBrowserResource(event.payload.browser_event);
93977
+ if (candidate !== null && candidate.host === resource.host && candidate.path === resource.path && candidate.type === resource.type) {
93978
+ routes.push(event.payload.route ?? null);
93979
+ }
93980
+ }
93981
+ return {
93982
+ version: 1,
93983
+ ...resource,
93984
+ diagnosis: browserResourceDiagnosis(resource),
93985
+ routes: incident.resource_routes ?? summarizeResourceRoutes(routes, incident.occurrence_count)
93986
+ };
93987
+ }
93988
+
93748
93989
  // ../../packages/bundle-engine/src/deployment-context.ts
93749
93990
  function selectScopedDeployments(input3) {
93750
93991
  const cutoff = Date.parse(input3.occurredAt);
@@ -93830,9 +94071,9 @@ function stableJson2(value) {
93830
94071
  if (Array.isArray(value)) {
93831
94072
  return `[${value.map((entry) => stableJson2(entry)).join(",")}]`;
93832
94073
  }
93833
- const record = value;
93834
- const keys2 = Object.keys(record).sort();
93835
- return `{${keys2.map((key) => `${JSON.stringify(key)}:${stableJson2(record[key])}`).join(",")}}`;
94074
+ const record2 = value;
94075
+ const keys2 = Object.keys(record2).sort();
94076
+ return `{${keys2.map((key) => `${JSON.stringify(key)}:${stableJson2(record2[key])}`).join(",")}}`;
93836
94077
  }
93837
94078
  function buildFrontendBreadcrumbKey(input3) {
93838
94079
  return `${input3.breadcrumb_type}:${input3.ts}:${input3.route ?? ""}:${stableJson2(input3.data)}`;
@@ -94206,10 +94447,11 @@ function buildSummaryGuidance(input3) {
94206
94447
  }
94207
94448
  if (input3.opaqueBrowserError) {
94208
94449
  if (input3.browserEvent?.kind === "resource_error") {
94450
+ const resource = describeBrowserResource(input3.browserEvent);
94209
94451
  return {
94210
- likely_cause: "The browser reported a resource load error without a usable application stack.",
94452
+ likely_cause: browserResourceDiagnosis(resource),
94211
94453
  confidence: 0.35,
94212
- recommended_action: "Inspect the captured resource target, browser network failures, CSP rules, and cross-origin asset configuration."
94454
+ recommended_action: "Check the affected resource and routes, browser network failures, CSP and provider availability." + (resource?.optional_candidate === true ? " If the dependency is optional for your app, review a resource-scoped noise rule." : " Verify whether application functionality is affected.")
94213
94455
  };
94214
94456
  }
94215
94457
  return {
@@ -94483,8 +94725,10 @@ function buildBundle(input3) {
94483
94725
  errorContext,
94484
94726
  requestContext
94485
94727
  );
94486
- const browserEvent = getPrimaryBrowserExceptionEvent(sourceEnvelopes, primarySignalEnvelope);
94728
+ const browserEventCandidate = getPrimaryBrowserExceptionEvent(sourceEnvelopes, primarySignalEnvelope);
94729
+ const browserEvent = browserEventCandidate?.kind === "resource_error" && primarySignalEnvelope !== null && !isFrontendExceptionEnvelope(primarySignalEnvelope) ? null : browserEventCandidate;
94487
94730
  const opaqueBrowserError = isOpaqueBrowserError(errorContext, browserEvent);
94731
+ const resourceContext = buildBrowserResourceContext(browserEvent, input3.incident, sourceEnvelopes);
94488
94732
  const primarySignalType = primarySignalEnvelope !== null ? mapSignalType(primarySignalEnvelope.event_type) : inferSignalTypeFromSourceEventTypes(sourceEventTypes);
94489
94733
  const primarySourceEvent = errorContext?.name ?? sourceEventTypes[0] ?? "backend_exception";
94490
94734
  const firstSeenAt = new Date(input3.incident.first_seen_at).toISOString();
@@ -94558,6 +94802,7 @@ function buildBundle(input3) {
94558
94802
  regression_suspected: input3.job.trigger === "regression_reopen"
94559
94803
  },
94560
94804
  context: {
94805
+ ...resourceContext === void 0 ? {} : { resource_failure: resourceContext },
94561
94806
  error: errorContext,
94562
94807
  request: requestContext,
94563
94808
  response: responseContext,
@@ -94621,8 +94866,8 @@ function stableStringify(value) {
94621
94866
  function shellQuote(value) {
94622
94867
  return `'${value.replaceAll("'", `'\\''`)}'`;
94623
94868
  }
94624
- function sortRecordEntries(record) {
94625
- return Object.entries(record).sort(([left], [right]) => left.localeCompare(right));
94869
+ function sortRecordEntries(record2) {
94870
+ return Object.entries(record2).sort(([left], [right]) => left.localeCompare(right));
94626
94871
  }
94627
94872
  var REPLAY_HEADER_PRIORITY = [
94628
94873
  "authorization",
@@ -94927,6 +95172,159 @@ function buildReproduction(bundle) {
94927
95172
  };
94928
95173
  }
94929
95174
 
95175
+ // src/local-processing-state.ts
95176
+ var EVENT_TYPE_SET = new Set(EventTypeValues);
95177
+ function isEventType(value) {
95178
+ return typeof value === "string" && EVENT_TYPE_SET.has(value);
95179
+ }
95180
+ function parseIncidentState(candidate) {
95181
+ if (!isRecord(candidate)) {
95182
+ return null;
95183
+ }
95184
+ if (candidate["source"] !== "local" || candidate["status"] !== "open" && candidate["status"] !== "resolved") {
95185
+ return null;
95186
+ }
95187
+ const sourceEvents = candidate["source_events"];
95188
+ if (!Array.isArray(sourceEvents)) {
95189
+ return null;
95190
+ }
95191
+ const validatedSourceEvents = [];
95192
+ for (const sourceEvent of sourceEvents) {
95193
+ const validated = validateEvent(sourceEvent);
95194
+ if (!validated.success) {
95195
+ return null;
95196
+ }
95197
+ validatedSourceEvents.push(validated.data);
95198
+ }
95199
+ const matchedFields = candidate["matched_fields"];
95200
+ const sourceEventTypes = candidate["source_event_types"];
95201
+ if (!Array.isArray(matchedFields) || !matchedFields.every((value) => typeof value === "string")) {
95202
+ return null;
95203
+ }
95204
+ if (!Array.isArray(sourceEventTypes) || !sourceEventTypes.every(isEventType)) {
95205
+ return null;
95206
+ }
95207
+ const severity = candidate["severity"];
95208
+ if (severity !== "low" && severity !== "medium" && severity !== "high" && severity !== "critical") {
95209
+ return null;
95210
+ }
95211
+ const status = candidate["status"];
95212
+ if (status !== "open" && status !== "resolved") {
95213
+ return null;
95214
+ }
95215
+ const requiredStringKeys = [
95216
+ "incident_id",
95217
+ "project_id",
95218
+ "service_id",
95219
+ "service_name",
95220
+ "environment",
95221
+ "fingerprint",
95222
+ "fingerprint_version",
95223
+ "title",
95224
+ "first_seen_at",
95225
+ "last_seen_at",
95226
+ "source_event_id",
95227
+ "source_occurred_at",
95228
+ "bundle_path",
95229
+ "reproduction_path"
95230
+ ];
95231
+ for (const key of requiredStringKeys) {
95232
+ if (typeof candidate[key] !== "string") {
95233
+ return null;
95234
+ }
95235
+ }
95236
+ if (typeof candidate["occurrence_count"] !== "number" || typeof candidate["generation_number"] !== "number") {
95237
+ return null;
95238
+ }
95239
+ const serviceRuntime = candidate["service_runtime"];
95240
+ const serviceFramework = candidate["service_framework"];
95241
+ if (serviceRuntime !== null && typeof serviceRuntime !== "string") {
95242
+ return null;
95243
+ }
95244
+ if (serviceFramework !== null && typeof serviceFramework !== "string") {
95245
+ return null;
95246
+ }
95247
+ const incidentId = candidate["incident_id"];
95248
+ const projectId = candidate["project_id"];
95249
+ const serviceId = candidate["service_id"];
95250
+ const serviceName = candidate["service_name"];
95251
+ const environment = candidate["environment"];
95252
+ const incidentFingerprint = candidate["fingerprint"];
95253
+ const fingerprintVersion2 = candidate["fingerprint_version"];
95254
+ const title = candidate["title"];
95255
+ const firstSeenAt = candidate["first_seen_at"];
95256
+ const lastSeenAt = candidate["last_seen_at"];
95257
+ const occurrenceCount = candidate["occurrence_count"];
95258
+ const sourceEventId = candidate["source_event_id"];
95259
+ const sourceOccurredAt = candidate["source_occurred_at"];
95260
+ const bundlePath = candidate["bundle_path"];
95261
+ const reproductionPath = candidate["reproduction_path"];
95262
+ const generationNumber = candidate["generation_number"];
95263
+ const normalizedSourceEventTypes = [...sourceEventTypes].sort();
95264
+ return {
95265
+ incident_id: incidentId,
95266
+ source: "local",
95267
+ project_id: projectId,
95268
+ service_id: serviceId,
95269
+ service_name: serviceName,
95270
+ service_runtime: serviceRuntime,
95271
+ service_framework: serviceFramework,
95272
+ environment,
95273
+ fingerprint: incidentFingerprint,
95274
+ fingerprint_version: fingerprintVersion2,
95275
+ title,
95276
+ severity,
95277
+ status,
95278
+ first_seen_at: firstSeenAt,
95279
+ last_seen_at: lastSeenAt,
95280
+ occurrence_count: occurrenceCount,
95281
+ source_event_id: sourceEventId,
95282
+ source_occurred_at: sourceOccurredAt,
95283
+ source_event_types: normalizedSourceEventTypes,
95284
+ matched_fields: [...matchedFields].sort(),
95285
+ bundle_path: bundlePath,
95286
+ reproduction_path: reproductionPath,
95287
+ generation_number: generationNumber,
95288
+ source_events: validatedSourceEvents.sort(compareEventEnvelopes)
95289
+ };
95290
+ }
95291
+ function parseState(rawState) {
95292
+ const parsed = JSON.parse(rawState);
95293
+ if (!isRecord(parsed) || parsed["version"] !== 1) {
95294
+ return null;
95295
+ }
95296
+ const lastProcessedEventFile = parsed["last_processed_event_file"];
95297
+ if (lastProcessedEventFile !== null && typeof lastProcessedEventFile !== "string") {
95298
+ return null;
95299
+ }
95300
+ const incidents = parsed["incidents"];
95301
+ if (!isRecord(incidents)) {
95302
+ return null;
95303
+ }
95304
+ const parsedIncidents = {};
95305
+ for (const [incidentId, incidentValue] of Object.entries(incidents).sort(
95306
+ ([left], [right]) => left.localeCompare(right)
95307
+ )) {
95308
+ const incident = parseIncidentState(incidentValue);
95309
+ if (incident === null || incident.incident_id !== incidentId) {
95310
+ return null;
95311
+ }
95312
+ parsedIncidents[incidentId] = incident;
95313
+ }
95314
+ return {
95315
+ version: 1,
95316
+ last_processed_event_file: lastProcessedEventFile,
95317
+ incidents: parsedIncidents
95318
+ };
95319
+ }
95320
+ function compareEventEnvelopes(left, right) {
95321
+ const occurredAtComparison = left.occurred_at.localeCompare(right.occurred_at);
95322
+ if (occurredAtComparison !== 0) {
95323
+ return occurredAtComparison;
95324
+ }
95325
+ return left.event_id.localeCompare(right.event_id);
95326
+ }
95327
+
94930
95328
  // src/process-command.ts
94931
95329
  var LOCAL_EVENTS_DIRECTORY_PATH2 = ".debugbundle/local/events";
94932
95330
  var LOCAL_STATE_FILE_PATH = ".debugbundle/local/state.json";
@@ -94937,10 +95335,6 @@ var CLI_SDK = {
94937
95335
  name: "debugbundle-cli",
94938
95336
  version: "0.1.0"
94939
95337
  };
94940
- var EVENT_TYPE_SET = new Set(EventTypeValues);
94941
- function isEventType(value) {
94942
- return typeof value === "string" && EVENT_TYPE_SET.has(value);
94943
- }
94944
95338
  function inferSeverity2(event, capturePreset, incidentKind = "immediate") {
94945
95339
  if (incidentKind === "request_anomaly") {
94946
95340
  return "medium";
@@ -94948,7 +95342,8 @@ function inferSeverity2(event, capturePreset, incidentKind = "immediate") {
94948
95342
  if (event.event_type === "request_event") {
94949
95343
  return classifyRequestStatus({ responseStatus: event.payload.response_status, capturePreset }) === "incident_signal" ? "high" : "low";
94950
95344
  }
94951
- if (event.event_type === "backend_exception" || event.event_type === "frontend_exception") {
95345
+ if (event.event_type === "frontend_exception") return inferFrontendExceptionSeverity(event);
95346
+ if (event.event_type === "backend_exception") {
94952
95347
  return "high";
94953
95348
  }
94954
95349
  if (event.event_type === "error_suppressed") {
@@ -94968,13 +95363,6 @@ function severityRank(severity) {
94968
95363
  return 1;
94969
95364
  }
94970
95365
  }
94971
- function compareEventEnvelopes(left, right) {
94972
- const occurredAtComparison = left.occurred_at.localeCompare(right.occurred_at);
94973
- if (occurredAtComparison !== 0) {
94974
- return occurredAtComparison;
94975
- }
94976
- return left.event_id.localeCompare(right.event_id);
94977
- }
94978
95366
  function classifyEnvelope(envelope, capturePreset) {
94979
95367
  return classifyEvent(
94980
95368
  envelope.event_type,
@@ -95066,9 +95454,9 @@ function stableJson3(value) {
95066
95454
  if (Array.isArray(value)) {
95067
95455
  return `[${value.map((entry) => stableJson3(entry)).join(",")}]`;
95068
95456
  }
95069
- const record = value;
95070
- const keys2 = Object.keys(record).sort();
95071
- return `{${keys2.map((key) => `${JSON.stringify(key)}:${stableJson3(record[key])}`).join(",")}}`;
95457
+ const record2 = value;
95458
+ const keys2 = Object.keys(record2).sort();
95459
+ return `{${keys2.map((key) => `${JSON.stringify(key)}:${stableJson3(record2[key])}`).join(",")}}`;
95072
95460
  }
95073
95461
  function buildRequestAnomalyFingerprint(input3) {
95074
95462
  return (0, import_node_crypto10.createHash)("sha256").update(
@@ -95221,144 +95609,6 @@ async function pathExists4(path, stat) {
95221
95609
  throw error;
95222
95610
  }
95223
95611
  }
95224
- function parseIncidentState(candidate) {
95225
- if (!isRecord(candidate)) {
95226
- return null;
95227
- }
95228
- if (candidate["source"] !== "local" || candidate["status"] !== "open" && candidate["status"] !== "resolved") {
95229
- return null;
95230
- }
95231
- const sourceEvents = candidate["source_events"];
95232
- if (!Array.isArray(sourceEvents)) {
95233
- return null;
95234
- }
95235
- const validatedSourceEvents = [];
95236
- for (const sourceEvent of sourceEvents) {
95237
- const validated = validateEvent(sourceEvent);
95238
- if (!validated.success) {
95239
- return null;
95240
- }
95241
- validatedSourceEvents.push(validated.data);
95242
- }
95243
- const matchedFields = candidate["matched_fields"];
95244
- const sourceEventTypes = candidate["source_event_types"];
95245
- if (!Array.isArray(matchedFields) || !matchedFields.every((value) => typeof value === "string")) {
95246
- return null;
95247
- }
95248
- if (!Array.isArray(sourceEventTypes) || !sourceEventTypes.every(isEventType)) {
95249
- return null;
95250
- }
95251
- const severity = candidate["severity"];
95252
- if (severity !== "low" && severity !== "medium" && severity !== "high" && severity !== "critical") {
95253
- return null;
95254
- }
95255
- const status = candidate["status"];
95256
- if (status !== "open" && status !== "resolved") {
95257
- return null;
95258
- }
95259
- const requiredStringKeys = [
95260
- "incident_id",
95261
- "project_id",
95262
- "service_id",
95263
- "service_name",
95264
- "environment",
95265
- "fingerprint",
95266
- "fingerprint_version",
95267
- "title",
95268
- "first_seen_at",
95269
- "last_seen_at",
95270
- "source_event_id",
95271
- "source_occurred_at",
95272
- "bundle_path",
95273
- "reproduction_path"
95274
- ];
95275
- for (const key of requiredStringKeys) {
95276
- if (typeof candidate[key] !== "string") {
95277
- return null;
95278
- }
95279
- }
95280
- if (typeof candidate["occurrence_count"] !== "number" || typeof candidate["generation_number"] !== "number") {
95281
- return null;
95282
- }
95283
- const serviceRuntime = candidate["service_runtime"];
95284
- const serviceFramework = candidate["service_framework"];
95285
- if (serviceRuntime !== null && typeof serviceRuntime !== "string") {
95286
- return null;
95287
- }
95288
- if (serviceFramework !== null && typeof serviceFramework !== "string") {
95289
- return null;
95290
- }
95291
- const incidentId = candidate["incident_id"];
95292
- const projectId = candidate["project_id"];
95293
- const serviceId = candidate["service_id"];
95294
- const serviceName = candidate["service_name"];
95295
- const environment = candidate["environment"];
95296
- const incidentFingerprint = candidate["fingerprint"];
95297
- const fingerprintVersion = candidate["fingerprint_version"];
95298
- const title = candidate["title"];
95299
- const firstSeenAt = candidate["first_seen_at"];
95300
- const lastSeenAt = candidate["last_seen_at"];
95301
- const occurrenceCount = candidate["occurrence_count"];
95302
- const sourceEventId = candidate["source_event_id"];
95303
- const sourceOccurredAt = candidate["source_occurred_at"];
95304
- const bundlePath = candidate["bundle_path"];
95305
- const reproductionPath = candidate["reproduction_path"];
95306
- const generationNumber = candidate["generation_number"];
95307
- const normalizedSourceEventTypes = [...sourceEventTypes].sort();
95308
- return {
95309
- incident_id: incidentId,
95310
- source: "local",
95311
- project_id: projectId,
95312
- service_id: serviceId,
95313
- service_name: serviceName,
95314
- service_runtime: serviceRuntime,
95315
- service_framework: serviceFramework,
95316
- environment,
95317
- fingerprint: incidentFingerprint,
95318
- fingerprint_version: fingerprintVersion,
95319
- title,
95320
- severity,
95321
- status,
95322
- first_seen_at: firstSeenAt,
95323
- last_seen_at: lastSeenAt,
95324
- occurrence_count: occurrenceCount,
95325
- source_event_id: sourceEventId,
95326
- source_occurred_at: sourceOccurredAt,
95327
- source_event_types: normalizedSourceEventTypes,
95328
- matched_fields: [...matchedFields].sort(),
95329
- bundle_path: bundlePath,
95330
- reproduction_path: reproductionPath,
95331
- generation_number: generationNumber,
95332
- source_events: validatedSourceEvents.sort(compareEventEnvelopes)
95333
- };
95334
- }
95335
- function parseState(rawState) {
95336
- const parsed = JSON.parse(rawState);
95337
- if (!isRecord(parsed) || parsed["version"] !== 1) {
95338
- return null;
95339
- }
95340
- const lastProcessedEventFile = parsed["last_processed_event_file"];
95341
- if (lastProcessedEventFile !== null && typeof lastProcessedEventFile !== "string") {
95342
- return null;
95343
- }
95344
- const incidents = parsed["incidents"];
95345
- if (!isRecord(incidents)) {
95346
- return null;
95347
- }
95348
- const parsedIncidents = {};
95349
- for (const [incidentId, incidentValue] of Object.entries(incidents).sort(([left], [right]) => left.localeCompare(right))) {
95350
- const incident = parseIncidentState(incidentValue);
95351
- if (incident === null || incident.incident_id !== incidentId) {
95352
- return null;
95353
- }
95354
- parsedIncidents[incidentId] = incident;
95355
- }
95356
- return {
95357
- version: 1,
95358
- last_processed_event_file: lastProcessedEventFile,
95359
- incidents: parsedIncidents
95360
- };
95361
- }
95362
95612
  async function readState(statePath, readFile, stat) {
95363
95613
  if (!await pathExists4(statePath, stat)) {
95364
95614
  return null;
@@ -95492,7 +95742,7 @@ async function processCommand(input3, dependencies = {}) {
95492
95742
  mergedIncidentIds: /* @__PURE__ */ new Set([incidentId]),
95493
95743
  signalEventTypes: /* @__PURE__ */ new Set(),
95494
95744
  traceIds: /* @__PURE__ */ new Set(),
95495
- title: normalizedEvent.normalized_message,
95745
+ title: normalizedEvent.incident_title ?? normalizedEvent.normalized_message,
95496
95746
  kind: "immediate",
95497
95747
  severity: inferSeverity2(event, capturePreset)
95498
95748
  };
@@ -95502,7 +95752,7 @@ async function processCommand(input3, dependencies = {}) {
95502
95752
  aggregate.newEvents.push(event);
95503
95753
  aggregate.signalEventTypes.add(event.event_type);
95504
95754
  const traceId = getTraceId(event);
95505
- if (traceId !== null) {
95755
+ if (traceId !== null && normalizedEvent.resource_type === void 0) {
95506
95756
  aggregate.traceIds.add(traceId);
95507
95757
  const traceCorrelationGroup = traceCorrelationGroups.get(traceId) ?? {
95508
95758
  incidentIds: /* @__PURE__ */ new Set(),
@@ -95594,7 +95844,7 @@ async function processCommand(input3, dependencies = {}) {
95594
95844
  service_framework: latestSignalEvent.service.framework ?? existing?.service_framework ?? null,
95595
95845
  environment: aggregate.environment,
95596
95846
  fingerprint: aggregate.fingerprint,
95597
- fingerprint_version: FINGERPRINT_VERSION,
95847
+ fingerprint_version: aggregate.kind === "request_anomaly" ? FINGERPRINT_VERSION : fingerprintVersion(normalizeEvent(latestSignalEvent)),
95598
95848
  title: aggregate.title,
95599
95849
  severity,
95600
95850
  status: existingIncidents.some((incidentState) => incidentState.status === "resolved") ? "open" : existing?.status ?? "open",
@@ -107980,7 +108230,7 @@ async function handleCaptureRuleCommand2(parsedArgv, dependencies) {
107980
108230
  // package.json
107981
108231
  var package_default = {
107982
108232
  name: "@debugbundle/cli",
107983
- version: "1.8.1",
108233
+ version: "1.9.1",
107984
108234
  private: false,
107985
108235
  description: "Command-line interface for DebugBundle",
107986
108236
  license: "Apache-2.0",