@debugbundle/mcp 1.2.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/main.cjs CHANGED
@@ -15090,6 +15090,10 @@ function createGitHubManagementApi(client) {
15090
15090
 
15091
15091
  // ../../packages/project-management-client/src/index.ts
15092
15092
  var ProjectMetricsSchema = external_exports.object({
15093
+ open_incidents: external_exports.number().int().nonnegative().default(0),
15094
+ regressed_incidents: external_exports.number().int().nonnegative().default(0),
15095
+ opened_incidents_today: external_exports.number().int().nonnegative().default(0),
15096
+ opened_incidents_month: external_exports.number().int().nonnegative().default(0),
15093
15097
  monthly_bundle_requests: external_exports.number().int().nonnegative(),
15094
15098
  monthly_raw_ingested_events: external_exports.number().int().nonnegative(),
15095
15099
  retained_bundles: external_exports.number().int().nonnegative(),
@@ -15485,6 +15489,9 @@ function createRetrievalApi(client) {
15485
15489
  if (input.severity !== void 0) {
15486
15490
  query.set("severity", input.severity);
15487
15491
  }
15492
+ if (input.firstSeenAfter !== void 0) {
15493
+ query.set("first_seen_after", input.firstSeenAfter);
15494
+ }
15488
15495
  if (input.cursor !== void 0) {
15489
15496
  query.set("cursor", input.cursor);
15490
15497
  }
@@ -16786,7 +16793,7 @@ function isIpLikeHost(value) {
16786
16793
  return /^(?:\d{1,3}\.){3}\d{1,3}$/.test(host) || /^\[[0-9a-f:]+\]$/i.test(host) || host.includes(":") && /^[0-9a-f:]+$/i.test(host);
16787
16794
  }
16788
16795
 
16789
- // ../../packages/shared-types/src/capture-rules.ts
16796
+ // ../../packages/shared-types/src/capture-rule-schemas.ts
16790
16797
  var CAPTURE_RULE_EVENT_TYPES = [
16791
16798
  "backend_exception",
16792
16799
  "request_event",
@@ -16814,6 +16821,7 @@ var CaptureRuleSampleEventClassSchema = external_exports.enum(CaptureRuleSampleE
16814
16821
  var CaptureRuleRuntimeSchema = external_exports.enum(CAPTURE_RULE_RUNTIME_VALUES);
16815
16822
  var CaptureRuleEventTypeSchema = external_exports.enum(CAPTURE_RULE_EVENT_TYPES);
16816
16823
  var BrowserEventKindSchema = external_exports.enum(["window_error", "resource_error"]);
16824
+ var CaptureRuleClientKindSchema = external_exports.enum(["human", "bot", "unknown"]);
16817
16825
  function normalizeOptionalTrimmedString(value) {
16818
16826
  const trimmed = value?.trim();
16819
16827
  return trimmed && trimmed.length > 0 ? trimmed : void 0;
@@ -16891,6 +16899,9 @@ var CaptureRuleMatcherSchema = external_exports.object({
16891
16899
  message_contains: external_exports.string().min(1).max(500).optional(),
16892
16900
  message_equals: external_exports.string().min(1).max(500).optional(),
16893
16901
  browser_event_kind: BrowserEventKindSchema.optional(),
16902
+ browser_event_opaque: external_exports.boolean().optional(),
16903
+ client_kind: CaptureRuleClientKindSchema.optional(),
16904
+ bot_family: external_exports.string().min(1).max(120).optional(),
16894
16905
  resource_url: UrlMatcherSchema.optional(),
16895
16906
  request_url: UrlMatcherSchema.optional(),
16896
16907
  status_codes: external_exports.array(external_exports.number().int().min(100).max(599)).min(1).optional(),
@@ -16905,6 +16916,7 @@ var CaptureRuleMatcherSchema = external_exports.object({
16905
16916
  const errorName = normalizeOptionalTrimmedString(value.error_name);
16906
16917
  const messageContains = normalizeOptionalTrimmedString(value.message_contains);
16907
16918
  const messageEquals = normalizeOptionalTrimmedString(value.message_equals);
16919
+ const botFamily = normalizeOptionalTrimmedString(value.bot_family);
16908
16920
  const statusCodes = normalizeNumberArray(value.status_codes);
16909
16921
  if (eventTypes !== void 0) {
16910
16922
  normalized.event_types = eventTypes;
@@ -16933,6 +16945,15 @@ var CaptureRuleMatcherSchema = external_exports.object({
16933
16945
  if (value.browser_event_kind !== void 0) {
16934
16946
  normalized.browser_event_kind = value.browser_event_kind;
16935
16947
  }
16948
+ if (value.browser_event_opaque !== void 0) {
16949
+ normalized.browser_event_opaque = value.browser_event_opaque;
16950
+ }
16951
+ if (value.client_kind !== void 0) {
16952
+ normalized.client_kind = value.client_kind;
16953
+ }
16954
+ if (botFamily !== void 0) {
16955
+ normalized.bot_family = botFamily;
16956
+ }
16936
16957
  if (value.resource_url !== void 0) {
16937
16958
  normalized.resource_url = value.resource_url;
16938
16959
  }
@@ -16959,6 +16980,9 @@ var CaptureRuleMatcherSchema = external_exports.object({
16959
16980
  "message_contains",
16960
16981
  "message_equals",
16961
16982
  "browser_event_kind",
16983
+ "browser_event_opaque",
16984
+ "client_kind",
16985
+ "bot_family",
16962
16986
  "resource_url",
16963
16987
  "request_url",
16964
16988
  "status_codes",
@@ -17141,6 +17165,8 @@ var CaptureRulesFileSchema = external_exports.object({
17141
17165
  version: external_exports.literal(1),
17142
17166
  rules: external_exports.array(CaptureRuleSchema)
17143
17167
  });
17168
+
17169
+ // ../../packages/shared-types/src/capture-rule-evaluation.ts
17144
17170
  var CaptureRuleEvaluationUrlSchema = external_exports.object({
17145
17171
  host: external_exports.string().min(1).transform((value) => value.toLowerCase()).optional(),
17146
17172
  path: external_exports.string().min(1).transform((value) => value.startsWith("/") ? value : `/${value}`)
@@ -17156,11 +17182,40 @@ var CaptureRuleEvaluationContextSchema = external_exports.object({
17156
17182
  error_name: external_exports.string().min(1).optional(),
17157
17183
  message: external_exports.string().min(1).optional(),
17158
17184
  browser_event_kind: BrowserEventKindSchema.optional(),
17185
+ browser_event_opaque: external_exports.boolean().optional(),
17186
+ client_kind: CaptureRuleClientKindSchema.optional(),
17187
+ bot_family: external_exports.string().min(1).max(120).optional(),
17159
17188
  resource_url: CaptureRuleEvaluationUrlSchema.optional(),
17160
17189
  request_url: CaptureRuleEvaluationUrlSchema.optional(),
17161
17190
  status_code: external_exports.number().int().min(0).max(599).optional(),
17162
17191
  fingerprint: CaptureRuleFingerprintSchema.optional()
17163
17192
  });
17193
+ function classifyCaptureRuleClientFromUserAgent(userAgent) {
17194
+ if (userAgent === null || userAgent === void 0) {
17195
+ return { client_kind: "unknown" };
17196
+ }
17197
+ const lower = userAgent.toLowerCase();
17198
+ const knownBots = [
17199
+ { family: "Googlebot", markers: ["googlebot", "adsbot-google", "google-inspectiontool"] },
17200
+ { family: "Bingbot", markers: ["bingbot", "msnbot"] },
17201
+ { family: "DuckDuckBot", markers: ["duckduckbot"] },
17202
+ { family: "Applebot", markers: ["applebot"] },
17203
+ { family: "YandexBot", markers: ["yandexbot"] },
17204
+ { family: "Baiduspider", markers: ["baiduspider"] },
17205
+ { family: "FacebookBot", markers: ["facebookexternalhit", "facebot"] },
17206
+ { family: "LinkedInBot", markers: ["linkedinbot"] },
17207
+ { family: "TwitterBot", markers: ["twitterbot"] },
17208
+ { family: "Slackbot", markers: ["slackbot"] }
17209
+ ];
17210
+ const knownBot = knownBots.find((entry) => entry.markers.some((marker) => lower.includes(marker)));
17211
+ if (knownBot !== void 0) {
17212
+ return { client_kind: "bot", bot_family: knownBot.family };
17213
+ }
17214
+ if (["bot", "crawler", "spider", "slurp"].some((marker) => lower.includes(marker))) {
17215
+ return { client_kind: "bot", bot_family: "OtherBot" };
17216
+ }
17217
+ return { client_kind: "human" };
17218
+ }
17164
17219
 
17165
17220
  // ../../packages/shared-types/src/capture-rule-suggestions.ts
17166
17221
  var CaptureRuleSuggestionConfidenceSchema = external_exports.enum(["high", "medium", "low"]);
@@ -17370,6 +17425,12 @@ var BrowserExceptionEventSchema = external_exports.object({
17370
17425
  }).strict().optional(),
17371
17426
  opaque: external_exports.boolean()
17372
17427
  }).strict();
17428
+ var FrontendRejectionReasonSchema = external_exports.object({
17429
+ kind: external_exports.enum(["error", "string", "object", "null", "undefined", "unknown"]),
17430
+ name: external_exports.string().min(1).optional(),
17431
+ message: external_exports.string().min(1).optional(),
17432
+ preview: external_exports.string().min(1).optional()
17433
+ }).strict();
17373
17434
  var FrontendExceptionPayloadSchema = external_exports.object({
17374
17435
  name: external_exports.string().min(1),
17375
17436
  message: external_exports.string().min(1),
@@ -17382,6 +17443,7 @@ var FrontendExceptionPayloadSchema = external_exports.object({
17382
17443
  breadcrumbs: external_exports.array(FrontendExceptionBreadcrumbSchema).optional(),
17383
17444
  device: DeviceInfoSchema.nullable().optional(),
17384
17445
  browser_event: BrowserExceptionEventSchema.optional(),
17446
+ rejection_reason: FrontendRejectionReasonSchema.optional(),
17385
17447
  dom_context: external_exports.object({
17386
17448
  mode: external_exports.literal("lightweight"),
17387
17449
  html_excerpt: external_exports.string().min(1)
@@ -17816,7 +17878,7 @@ function buildSkill() {
17816
17878
  "2. Inspect the incident bundle and reproduction artifact before proposing a fix.",
17817
17879
  "3. Run `debugbundle analyze --type improvement --local` after local processing when you need a deterministic change plan.",
17818
17880
  "4. Apply the narrowest fix, then validate it with the repository test workflow from `.debugbundle/profile.json`.",
17819
- "5. When the fix is confirmed, or when the incident was intentionally generated for smoke, verification, or dogfooding, resolve it with `debugbundle resolve <incident-id>` or MCP `resolve_incident` so the open queue stays actionable.",
17881
+ "5. When the fix is confirmed, or when the incident was intentionally generated for smoke, verification, or dogfooding, resolve it with `debugbundle resolve <incident-id> [incident-id ...]` or MCP `resolve_incident` / `resolve_incidents` so the open queue stays actionable.",
17820
17882
  "",
17821
17883
  "## Incident Hygiene",
17822
17884
  "",
@@ -17825,6 +17887,15 @@ function buildSkill() {
17825
17887
  "- Reopen or leave open if the failure is still present, the validation is incomplete, or the incident represents a live unresolved problem.",
17826
17888
  "- If a resolved incident regresses, let the platform move it back to `regressed` through normal incident lifecycle behavior.",
17827
17889
  "",
17890
+ "## Noise Management",
17891
+ "",
17892
+ "When incident evidence shows repeated low-value operational noise rather than a product bug, evaluate whether a scoped capture rule or capture-policy path rule should handle future matches.",
17893
+ "",
17894
+ "- Run `debugbundle capture-rule suggest <incident-id> --json` before creating a manual rule. Apply deterministic suggestions with `debugbundle capture-rule create-from-suggestion <incident-id> --suggestion-id <id>` after confirming the scope is safe.",
17895
+ "- Prefer project capture rules for operational noise because they are centralized, auditable, and enforced by ingestion and processing. Use SDK `beforeSend` only for app-owned local policy such as final redaction or events that must never leave the runtime.",
17896
+ "- 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.",
17897
+ "- 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>`.",
17898
+ "",
17828
17899
  "## Profile Validation",
17829
17900
  "",
17830
17901
  "Use this task after setup or whenever architecture changes make the static profile stale.",
@@ -17887,10 +17958,21 @@ function buildCliReference() {
17887
17958
  "- `debugbundle explain <incident-id> [--source <local|cloud>] [--json]`",
17888
17959
  "- `debugbundle bundle <incident-id> [--source <local|cloud>] [--json]`",
17889
17960
  "- `debugbundle reproduce <incident-id> [--source <local|cloud>] [--json]`",
17890
- "- `debugbundle resolve <incident-id> [--source <local|cloud>] [--json]`",
17891
- "- `debugbundle reopen <incident-id> [--source <local|cloud>] [--json]`",
17961
+ "- `debugbundle resolve <incident-id> [incident-id ...] [--source <local|cloud>] [--json]`",
17962
+ "- `debugbundle reopen <incident-id> [incident-id ...] [--source <local|cloud>] [--json]`",
17892
17963
  "- `debugbundle analyze --type improvement --local`",
17893
17964
  "",
17965
+ "## Noise Management",
17966
+ "",
17967
+ "- `debugbundle capture-rule suggest <incident-id> [--auth-file <path>] [--json]`",
17968
+ "- `debugbundle capture-rule create-from-suggestion <incident-id> --suggestion-id <id> [--name <name>] [--expires-at <ISO8601>] [--auth-file <path>] [--json]`",
17969
+ "- `debugbundle capture-rule list --project-id <id> [--auth-file <path>] [--json]`",
17970
+ "- `debugbundle capture-rule create --project-id <id> --name <name> --action <demote|sample|drop> --matcher-json <json> [--auth-file <path>] [--json]`",
17971
+ "- `debugbundle capture-policy get [--project <id>] [--json]`",
17972
+ "- `debugbundle capture-policy set [--project <id>] --client-error-path-rule <404=/path/*@GET,POST> [--json]`",
17973
+ "",
17974
+ "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.",
17975
+ "",
17894
17976
  "## Operational Paths",
17895
17977
  "",
17896
17978
  "- `.debugbundle/profile.json` \u2014 committed project map and agent validation state",
@@ -17921,7 +18003,7 @@ function buildCliReference() {
17921
18003
  "```bash",
17922
18004
  "debugbundle incidents --status open --json \\",
17923
18005
  ` | jq -r '.incidents[] | select(.title | test("smoke test|dogfood|verification|synthetic"; "i")) | .incident_id' \\`,
17924
- " | xargs -n1 debugbundle resolve",
18006
+ " | xargs debugbundle resolve",
17925
18007
  "```",
17926
18008
  ""
17927
18009
  ].join("\n");
@@ -17940,19 +18022,28 @@ function buildMcpReference() {
17940
18022
  "- `get_incident_context` \u2014 fetch deterministic explanation context for triage.",
17941
18023
  "- `get_bundle` \u2014 fetch the full debug bundle before proposing a fix.",
17942
18024
  "- `get_reproduction` \u2014 fetch reproduction guidance before editing code.",
17943
- "- `resolve_incident` / `reopen_incident` \u2014 update lifecycle state after validation.",
18025
+ "- `resolve_incident` / `resolve_incidents` / `reopen_incident` / `reopen_incidents` \u2014 update lifecycle state after validation.",
17944
18026
  "- `analyze` \u2014 run local agent-oriented analysis from local bundles and skill schemas.",
17945
18027
  "",
17946
18028
  "- Prefer bundle retrieval tools before reading raw repository files.",
17947
18029
  "- Use MCP bundle access when the current issue originated in production.",
17948
- "- Resolve fixed or intentionally generated incidents with `resolve_incident` so open incidents stay actionable.",
18030
+ "- Resolve fixed or intentionally generated incidents with `resolve_incident` or `resolve_incidents` so open incidents stay actionable.",
17949
18031
  "- Fall back to local CLI processing when the project is local-only.",
17950
18032
  "",
18033
+ "## Noise and Capture Policy Tools",
18034
+ "",
18035
+ "- `suggest_capture_rules_from_incident` \u2014 generate deterministic capture-rule suggestions from an incident bundle.",
18036
+ "- `create_capture_rule_from_incident_suggestion` \u2014 apply a confirmed suggestion.",
18037
+ "- `list_capture_rules`, `create_capture_rule`, `update_capture_rule`, `delete_capture_rule` \u2014 manage project capture rules.",
18038
+ "- `get_capture_policy`, `update_capture_policy` \u2014 review or update capture policy, including path-scoped client-error incident rules.",
18039
+ "",
18040
+ "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.",
18041
+ "",
17951
18042
  "## Smoke-Test Cleanup Recipe",
17952
18043
  "",
17953
18044
  '1. Call `list_incidents` with `status: "open"`.',
17954
18045
  "2. Filter incidents whose titles show they were intentionally generated for smoke, dogfood, verification, or synthetic checks.",
17955
- "3. Call `resolve_incident` for each verified synthetic incident.",
18046
+ "3. Call `resolve_incidents` for verified synthetic incidents, or `resolve_incident` for a single incident.",
17956
18047
  "4. Call `list_incidents` again and confirm the open queue only contains actionable failures.",
17957
18048
  ""
17958
18049
  ].join("\n");
@@ -18072,6 +18163,16 @@ function buildSkillEvals() {
18072
18163
  "Leave unresolved incidents open when the failure is still live or unverified."
18073
18164
  ]
18074
18165
  },
18166
+ {
18167
+ name: "noise_management_guidance",
18168
+ prompt: "The same low-value frontend incident keeps reopening. Confirm the skill tells the agent how to evaluate operational noise without hiding real bugs.",
18169
+ expected_behavior: [
18170
+ "Inspect incident evidence before creating a rule.",
18171
+ "Use capture-rule suggestions for repeated operational noise.",
18172
+ "Keep generic frontend suppression narrow with structured browser or bot signals.",
18173
+ "Use capture-policy path rules for known route-scoped 4xx incidents."
18174
+ ]
18175
+ },
18075
18176
  {
18076
18177
  name: "artifact_path_discovery",
18077
18178
  prompt: "The user reports an unknown local runtime error. Confirm the skill tells the agent which DebugBundle paths and commands to inspect first.",
@@ -20032,6 +20133,88 @@ var DebugbundleNdjsonStructuredEntrySchema = external_exports.object({
20032
20133
  service: external_exports.string().min(1)
20033
20134
  });
20034
20135
 
20136
+ // ../../packages/storage/src/account-analytics-store.ts
20137
+ var ACCOUNT_METRIC_KEYS = [
20138
+ "account_created",
20139
+ "account_deleted",
20140
+ "project_created",
20141
+ "project_deleted",
20142
+ "raw_events_accepted",
20143
+ "raw_events_rejected",
20144
+ "events_rejected_malformed",
20145
+ "events_rejected_rate_limited",
20146
+ "events_rejected_quota",
20147
+ "events_rejected_capture_policy",
20148
+ "events_rejected_capture_rule",
20149
+ "billable_events_counted",
20150
+ "incident_signal_events_counted",
20151
+ "context_signal_events_counted",
20152
+ "operational_signal_events_counted",
20153
+ "local_verification_events_accepted",
20154
+ "cloud_verification_events_accepted",
20155
+ "incidents_opened",
20156
+ "incidents_resolved",
20157
+ "incidents_reopened",
20158
+ "incidents_regressed",
20159
+ "incident_occurrences",
20160
+ "incident_occurrences_high_severity",
20161
+ "incident_occurrences_critical_severity",
20162
+ "incidents_auto_detected_spiking",
20163
+ "failure_bundles_created",
20164
+ "failure_bundles_updated",
20165
+ "failure_bundle_generations_failed",
20166
+ "improvement_bundles_created",
20167
+ "improvement_bundles_updated",
20168
+ "improvement_bundle_generations_failed",
20169
+ "reproductions_created",
20170
+ "reproductions_failed",
20171
+ "retention_bundle_owners_rotated",
20172
+ "improvements_opened",
20173
+ "improvements_resolved",
20174
+ "improvements_reopened",
20175
+ "improvements_snoozed",
20176
+ "recurring_incident_improvements_opened",
20177
+ "post_deploy_regression_improvements_opened",
20178
+ "slow_request_improvements_opened",
20179
+ "request_failure_improvements_opened",
20180
+ "warning_log_improvements_opened",
20181
+ "alert_deliveries_created",
20182
+ "alert_deliveries_delivered",
20183
+ "alert_deliveries_failed",
20184
+ "alert_email_digests_sent",
20185
+ "operational_emails_sent",
20186
+ "weekly_reports_sent",
20187
+ "weekly_reports_failed",
20188
+ "webhook_deliveries_created",
20189
+ "webhook_deliveries_delivered",
20190
+ "webhook_deliveries_failed",
20191
+ "webhooks_auto_disabled",
20192
+ "github_dispatches_created",
20193
+ "github_dispatches_delivered",
20194
+ "github_dispatches_failed",
20195
+ "github_dispatch_rules_created",
20196
+ "github_dispatch_rules_deleted",
20197
+ "remote_probe_activations_created",
20198
+ "remote_probe_activations_expired",
20199
+ "probe_events_accepted",
20200
+ "capture_rules_created",
20201
+ "capture_rules_deleted",
20202
+ "capture_policy_updates",
20203
+ "trial_started",
20204
+ "trial_converted",
20205
+ "trial_expired",
20206
+ "plan_upgraded",
20207
+ "plan_downgraded",
20208
+ "capacity_units_purchased",
20209
+ "capacity_units_reduced",
20210
+ "allowance_warning_emails_sent",
20211
+ "allowance_limit_emails_sent",
20212
+ "projects_existing_at_account_deletion",
20213
+ "open_incidents_existing_at_account_deletion",
20214
+ "open_improvements_existing_at_account_deletion"
20215
+ ];
20216
+ var AccountMetricKeySchema = external_exports.enum(ACCOUNT_METRIC_KEYS);
20217
+
20035
20218
  // ../../packages/storage/src/incident-context.ts
20036
20219
  function isRecord(value) {
20037
20220
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -20128,6 +20311,33 @@ function buildRedactionRecord(bundleBody) {
20128
20311
  notes: readString(redaction["notes"])
20129
20312
  };
20130
20313
  }
20314
+ function buildBrowserSignalRecord(bundleBody) {
20315
+ const bundle = isRecord(bundleBody) ? bundleBody : {};
20316
+ const context = isRecord(bundle["context"]) ? bundle["context"] : {};
20317
+ const frontend = isRecord(context["frontend"]) ? context["frontend"] : {};
20318
+ const exceptions = Array.isArray(frontend["exceptions"]) ? frontend["exceptions"] : [];
20319
+ let exception;
20320
+ for (let index = exceptions.length - 1; index >= 0; index -= 1) {
20321
+ const candidate = exceptions[index];
20322
+ if (isRecord(candidate) && isRecord(candidate["browser_event"])) {
20323
+ exception = candidate;
20324
+ break;
20325
+ }
20326
+ }
20327
+ const browserEvent = isRecord(exception) && isRecord(exception["browser_event"]) ? exception["browser_event"] : null;
20328
+ const device = isRecord(context["device"]) ? context["device"] : {};
20329
+ const client = classifyCaptureRuleClientFromUserAgent(readString(device["user_agent"]) ?? void 0);
20330
+ if (browserEvent === null && client.client_kind === "unknown") {
20331
+ return null;
20332
+ }
20333
+ return {
20334
+ browser_event_kind: readString(browserEvent?.["kind"]),
20335
+ browser_event_opaque: readBoolean(browserEvent?.["opaque"]),
20336
+ browser_event_message: readString(browserEvent?.["message"]),
20337
+ client_kind: client.client_kind,
20338
+ bot_family: client.bot_family ?? null
20339
+ };
20340
+ }
20131
20341
  function buildVisibilityRecord(input) {
20132
20342
  const routeTarget = input.primarySignal.route_template ?? input.primarySignal.request_path;
20133
20343
  const matchedFields = input.incident.matched_fields.length === 0 ? "none" : input.incident.matched_fields.join(", ");
@@ -20165,6 +20375,14 @@ function buildSuggestedNextChecks(input) {
20165
20375
  if (input.deploy.regression_window === true || input.incident.status === "regressed") {
20166
20376
  suggestions.push("Compare this incident against the most recent deploy and recent regressions.");
20167
20377
  }
20378
+ if (input.browserSignal?.browser_event_opaque === true) {
20379
+ suggestions.push("Treat the browser event as opaque; inspect CSP, cross-origin scripts, resource loading, and framework error boundaries before changing application code.");
20380
+ }
20381
+ if (input.browserSignal?.client_kind === "bot") {
20382
+ suggestions.push(
20383
+ `Review whether ${input.browserSignal.bot_family ?? "bot"} traffic is operational noise before applying a bot-scoped capture rule.`
20384
+ );
20385
+ }
20168
20386
  if (input.reproduction.status === "pending") {
20169
20387
  suggestions.push("Recheck reproduction guidance after the reproduction artifact is ready.");
20170
20388
  }
@@ -20185,6 +20403,7 @@ function buildIncidentContextRecord(input) {
20185
20403
  primarySignal
20186
20404
  });
20187
20405
  const redaction = buildRedactionRecord(bundleBody);
20406
+ const browserSignal = buildBrowserSignalRecord(bundleBody);
20188
20407
  return {
20189
20408
  incident: input.incident,
20190
20409
  incident_reason: incidentReason,
@@ -20200,13 +20419,15 @@ function buildIncidentContextRecord(input) {
20200
20419
  },
20201
20420
  visibility,
20202
20421
  redaction,
20422
+ browser_signal: browserSignal,
20203
20423
  suggested_next_checks: buildSuggestedNextChecks({
20204
20424
  incident: input.incident,
20205
20425
  bundle: input.bundle,
20206
20426
  reproduction: input.reproduction,
20207
20427
  logs,
20208
20428
  primarySignal,
20209
- deploy
20429
+ deploy,
20430
+ browserSignal
20210
20431
  })
20211
20432
  };
20212
20433
  }
@@ -20279,6 +20500,9 @@ function deriveIncidentReasonFromSourceEventTypes(eventTypes) {
20279
20500
  // ../../packages/auth/src/primitives.ts
20280
20501
  var import_argon2 = require("@node-rs/argon2");
20281
20502
 
20503
+ // ../../packages/auth/src/account-deletion-auth.ts
20504
+ var DEFAULT_ACCOUNT_DELETION_CODE_LIFETIME_MS = 1e3 * 60 * 10;
20505
+
20282
20506
  // ../../packages/auth/src/web-session-auth.ts
20283
20507
  var DEFAULT_SESSION_LIFETIME_MS = 1e3 * 60 * 60 * 24 * 7;
20284
20508
  var DEFAULT_EMAIL_AUTH_CODE_LIFETIME_MS = 1e3 * 60 * 10;
@@ -20503,6 +20727,26 @@ var STORAGE_BOOTSTRAP_STATEMENTS = [
20503
20727
  CREATE INDEX email_auth_challenges_code_hash_idx
20504
20728
  ON email_auth_challenges (code_hash)
20505
20729
  `,
20730
+ `
20731
+ CREATE TABLE account_deletion_challenges (
20732
+ id uuid PRIMARY KEY,
20733
+ organization_id uuid NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
20734
+ user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE,
20735
+ email text NOT NULL,
20736
+ code_hash text NOT NULL,
20737
+ created_at timestamptz NOT NULL DEFAULT now(),
20738
+ expires_at timestamptz NOT NULL,
20739
+ used_at timestamptz
20740
+ )
20741
+ `,
20742
+ `
20743
+ CREATE INDEX account_deletion_challenges_scope_idx
20744
+ ON account_deletion_challenges (organization_id, user_id, lower(email), created_at DESC)
20745
+ `,
20746
+ `
20747
+ CREATE INDEX account_deletion_challenges_code_hash_idx
20748
+ ON account_deletion_challenges (code_hash)
20749
+ `,
20506
20750
  `
20507
20751
  CREATE TABLE github_device_authorizations (
20508
20752
  id uuid PRIMARY KEY,
@@ -21213,6 +21457,103 @@ var STORAGE_BOOTSTRAP_STATEMENTS = [
21213
21457
  PRIMARY KEY (organization_id, period_starts_at)
21214
21458
  )
21215
21459
  `,
21460
+ `
21461
+ CREATE TABLE project_usage_counters (
21462
+ project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
21463
+ period_starts_at timestamptz NOT NULL,
21464
+ raw_ingested_events integer NOT NULL DEFAULT 0,
21465
+ updated_at timestamptz NOT NULL DEFAULT now(),
21466
+ PRIMARY KEY (project_id, period_starts_at)
21467
+ )
21468
+ `,
21469
+ `
21470
+ CREATE TABLE account_analytics_accounts (
21471
+ analytics_account_id uuid PRIMARY KEY,
21472
+ organization_id uuid UNIQUE,
21473
+ organization_id_hash text NOT NULL UNIQUE,
21474
+ created_at timestamptz NOT NULL,
21475
+ first_seen_at timestamptz NOT NULL,
21476
+ metrics_collection_started_at timestamptz NOT NULL,
21477
+ backfilled_from_retained_rows_at timestamptz,
21478
+ deleted_at timestamptz,
21479
+ initial_plan text,
21480
+ latest_known_plan text,
21481
+ latest_capacity_units integer,
21482
+ account_deleted boolean NOT NULL DEFAULT false,
21483
+ metrics_schema_version integer NOT NULL DEFAULT 1,
21484
+ updated_at timestamptz NOT NULL DEFAULT now()
21485
+ )
21486
+ `,
21487
+ `
21488
+ CREATE TABLE account_metric_periods (
21489
+ analytics_account_id uuid NOT NULL REFERENCES account_analytics_accounts(analytics_account_id),
21490
+ period_grain text NOT NULL CHECK (period_grain IN ('day', 'month', 'year', 'lifetime')),
21491
+ period_starts_at timestamptz NOT NULL,
21492
+ metric_key text NOT NULL,
21493
+ metric_value bigint NOT NULL DEFAULT 0,
21494
+ updated_at timestamptz NOT NULL DEFAULT now(),
21495
+ PRIMARY KEY (analytics_account_id, period_grain, period_starts_at, metric_key)
21496
+ )
21497
+ `,
21498
+ `
21499
+ CREATE INDEX account_metric_periods_grain_period_metric_idx
21500
+ ON account_metric_periods (period_grain, period_starts_at, metric_key)
21501
+ `,
21502
+ `
21503
+ CREATE INDEX account_metric_periods_account_grain_period_idx
21504
+ ON account_metric_periods (analytics_account_id, period_grain, period_starts_at)
21505
+ `,
21506
+ `
21507
+ CREATE TABLE account_metric_events (
21508
+ dedupe_key_hash text PRIMARY KEY,
21509
+ analytics_account_id uuid NOT NULL REFERENCES account_analytics_accounts(analytics_account_id),
21510
+ metric_source text NOT NULL,
21511
+ occurred_at timestamptz NOT NULL,
21512
+ recorded_at timestamptz NOT NULL DEFAULT now(),
21513
+ metric_deltas jsonb NOT NULL
21514
+ )
21515
+ `,
21516
+ `
21517
+ CREATE TABLE account_payment_retention_records (
21518
+ id uuid PRIMARY KEY,
21519
+ analytics_account_id uuid NOT NULL REFERENCES account_analytics_accounts(analytics_account_id),
21520
+ organization_id_hash text NOT NULL,
21521
+ provider text NOT NULL,
21522
+ plan text,
21523
+ billing_state text,
21524
+ stripe_customer_id text,
21525
+ stripe_subscription_id text,
21526
+ billing_period_starts_at timestamptz,
21527
+ billing_period_ends_at timestamptz,
21528
+ additional_capacity_units integer,
21529
+ last_billing_event_id text,
21530
+ account_deleted_at timestamptz NOT NULL,
21531
+ recorded_at timestamptz NOT NULL DEFAULT now(),
21532
+ updated_at timestamptz NOT NULL DEFAULT now(),
21533
+ UNIQUE (analytics_account_id, provider)
21534
+ )
21535
+ `,
21536
+ `
21537
+ CREATE INDEX account_payment_retention_records_provider_idx
21538
+ ON account_payment_retention_records (provider, account_deleted_at DESC)
21539
+ `,
21540
+ `
21541
+ CREATE TABLE account_payment_provider_events (
21542
+ provider_event_key text PRIMARY KEY,
21543
+ analytics_account_id uuid NOT NULL REFERENCES account_analytics_accounts(analytics_account_id),
21544
+ organization_id_hash text NOT NULL,
21545
+ provider text NOT NULL,
21546
+ provider_event_id text NOT NULL,
21547
+ provider_event_type text NOT NULL,
21548
+ processed_at timestamptz NOT NULL,
21549
+ account_deleted_at timestamptz NOT NULL,
21550
+ recorded_at timestamptz NOT NULL DEFAULT now()
21551
+ )
21552
+ `,
21553
+ `
21554
+ CREATE UNIQUE INDEX account_payment_provider_events_provider_event_key
21555
+ ON account_payment_provider_events (provider, provider_event_id)
21556
+ `,
21216
21557
  `
21217
21558
  CREATE TABLE operational_email_deliveries (
21218
21559
  id uuid PRIMARY KEY,
@@ -21981,6 +22322,141 @@ var STORAGE_SCHEMA_MIGRATIONS = [
21981
22322
  statements: [
21982
22323
  "ALTER TABLE capture_policies ADD COLUMN IF NOT EXISTS immediate_client_error_path_rules jsonb"
21983
22324
  ]
22325
+ }),
22326
+ defineStorageSchemaMigration({
22327
+ id: "202606100001_add_project_usage_counters",
22328
+ description: "Add durable project-level raw ingestion counters for project dashboard metrics.",
22329
+ statements: [
22330
+ `
22331
+ CREATE TABLE IF NOT EXISTS project_usage_counters (
22332
+ project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
22333
+ period_starts_at timestamptz NOT NULL,
22334
+ raw_ingested_events integer NOT NULL DEFAULT 0,
22335
+ updated_at timestamptz NOT NULL DEFAULT now(),
22336
+ PRIMARY KEY (project_id, period_starts_at)
22337
+ )
22338
+ `
22339
+ ]
22340
+ }),
22341
+ defineStorageSchemaMigration({
22342
+ id: "202606100002_add_account_deletion_challenges",
22343
+ description: "Add scoped OTP challenges for account deletion confirmation.",
22344
+ statements: [
22345
+ `
22346
+ CREATE TABLE IF NOT EXISTS account_deletion_challenges (
22347
+ id uuid PRIMARY KEY,
22348
+ organization_id uuid NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
22349
+ user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE,
22350
+ email text NOT NULL,
22351
+ code_hash text NOT NULL,
22352
+ created_at timestamptz NOT NULL DEFAULT now(),
22353
+ expires_at timestamptz NOT NULL,
22354
+ used_at timestamptz
22355
+ )
22356
+ `,
22357
+ `
22358
+ CREATE INDEX IF NOT EXISTS account_deletion_challenges_scope_idx
22359
+ ON account_deletion_challenges (organization_id, user_id, lower(email), created_at DESC)
22360
+ `,
22361
+ `
22362
+ CREATE INDEX IF NOT EXISTS account_deletion_challenges_code_hash_idx
22363
+ ON account_deletion_challenges (code_hash)
22364
+ `
22365
+ ]
22366
+ }),
22367
+ defineStorageSchemaMigration({
22368
+ id: "202606100003_add_account_analytics_and_payment_retention",
22369
+ description: "Add deletion-safe account analytics and payment retention ledgers.",
22370
+ statements: [
22371
+ `
22372
+ CREATE TABLE IF NOT EXISTS account_analytics_accounts (
22373
+ analytics_account_id uuid PRIMARY KEY,
22374
+ organization_id uuid UNIQUE,
22375
+ organization_id_hash text NOT NULL UNIQUE,
22376
+ created_at timestamptz NOT NULL,
22377
+ first_seen_at timestamptz NOT NULL,
22378
+ metrics_collection_started_at timestamptz NOT NULL,
22379
+ backfilled_from_retained_rows_at timestamptz,
22380
+ deleted_at timestamptz,
22381
+ initial_plan text,
22382
+ latest_known_plan text,
22383
+ latest_capacity_units integer,
22384
+ account_deleted boolean NOT NULL DEFAULT false,
22385
+ metrics_schema_version integer NOT NULL DEFAULT 1,
22386
+ updated_at timestamptz NOT NULL DEFAULT now()
22387
+ )
22388
+ `,
22389
+ `
22390
+ CREATE TABLE IF NOT EXISTS account_metric_periods (
22391
+ analytics_account_id uuid NOT NULL REFERENCES account_analytics_accounts(analytics_account_id),
22392
+ period_grain text NOT NULL CHECK (period_grain IN ('day', 'month', 'year', 'lifetime')),
22393
+ period_starts_at timestamptz NOT NULL,
22394
+ metric_key text NOT NULL,
22395
+ metric_value bigint NOT NULL DEFAULT 0,
22396
+ updated_at timestamptz NOT NULL DEFAULT now(),
22397
+ PRIMARY KEY (analytics_account_id, period_grain, period_starts_at, metric_key)
22398
+ )
22399
+ `,
22400
+ `
22401
+ CREATE INDEX IF NOT EXISTS account_metric_periods_grain_period_metric_idx
22402
+ ON account_metric_periods (period_grain, period_starts_at, metric_key)
22403
+ `,
22404
+ `
22405
+ CREATE INDEX IF NOT EXISTS account_metric_periods_account_grain_period_idx
22406
+ ON account_metric_periods (analytics_account_id, period_grain, period_starts_at)
22407
+ `,
22408
+ `
22409
+ CREATE TABLE IF NOT EXISTS account_metric_events (
22410
+ dedupe_key_hash text PRIMARY KEY,
22411
+ analytics_account_id uuid NOT NULL REFERENCES account_analytics_accounts(analytics_account_id),
22412
+ metric_source text NOT NULL,
22413
+ occurred_at timestamptz NOT NULL,
22414
+ recorded_at timestamptz NOT NULL DEFAULT now(),
22415
+ metric_deltas jsonb NOT NULL
22416
+ )
22417
+ `,
22418
+ `
22419
+ CREATE TABLE IF NOT EXISTS account_payment_retention_records (
22420
+ id uuid PRIMARY KEY,
22421
+ analytics_account_id uuid NOT NULL REFERENCES account_analytics_accounts(analytics_account_id),
22422
+ organization_id_hash text NOT NULL,
22423
+ provider text NOT NULL,
22424
+ plan text,
22425
+ billing_state text,
22426
+ stripe_customer_id text,
22427
+ stripe_subscription_id text,
22428
+ billing_period_starts_at timestamptz,
22429
+ billing_period_ends_at timestamptz,
22430
+ additional_capacity_units integer,
22431
+ last_billing_event_id text,
22432
+ account_deleted_at timestamptz NOT NULL,
22433
+ recorded_at timestamptz NOT NULL DEFAULT now(),
22434
+ updated_at timestamptz NOT NULL DEFAULT now(),
22435
+ UNIQUE (analytics_account_id, provider)
22436
+ )
22437
+ `,
22438
+ `
22439
+ CREATE INDEX IF NOT EXISTS account_payment_retention_records_provider_idx
22440
+ ON account_payment_retention_records (provider, account_deleted_at DESC)
22441
+ `,
22442
+ `
22443
+ CREATE TABLE IF NOT EXISTS account_payment_provider_events (
22444
+ provider_event_key text PRIMARY KEY,
22445
+ analytics_account_id uuid NOT NULL REFERENCES account_analytics_accounts(analytics_account_id),
22446
+ organization_id_hash text NOT NULL,
22447
+ provider text NOT NULL,
22448
+ provider_event_id text NOT NULL,
22449
+ provider_event_type text NOT NULL,
22450
+ processed_at timestamptz NOT NULL,
22451
+ account_deleted_at timestamptz NOT NULL,
22452
+ recorded_at timestamptz NOT NULL DEFAULT now()
22453
+ )
22454
+ `,
22455
+ `
22456
+ CREATE UNIQUE INDEX IF NOT EXISTS account_payment_provider_events_provider_event_key
22457
+ ON account_payment_provider_events (provider, provider_event_id)
22458
+ `
22459
+ ]
21984
22460
  })
21985
22461
  ];
21986
22462
 
@@ -24053,7 +24529,7 @@ async function listLocalIncidents(input, dependencies) {
24053
24529
  return true;
24054
24530
  }
24055
24531
  return incident.status === input.status;
24056
- }).filter((incident) => input.severity === void 0 ? true : incident.severity === input.severity).sort(sortIncidentsDescending);
24532
+ }).filter((incident) => input.severity === void 0 ? true : incident.severity === input.severity).filter((incident) => input.firstSeenAfter === void 0 ? true : incident.first_seen_at >= input.firstSeenAfter).sort(sortIncidentsDescending);
24057
24533
  const startIndex = input.cursor === void 0 ? 0 : incidents.findIndex((incident) => buildCursor(incident) === input.cursor) + 1;
24058
24534
  const pagedIncidents = input.limit === void 0 ? incidents.slice(startIndex) : incidents.slice(startIndex, startIndex + input.limit);
24059
24535
  const hasMore = input.limit !== void 0 && startIndex + input.limit < incidents.length;
@@ -26364,6 +26840,9 @@ function readIncidentListFilters(input) {
26364
26840
  if (typeof input["severity"] === "string") {
26365
26841
  requestInput.severity = input["severity"];
26366
26842
  }
26843
+ if (typeof input["firstSeenAfter"] === "string") {
26844
+ requestInput.firstSeenAfter = input["firstSeenAfter"];
26845
+ }
26367
26846
  if (typeof input["cursor"] === "string") {
26368
26847
  requestInput.cursor = input["cursor"];
26369
26848
  }
@@ -26384,6 +26863,7 @@ async function listAllCloudIncidents(input, api) {
26384
26863
  ...filters.service === void 0 ? {} : { service: filters.service },
26385
26864
  ...filters.status === void 0 ? {} : { status: filters.status },
26386
26865
  ...filters.severity === void 0 ? {} : { severity: filters.severity },
26866
+ ...filters.firstSeenAfter === void 0 ? {} : { firstSeenAfter: filters.firstSeenAfter },
26387
26867
  ...cursor === void 0 ? {} : { cursor }
26388
26868
  });
26389
26869
  incidents.push(...response.incidents.map((incident) => attachSourceToRecord(incident, "cloud")));
@@ -26445,7 +26925,8 @@ function createRetrievalMcpTools(api) {
26445
26925
  ...incidentFilters.environment === void 0 ? {} : { environment: incidentFilters.environment },
26446
26926
  ...incidentFilters.service === void 0 ? {} : { service: incidentFilters.service },
26447
26927
  ...incidentFilters.status === void 0 ? {} : { status: incidentFilters.status },
26448
- ...incidentFilters.severity === void 0 ? {} : { severity: incidentFilters.severity }
26928
+ ...incidentFilters.severity === void 0 ? {} : { severity: incidentFilters.severity },
26929
+ ...incidentFilters.firstSeenAfter === void 0 ? {} : { firstSeenAfter: incidentFilters.firstSeenAfter }
26449
26930
  });
26450
26931
  const cloudIncidents = await listAllCloudIncidents(input, {
26451
26932
  listIncidents: (requestInput2) => api.listIncidents(requestInput2)
@@ -26476,6 +26957,9 @@ function createRetrievalMcpTools(api) {
26476
26957
  if (incidentFilters.severity !== void 0) {
26477
26958
  requestInput.severity = incidentFilters.severity;
26478
26959
  }
26960
+ if (incidentFilters.firstSeenAfter !== void 0) {
26961
+ requestInput.firstSeenAfter = incidentFilters.firstSeenAfter;
26962
+ }
26479
26963
  if (incidentFilters.cursor !== void 0) {
26480
26964
  requestInput.cursor = incidentFilters.cursor;
26481
26965
  }
@@ -28676,7 +29160,7 @@ var zodToJsonSchema = (schema, options) => {
28676
29160
  var package_default = {
28677
29161
  name: "@debugbundle/mcp",
28678
29162
  mcpName: "com.debugbundle/mcp",
28679
- version: "1.2.0",
29163
+ version: "1.4.0",
28680
29164
  private: false,
28681
29165
  description: "Model Context Protocol server for DebugBundle",
28682
29166
  license: "AGPL-3.0-only",
@@ -28730,6 +29214,7 @@ var listIncidentsInputSchema = external_exports.object({
28730
29214
  service: external_exports.string().optional(),
28731
29215
  status: external_exports.string().optional(),
28732
29216
  severity: external_exports.string().optional(),
29217
+ firstSeenAfter: external_exports.string().optional(),
28733
29218
  cursor: external_exports.string().optional(),
28734
29219
  limit: external_exports.number().optional()
28735
29220
  });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@debugbundle/mcp",
3
3
  "mcpName": "com.debugbundle/mcp",
4
- "version": "1.2.0",
4
+ "version": "1.4.0",
5
5
  "private": false,
6
6
  "description": "Model Context Protocol server for DebugBundle",
7
7
  "license": "AGPL-3.0-only",
package/server.json CHANGED
@@ -8,13 +8,13 @@
8
8
  "source": "github",
9
9
  "subfolder": "apps/mcp"
10
10
  },
11
- "version": "1.2.0",
11
+ "version": "1.4.0",
12
12
  "packages": [
13
13
  {
14
14
  "registryType": "npm",
15
15
  "registryBaseUrl": "https://registry.npmjs.org",
16
16
  "identifier": "@debugbundle/mcp",
17
- "version": "1.2.0",
17
+ "version": "1.4.0",
18
18
  "transport": {
19
19
  "type": "stdio"
20
20
  },