@debugbundle/mcp 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/main.cjs +522 -90
  2. package/package.json +1 -1
package/dist/main.cjs CHANGED
@@ -15198,7 +15198,7 @@ function createProjectManagementApi(client) {
15198
15198
 
15199
15199
  // ../../packages/retrieval-client/src/index.ts
15200
15200
  var IncidentReasonSchema = external_exports.object({
15201
- kind: external_exports.enum(["backend_exception", "frontend_exception", "request_failure_5xx", "error_log"]),
15201
+ kind: external_exports.enum(["backend_exception", "frontend_exception", "request_failure", "error_log"]),
15202
15202
  description: external_exports.string(),
15203
15203
  event_type: external_exports.enum(["backend_exception", "frontend_exception", "request_event", "log_event"]),
15204
15204
  event_class: external_exports.literal("incident_signal"),
@@ -15520,6 +15520,121 @@ function createRetrievalApi(client) {
15520
15520
  };
15521
15521
  }
15522
15522
 
15523
+ // ../../packages/slack-client/src/index.ts
15524
+ var SlackDestinationRecordSchema = external_exports.object({
15525
+ slack_destination_id: external_exports.string(),
15526
+ organization_id: external_exports.string(),
15527
+ slack_team_id: external_exports.string(),
15528
+ slack_team_name: external_exports.string().nullable(),
15529
+ slack_channel_id: external_exports.string(),
15530
+ slack_channel_name: external_exports.string().nullable(),
15531
+ installed_by_member_id: external_exports.string().nullable(),
15532
+ is_active: external_exports.boolean(),
15533
+ created_at: external_exports.string(),
15534
+ updated_at: external_exports.string()
15535
+ }).strict();
15536
+ var SlackDestinationListResponseSchema = external_exports.object({
15537
+ destinations: external_exports.array(SlackDestinationRecordSchema)
15538
+ }).strict();
15539
+ var SlackInstallUrlResponseSchema = external_exports.object({
15540
+ install_url: external_exports.string().url()
15541
+ }).strict();
15542
+ var SlackDestinationTestResponseSchema = external_exports.object({
15543
+ delivered: external_exports.boolean()
15544
+ }).strict();
15545
+ var ApiErrorResponseSchema6 = external_exports.object({
15546
+ error: external_exports.string()
15547
+ }).strict();
15548
+ var SlackApiError = class extends Error {
15549
+ status;
15550
+ code;
15551
+ constructor(status, code) {
15552
+ super(`slack_api_error: ${status}:${code}`);
15553
+ this.status = status;
15554
+ this.code = code;
15555
+ }
15556
+ };
15557
+ function parseApiError6(status, body) {
15558
+ const parsed = ApiErrorResponseSchema6.safeParse(body);
15559
+ if (!parsed.success) {
15560
+ throw new SlackApiError(status, "unknown_error");
15561
+ }
15562
+ throw new SlackApiError(status, parsed.data.error);
15563
+ }
15564
+ function buildQuery2(input) {
15565
+ const params = new URLSearchParams();
15566
+ for (const [key, value] of Object.entries(input)) {
15567
+ if (value !== void 0) {
15568
+ params.set(key, String(value));
15569
+ }
15570
+ }
15571
+ const query = params.toString();
15572
+ return query.length === 0 ? "" : `?${query}`;
15573
+ }
15574
+ function createSlackApi(client) {
15575
+ return {
15576
+ async getSlackInstallUrl(input) {
15577
+ const response = await client.request({
15578
+ method: "GET",
15579
+ path: `/v1/slack/app/install-url${buildQuery2({
15580
+ project_id: input.projectId,
15581
+ return_to: input.returnTo
15582
+ })}`,
15583
+ bearerToken: input.bearerToken
15584
+ });
15585
+ if (response.status < 200 || response.status >= 300) {
15586
+ parseApiError6(response.status, response.body);
15587
+ }
15588
+ const parsed = SlackInstallUrlResponseSchema.safeParse(response.body);
15589
+ if (!parsed.success) {
15590
+ throw new SlackApiError(response.status, "invalid_response_shape");
15591
+ }
15592
+ return parsed.data.install_url;
15593
+ },
15594
+ async listSlackDestinations(input) {
15595
+ const response = await client.request({
15596
+ method: "GET",
15597
+ path: `/v1/projects/${input.projectId}/slack/destinations`,
15598
+ bearerToken: input.bearerToken
15599
+ });
15600
+ if (response.status < 200 || response.status >= 300) {
15601
+ parseApiError6(response.status, response.body);
15602
+ }
15603
+ const parsed = SlackDestinationListResponseSchema.safeParse(response.body);
15604
+ if (!parsed.success) {
15605
+ throw new SlackApiError(response.status, "invalid_response_shape");
15606
+ }
15607
+ return parsed.data.destinations;
15608
+ },
15609
+ async testSlackDestination(input) {
15610
+ const response = await client.request({
15611
+ method: "POST",
15612
+ path: `/v1/projects/${input.projectId}/slack/destinations/${input.destinationId}/test`,
15613
+ bearerToken: input.bearerToken
15614
+ });
15615
+ if (response.status < 200 || response.status >= 300) {
15616
+ parseApiError6(response.status, response.body);
15617
+ }
15618
+ const parsed = SlackDestinationTestResponseSchema.safeParse(response.body);
15619
+ if (!parsed.success || parsed.data.delivered !== true) {
15620
+ throw new SlackApiError(response.status, "invalid_response_shape");
15621
+ }
15622
+ return { delivered: true };
15623
+ },
15624
+ async deleteSlackDestination(input) {
15625
+ const response = await client.request({
15626
+ method: "DELETE",
15627
+ path: `/v1/projects/${input.projectId}/slack/destinations/${input.destinationId}`,
15628
+ bearerToken: input.bearerToken
15629
+ });
15630
+ if (response.status < 200 || response.status >= 300) {
15631
+ parseApiError6(response.status, response.body);
15632
+ }
15633
+ return { slack_destination_id: input.destinationId };
15634
+ }
15635
+ };
15636
+ }
15637
+
15523
15638
  // ../../packages/token-management/src/index.ts
15524
15639
  var ProjectTokenSchema = external_exports.object({
15525
15640
  token_id: external_exports.string(),
@@ -15548,7 +15663,7 @@ var TokenListResponseSchema = external_exports.object({
15548
15663
  var TokenCreateResponseSchema = external_exports.object({
15549
15664
  token: external_exports.union([ProjectTokenSchema, MemberTokenSchema])
15550
15665
  }).strict();
15551
- var ApiErrorResponseSchema6 = external_exports.object({
15666
+ var ApiErrorResponseSchema7 = external_exports.object({
15552
15667
  error: external_exports.string()
15553
15668
  }).strict();
15554
15669
  var TokenManagementApiError = class extends Error {
@@ -15560,8 +15675,8 @@ var TokenManagementApiError = class extends Error {
15560
15675
  this.code = code;
15561
15676
  }
15562
15677
  };
15563
- function parseApiError6(status, body) {
15564
- const parsed = ApiErrorResponseSchema6.safeParse(body);
15678
+ function parseApiError7(status, body) {
15679
+ const parsed = ApiErrorResponseSchema7.safeParse(body);
15565
15680
  if (!parsed.success) {
15566
15681
  throw new TokenManagementApiError(status, "unknown_error");
15567
15682
  }
@@ -15570,7 +15685,7 @@ function parseApiError6(status, body) {
15570
15685
  async function expectTokenList(responsePromise) {
15571
15686
  const response = await responsePromise;
15572
15687
  if (response.status < 200 || response.status >= 300) {
15573
- parseApiError6(response.status, response.body);
15688
+ parseApiError7(response.status, response.body);
15574
15689
  }
15575
15690
  const parsed = TokenListResponseSchema.safeParse(response.body);
15576
15691
  if (!parsed.success) {
@@ -15581,7 +15696,7 @@ async function expectTokenList(responsePromise) {
15581
15696
  async function expectToken(responsePromise) {
15582
15697
  const response = await responsePromise;
15583
15698
  if (response.status < 200 || response.status >= 300) {
15584
- parseApiError6(response.status, response.body);
15699
+ parseApiError7(response.status, response.body);
15585
15700
  }
15586
15701
  const parsed = TokenCreateResponseSchema.safeParse(response.body);
15587
15702
  if (!parsed.success) {
@@ -15773,7 +15888,7 @@ var RetryWebhookDeliveryResponseSchema = external_exports.object({
15773
15888
  delivery_id: external_exports.string(),
15774
15889
  event_type: WebhookEventTypeSchema
15775
15890
  }).strict();
15776
- var ApiErrorResponseSchema7 = external_exports.object({
15891
+ var ApiErrorResponseSchema8 = external_exports.object({
15777
15892
  error: external_exports.string()
15778
15893
  }).strict();
15779
15894
  var WebhookApiError = class extends Error {
@@ -15785,14 +15900,14 @@ var WebhookApiError = class extends Error {
15785
15900
  this.code = code;
15786
15901
  }
15787
15902
  };
15788
- function parseApiError7(status, body) {
15789
- const parsed = ApiErrorResponseSchema7.safeParse(body);
15903
+ function parseApiError8(status, body) {
15904
+ const parsed = ApiErrorResponseSchema8.safeParse(body);
15790
15905
  if (!parsed.success) {
15791
15906
  throw new WebhookApiError(status, "unknown_error");
15792
15907
  }
15793
15908
  throw new WebhookApiError(status, parsed.data.error);
15794
15909
  }
15795
- function buildQuery2(input) {
15910
+ function buildQuery3(input) {
15796
15911
  const params = new URLSearchParams();
15797
15912
  for (const [key, value] of Object.entries(input)) {
15798
15913
  if (value !== void 0) {
@@ -15805,7 +15920,7 @@ function buildQuery2(input) {
15805
15920
  async function expectWebhookList(responsePromise) {
15806
15921
  const response = await responsePromise;
15807
15922
  if (response.status < 200 || response.status >= 300) {
15808
- parseApiError7(response.status, response.body);
15923
+ parseApiError8(response.status, response.body);
15809
15924
  }
15810
15925
  const parsed = WebhookListResponseSchema.safeParse(response.body);
15811
15926
  if (!parsed.success) {
@@ -15816,7 +15931,7 @@ async function expectWebhookList(responsePromise) {
15816
15931
  async function expectWebhook(responsePromise) {
15817
15932
  const response = await responsePromise;
15818
15933
  if (response.status < 200 || response.status >= 300) {
15819
- parseApiError7(response.status, response.body);
15934
+ parseApiError8(response.status, response.body);
15820
15935
  }
15821
15936
  const parsed = WebhookResponseSchema.safeParse(response.body);
15822
15937
  if (!parsed.success) {
@@ -15827,7 +15942,7 @@ async function expectWebhook(responsePromise) {
15827
15942
  async function expectCreatedWebhook(responsePromise) {
15828
15943
  const response = await responsePromise;
15829
15944
  if (response.status < 200 || response.status >= 300) {
15830
- parseApiError7(response.status, response.body);
15945
+ parseApiError8(response.status, response.body);
15831
15946
  }
15832
15947
  const parsed = WebhookCreateResponseSchema.safeParse(response.body);
15833
15948
  if (!parsed.success) {
@@ -15838,7 +15953,7 @@ async function expectCreatedWebhook(responsePromise) {
15838
15953
  async function expectWebhookDeliveries(responsePromise) {
15839
15954
  const response = await responsePromise;
15840
15955
  if (response.status < 200 || response.status >= 300) {
15841
- parseApiError7(response.status, response.body);
15956
+ parseApiError8(response.status, response.body);
15842
15957
  }
15843
15958
  const parsed = WebhookDeliveriesResponseSchema.safeParse(response.body);
15844
15959
  if (!parsed.success) {
@@ -15849,7 +15964,7 @@ async function expectWebhookDeliveries(responsePromise) {
15849
15964
  async function expectWebhookTestDelivery(responsePromise) {
15850
15965
  const response = await responsePromise;
15851
15966
  if (response.status < 200 || response.status >= 300) {
15852
- parseApiError7(response.status, response.body);
15967
+ parseApiError8(response.status, response.body);
15853
15968
  }
15854
15969
  const parsed = WebhookTestResponseSchema.safeParse(response.body);
15855
15970
  if (!parsed.success) {
@@ -15860,7 +15975,7 @@ async function expectWebhookTestDelivery(responsePromise) {
15860
15975
  async function expectRetryDelivery(responsePromise) {
15861
15976
  const response = await responsePromise;
15862
15977
  if (response.status < 200 || response.status >= 300) {
15863
- parseApiError7(response.status, response.body);
15978
+ parseApiError8(response.status, response.body);
15864
15979
  }
15865
15980
  const parsed = RetryWebhookDeliveryResponseSchema.safeParse(response.body);
15866
15981
  if (!parsed.success) {
@@ -15874,7 +15989,7 @@ function createWebhookApi(client) {
15874
15989
  return expectWebhookList(
15875
15990
  client.request({
15876
15991
  method: "GET",
15877
- path: `/v1/webhooks${buildQuery2({ project_id: input.projectId, limit: input.limit })}`,
15992
+ path: `/v1/webhooks${buildQuery3({ project_id: input.projectId, limit: input.limit })}`,
15878
15993
  bearerToken: input.bearerToken
15879
15994
  })
15880
15995
  );
@@ -15939,7 +16054,7 @@ function createWebhookApi(client) {
15939
16054
  bearerToken: input.bearerToken
15940
16055
  });
15941
16056
  if (response.status < 200 || response.status >= 300) {
15942
- parseApiError7(response.status, response.body);
16057
+ parseApiError8(response.status, response.body);
15943
16058
  }
15944
16059
  return {
15945
16060
  webhook_id: input.webhookId
@@ -15963,7 +16078,7 @@ function createWebhookApi(client) {
15963
16078
  return expectWebhookDeliveries(
15964
16079
  client.request({
15965
16080
  method: "GET",
15966
- path: `/v1/webhooks/${input.webhookId}/deliveries${buildQuery2({ limit: input.limit })}`,
16081
+ path: `/v1/webhooks/${input.webhookId}/deliveries${buildQuery3({ limit: input.limit })}`,
15967
16082
  bearerToken: input.bearerToken
15968
16083
  })
15969
16084
  );
@@ -16013,7 +16128,7 @@ var WeeklyReportChannelListResponseSchema = external_exports.object({
16013
16128
  var WeeklyReportChannelResponseSchema = external_exports.object({
16014
16129
  channel: WeeklyReportChannelRecordSchema
16015
16130
  }).strict();
16016
- var ApiErrorResponseSchema8 = external_exports.object({
16131
+ var ApiErrorResponseSchema9 = external_exports.object({
16017
16132
  error: external_exports.string()
16018
16133
  }).strict();
16019
16134
  var WeeklyReportApiError = class extends Error {
@@ -16025,14 +16140,14 @@ var WeeklyReportApiError = class extends Error {
16025
16140
  this.code = code;
16026
16141
  }
16027
16142
  };
16028
- function parseApiError8(status, body) {
16029
- const parsed = ApiErrorResponseSchema8.safeParse(body);
16143
+ function parseApiError9(status, body) {
16144
+ const parsed = ApiErrorResponseSchema9.safeParse(body);
16030
16145
  if (!parsed.success) {
16031
16146
  throw new WeeklyReportApiError(status, "unknown_error");
16032
16147
  }
16033
16148
  throw new WeeklyReportApiError(status, parsed.data.error);
16034
16149
  }
16035
- function buildQuery3(input) {
16150
+ function buildQuery4(input) {
16036
16151
  const params = new URLSearchParams();
16037
16152
  for (const [key, value] of Object.entries(input)) {
16038
16153
  if (value !== void 0) {
@@ -16045,7 +16160,7 @@ function buildQuery3(input) {
16045
16160
  async function expectChannelList(responsePromise) {
16046
16161
  const response = await responsePromise;
16047
16162
  if (response.status < 200 || response.status >= 300) {
16048
- parseApiError8(response.status, response.body);
16163
+ parseApiError9(response.status, response.body);
16049
16164
  }
16050
16165
  const parsed = WeeklyReportChannelListResponseSchema.safeParse(response.body);
16051
16166
  if (!parsed.success) {
@@ -16056,7 +16171,7 @@ async function expectChannelList(responsePromise) {
16056
16171
  async function expectChannel(responsePromise) {
16057
16172
  const response = await responsePromise;
16058
16173
  if (response.status < 200 || response.status >= 300) {
16059
- parseApiError8(response.status, response.body);
16174
+ parseApiError9(response.status, response.body);
16060
16175
  }
16061
16176
  const parsed = WeeklyReportChannelResponseSchema.safeParse(response.body);
16062
16177
  if (!parsed.success) {
@@ -16070,13 +16185,13 @@ function createWeeklyReportApi(client) {
16070
16185
  return expectChannelList(
16071
16186
  client.request({
16072
16187
  method: "GET",
16073
- path: `/v1/weekly-report-channels${buildQuery3({ project_id: input.projectId, limit: input.limit })}`,
16188
+ path: `/v1/weekly-report-channels${buildQuery4({ project_id: input.projectId, limit: input.limit })}`,
16074
16189
  bearerToken: input.bearerToken
16075
16190
  })
16076
16191
  );
16077
16192
  },
16078
16193
  async createWeeklyReportChannel(input) {
16079
- const config = input.channel === "email" ? { to: input.config.to } : { webhook_url: input.config.webhookUrl };
16194
+ const config = input.channel === "email" ? { to: input.config.to } : "webhookUrl" in input.config ? { webhook_url: input.config.webhookUrl } : { slack_destination_id: input.config.slackDestinationId };
16080
16195
  return expectChannel(
16081
16196
  client.request({
16082
16197
  method: "POST",
@@ -16099,7 +16214,7 @@ function createWeeklyReportApi(client) {
16099
16214
  async updateWeeklyReportChannel(input) {
16100
16215
  const body = {};
16101
16216
  if (input.config !== void 0) {
16102
- body["config"] = "to" in input.config ? { to: input.config.to } : { webhook_url: input.config.webhookUrl };
16217
+ body["config"] = "to" in input.config ? { to: input.config.to } : "webhookUrl" in input.config ? { webhook_url: input.config.webhookUrl } : { slack_destination_id: input.config.slackDestinationId };
16103
16218
  }
16104
16219
  if (input.schedule !== void 0) {
16105
16220
  body["schedule"] = {
@@ -16127,7 +16242,7 @@ function createWeeklyReportApi(client) {
16127
16242
  bearerToken: input.bearerToken
16128
16243
  });
16129
16244
  if (response.status < 200 || response.status >= 300) {
16130
- parseApiError8(response.status, response.body);
16245
+ parseApiError9(response.status, response.body);
16131
16246
  }
16132
16247
  return { channel_id: input.channelId };
16133
16248
  }
@@ -16159,6 +16274,8 @@ var CaptureBreadcrumbsValues = ["local_only", "exception_only", "standalone"];
16159
16274
  var CaptureBreadcrumbsSchema = external_exports.enum(CaptureBreadcrumbsValues);
16160
16275
  var CaptureProbeEventsValues = ["buffer_only", "standalone_when_activated"];
16161
16276
  var CaptureProbeEventsSchema = external_exports.enum(CaptureProbeEventsValues);
16277
+ var RequestSignalClassificationValues = ["incident_signal", "context_signal"];
16278
+ var RequestSignalClassificationSchema = external_exports.enum(RequestSignalClassificationValues);
16162
16279
  var CapturePolicySchema = external_exports.object({
16163
16280
  project_id: external_exports.string().uuid(),
16164
16281
  preset: CapturePresetSchema,
@@ -16175,6 +16292,55 @@ var CapturePolicyUpdateSchema = external_exports.object({
16175
16292
  capture_breadcrumbs: CaptureBreadcrumbsSchema.nullable().optional(),
16176
16293
  capture_probe_events: CaptureProbeEventsSchema.nullable().optional()
16177
16294
  });
16295
+ var BALANCED_IMMEDIATE_REQUEST_STATUSES = /* @__PURE__ */ new Set([408, 423, 424, 425, 429]);
16296
+ var INVESTIGATIVE_IMMEDIATE_REQUEST_STATUSES = /* @__PURE__ */ new Set([...BALANCED_IMMEDIATE_REQUEST_STATUSES, 409]);
16297
+ var BALANCED_STANDARD_ANOMALY_STATUSES = /* @__PURE__ */ new Set([401, 403, 404, 409, 422]);
16298
+ var BALANCED_HIGH_VOLUME_ANOMALY_STATUSES = /* @__PURE__ */ new Set([400, 410]);
16299
+ var INVESTIGATIVE_ANOMALY_STATUSES = /* @__PURE__ */ new Set([...BALANCED_STANDARD_ANOMALY_STATUSES, ...BALANCED_HIGH_VOLUME_ANOMALY_STATUSES]);
16300
+ function classifyRequestStatus(input) {
16301
+ const { responseStatus, capturePreset } = input;
16302
+ if (responseStatus === null || !Number.isFinite(responseStatus)) {
16303
+ return "context_signal";
16304
+ }
16305
+ if (responseStatus >= 500) {
16306
+ return "incident_signal";
16307
+ }
16308
+ if (capturePreset === "investigative") {
16309
+ return INVESTIGATIVE_IMMEDIATE_REQUEST_STATUSES.has(responseStatus) ? "incident_signal" : "context_signal";
16310
+ }
16311
+ if (capturePreset === "balanced") {
16312
+ return BALANCED_IMMEDIATE_REQUEST_STATUSES.has(responseStatus) ? "incident_signal" : "context_signal";
16313
+ }
16314
+ return "context_signal";
16315
+ }
16316
+ function getRequestAnomalyThreshold(input) {
16317
+ const { responseStatus, capturePreset } = input;
16318
+ if (responseStatus === null || !Number.isFinite(responseStatus) || responseStatus < 400 || responseStatus >= 500) {
16319
+ return null;
16320
+ }
16321
+ if (capturePreset === "minimal") {
16322
+ return null;
16323
+ }
16324
+ if (capturePreset === "investigative") {
16325
+ return INVESTIGATIVE_ANOMALY_STATUSES.has(responseStatus) ? {
16326
+ minimum_occurrences_5m: 8,
16327
+ minimum_ratio_5m_to_1h: 2
16328
+ } : null;
16329
+ }
16330
+ if (BALANCED_STANDARD_ANOMALY_STATUSES.has(responseStatus)) {
16331
+ return {
16332
+ minimum_occurrences_5m: 20,
16333
+ minimum_ratio_5m_to_1h: 3
16334
+ };
16335
+ }
16336
+ if (BALANCED_HIGH_VOLUME_ANOMALY_STATUSES.has(responseStatus)) {
16337
+ return {
16338
+ minimum_occurrences_5m: 50,
16339
+ minimum_ratio_5m_to_1h: 5
16340
+ };
16341
+ }
16342
+ return null;
16343
+ }
16178
16344
 
16179
16345
  // ../../packages/shared-types/src/index.ts
16180
16346
  function createUuidV4() {
@@ -16783,7 +16949,7 @@ function buildCliReference() {
16783
16949
  "- `debugbundle ingest <file> --format <format> [--json]`",
16784
16950
  "- `debugbundle watch --log <file> --format <format> [--json]`",
16785
16951
  "- `debugbundle watch --cloud --log <file> --format <format> [--json]`",
16786
- "- `debugbundle process [--json]`",
16952
+ "- `debugbundle process [--preset <minimal|balanced|investigative>] [--json]`",
16787
16953
  "",
16788
16954
  "## Investigation",
16789
16955
  "",
@@ -17875,7 +18041,7 @@ function getRequestResponseStatus(payload) {
17875
18041
  const status = payload?.["response_status"];
17876
18042
  return typeof status === "number" && Number.isFinite(status) ? status : null;
17877
18043
  }
17878
- function classifyEvent(eventType, logLevel, probeActivationId, payload) {
18044
+ function classifyEvent(eventType, logLevel, probeActivationId, payload, capturePreset = "minimal") {
17879
18045
  switch (eventType) {
17880
18046
  case "backend_exception":
17881
18047
  case "frontend_exception":
@@ -17887,10 +18053,7 @@ function classifyEvent(eventType, logLevel, probeActivationId, payload) {
17887
18053
  return "context_signal";
17888
18054
  case "request_event": {
17889
18055
  const responseStatus = getRequestResponseStatus(payload);
17890
- if (responseStatus !== null && responseStatus >= 500) {
17891
- return "incident_signal";
17892
- }
17893
- return "context_signal";
18056
+ return classifyRequestStatus({ responseStatus, capturePreset });
17894
18057
  }
17895
18058
  case "frontend_breadcrumb":
17896
18059
  case "deploy_metadata":
@@ -18052,7 +18215,7 @@ function buildPrivacyPreview() {
18052
18215
  sample_event_type: sampleEvent.event_type,
18053
18216
  sample_event_class: sampleEventClass,
18054
18217
  sample_can_create_incident: sampleEventClass === "incident_signal",
18055
- incident_rule: "request_event with response_status >= 500 is classified as an incident_signal",
18218
+ incident_rule: "request_event incident classification follows the resolved capture preset: 5xx always create incidents, balanced also promotes 408/423/424/425/429, and investigative also promotes 409.",
18056
18219
  redacted_fields,
18057
18220
  omitted_fields: [],
18058
18221
  retained_metadata: {
@@ -18563,7 +18726,8 @@ function buildRedactionRecord(bundleBody) {
18563
18726
  function buildVisibilityRecord(input) {
18564
18727
  const routeTarget = input.primarySignal.route_template ?? input.primarySignal.request_path;
18565
18728
  const matchedFields = input.incident.matched_fields.length === 0 ? "none" : input.incident.matched_fields.join(", ");
18566
- const grouping = input.primarySignal.kind === "request_failure_5xx" && input.primarySignal.request_method !== null && routeTarget !== null ? `Repeated 5xx request failures with the same normalized route template, request method, response status, service, and environment reuse this incident fingerprint. This incident currently groups ${input.primarySignal.request_method} ${routeTarget} with matched fields ${matchedFields}.` : `This incident groups repeated failures by fingerprint version ${input.incident.fingerprint_version} inside the service and environment boundary, with matched fields ${matchedFields}.`;
18729
+ const isRequestAnomaly = input.incident.matched_fields.includes("request_anomaly");
18730
+ const grouping = input.primarySignal.kind === "request_failure" && input.primarySignal.request_method !== null && routeTarget !== null ? isRequestAnomaly ? `Repeated request-anomaly incidents with the same normalized route template, request method, response status, service, and environment reuse this incident fingerprint once the anomaly threshold fires. This incident currently groups ${input.primarySignal.request_method} ${routeTarget} with matched fields ${matchedFields}.` : `Repeated request-failure incidents with the same normalized route template, request method, response status, service, and environment reuse this incident fingerprint. This incident currently groups ${input.primarySignal.request_method} ${routeTarget} with matched fields ${matchedFields}.` : `This incident groups repeated failures by fingerprint version ${input.incident.fingerprint_version} inside the service and environment boundary, with matched fields ${matchedFields}.`;
18567
18731
  const spikeLead = input.incident.spike_detected_at === void 0 || input.incident.spike_detected_at === null ? "This incident is not currently marked as spiking." : `This incident was marked as spiking at ${input.incident.spike_detected_at}.`;
18568
18732
  return {
18569
18733
  grouping,
@@ -18574,15 +18738,16 @@ function buildVisibilityRecord(input) {
18574
18738
  }
18575
18739
  function buildSuggestedNextChecks(input) {
18576
18740
  const suggestions = [];
18741
+ const isRequestAnomaly = input.incident.matched_fields.includes("request_anomaly");
18577
18742
  if (input.bundle.status === "pending") {
18578
18743
  suggestions.push("Wait for bundle generation to finish, then rerun the incident context command.");
18579
18744
  } else if (input.bundle.status === "failed") {
18580
18745
  suggestions.push("Inspect bundle generation status or retry bundle retrieval to recover missing context.");
18581
18746
  }
18582
18747
  const routeTarget = input.primarySignal.route_template ?? input.primarySignal.request_path;
18583
- if (input.primarySignal.request_method !== null && input.primarySignal.response_status !== null && input.primarySignal.response_status >= 500) {
18748
+ if (input.primarySignal.kind === "request_failure" && input.primarySignal.request_method !== null && routeTarget !== null) {
18584
18749
  suggestions.push(
18585
- `Inspect the ${input.primarySignal.request_method} ${routeTarget ?? "request"} handler behind this 5xx path.`
18750
+ isRequestAnomaly ? `Inspect the ${input.primarySignal.request_method} ${routeTarget} handler behind this repeated request-anomaly path.` : `Inspect the ${input.primarySignal.request_method} ${routeTarget} handler behind this request-failure path.`
18586
18751
  );
18587
18752
  }
18588
18753
  const firstApplicationFrame = input.primarySignal.first_application_frame;
@@ -18671,12 +18836,13 @@ function deriveIncidentReasonFromSignal(input) {
18671
18836
  };
18672
18837
  case "request_event": {
18673
18838
  const responseStatus = typeof input.response_status === "number" && Number.isFinite(input.response_status) ? input.response_status : null;
18839
+ const isRequestAnomaly = input.request_anomaly === true;
18674
18840
  return {
18675
- kind: "request_failure_5xx",
18676
- description: responseStatus !== null && responseStatus >= 500 ? `request_event response_status=${responseStatus} matched the 5xx request incident rule` : "request_event matched the 5xx request incident rule",
18841
+ kind: "request_failure",
18842
+ description: isRequestAnomaly ? responseStatus !== null ? `request_event response_status=${responseStatus} crossed the repeated request anomaly threshold` : "request_event crossed the repeated request anomaly threshold" : responseStatus !== null ? `request_event response_status=${responseStatus} matched the immediate request failure incident rule` : "request_event matched the immediate request failure incident rule",
18677
18843
  event_type: "request_event",
18678
18844
  event_class: "incident_signal",
18679
- matched_policy: "5xx request failures bypass capture_request_events suppression"
18845
+ matched_policy: isRequestAnomaly ? "Repeated contextual request failures crossed the request anomaly threshold" : "Immediate request failure statuses bypass capture_request_events suppression"
18680
18846
  };
18681
18847
  }
18682
18848
  case "log_event": {
@@ -18779,6 +18945,39 @@ var STORAGE_SCHEMA_MIGRATIONS = [
18779
18945
  ON github_device_authorizations (expires_at)
18780
18946
  `
18781
18947
  ]
18948
+ }),
18949
+ defineStorageSchemaMigration({
18950
+ id: "202605130001_allow_synthetic_webhook_test_deliveries_without_incident_fk",
18951
+ description: "Allow webhook test deliveries to persist without requiring a backing incidents row.",
18952
+ statements: [
18953
+ "ALTER TABLE webhook_deliveries ALTER COLUMN incident_id DROP NOT NULL"
18954
+ ]
18955
+ }),
18956
+ defineStorageSchemaMigration({
18957
+ id: "202605130002_add_slack_destinations",
18958
+ description: "Add reusable encrypted Slack alert destinations scoped to organizations.",
18959
+ statements: [
18960
+ `
18961
+ CREATE TABLE IF NOT EXISTS slack_destinations (
18962
+ id uuid PRIMARY KEY,
18963
+ organization_id uuid NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
18964
+ slack_team_id text NOT NULL,
18965
+ slack_team_name text,
18966
+ slack_channel_id text NOT NULL,
18967
+ slack_channel_name text,
18968
+ webhook_url_ciphertext text NOT NULL,
18969
+ installed_by_member_id uuid REFERENCES users(id) ON DELETE SET NULL,
18970
+ is_active boolean NOT NULL DEFAULT true,
18971
+ created_at timestamptz NOT NULL DEFAULT now(),
18972
+ updated_at timestamptz NOT NULL DEFAULT now(),
18973
+ UNIQUE (organization_id, slack_team_id, slack_channel_id)
18974
+ )
18975
+ `,
18976
+ `
18977
+ CREATE INDEX IF NOT EXISTS slack_destinations_org_active_idx
18978
+ ON slack_destinations (organization_id, is_active, created_at)
18979
+ `
18980
+ ]
18782
18981
  })
18783
18982
  ];
18784
18983
 
@@ -19799,11 +19998,17 @@ var EVENT_TYPE_SET = new Set(EventTypeValues);
19799
19998
  function isEventType(value) {
19800
19999
  return typeof value === "string" && EVENT_TYPE_SET.has(value);
19801
20000
  }
19802
- function inferSeverity2(eventType) {
19803
- if (eventType === "backend_exception" || eventType === "frontend_exception") {
20001
+ function inferSeverity2(event, capturePreset, incidentKind = "immediate") {
20002
+ if (incidentKind === "request_anomaly") {
20003
+ return "medium";
20004
+ }
20005
+ if (event.event_type === "request_event") {
20006
+ return classifyRequestStatus({ responseStatus: event.payload.response_status, capturePreset }) === "incident_signal" ? "high" : "low";
20007
+ }
20008
+ if (event.event_type === "backend_exception" || event.event_type === "frontend_exception") {
19804
20009
  return "high";
19805
20010
  }
19806
- if (eventType === "error_suppressed") {
20011
+ if (event.event_type === "error_suppressed") {
19807
20012
  return "medium";
19808
20013
  }
19809
20014
  return "low";
@@ -19827,15 +20032,17 @@ function compareEventEnvelopes(left, right) {
19827
20032
  }
19828
20033
  return left.event_id.localeCompare(right.event_id);
19829
20034
  }
19830
- function classifyEnvelope(envelope) {
20035
+ function classifyEnvelope(envelope, capturePreset) {
19831
20036
  return classifyEvent(
19832
20037
  envelope.event_type,
19833
20038
  envelope.event_type === "log_event" ? envelope.payload.level : void 0,
19834
- envelope.event_type === "probe_event" ? envelope.payload.activation_id : void 0
20039
+ envelope.event_type === "probe_event" ? envelope.payload.activation_id : void 0,
20040
+ envelope.payload,
20041
+ capturePreset
19835
20042
  );
19836
20043
  }
19837
- function isIncidentSignalEnvelope(envelope) {
19838
- return classifyEnvelope(envelope) === "incident_signal";
20044
+ function isIncidentSignalEnvelope(envelope, capturePreset) {
20045
+ return classifyEnvelope(envelope, capturePreset) === "incident_signal";
19839
20046
  }
19840
20047
  function getTraceId(envelope) {
19841
20048
  return envelope.correlation?.trace_id ?? null;
@@ -19880,7 +20087,10 @@ function mergeAggregateGroup(aggregates) {
19880
20087
  newEvents: [...canonicalAggregate.newEvents],
19881
20088
  mergedIncidentIds: new Set(canonicalAggregate.mergedIncidentIds),
19882
20089
  signalEventTypes: new Set(canonicalAggregate.signalEventTypes),
19883
- traceIds: new Set(canonicalAggregate.traceIds)
20090
+ traceIds: new Set(canonicalAggregate.traceIds),
20091
+ title: canonicalAggregate.title,
20092
+ kind: canonicalAggregate.kind,
20093
+ severity: canonicalAggregate.severity
19884
20094
  });
19885
20095
  }
19886
20096
  function hashIdentifier(parts, prefix, length) {
@@ -19906,6 +20116,111 @@ function mergeSourceEvents(existingEvents, nextEvents) {
19906
20116
  }
19907
20117
  return [...merged.values()].sort(compareEventEnvelopes);
19908
20118
  }
20119
+ function stableJson2(value) {
20120
+ if (value === null || typeof value !== "object") {
20121
+ return JSON.stringify(value);
20122
+ }
20123
+ if (Array.isArray(value)) {
20124
+ return `[${value.map((entry) => stableJson2(entry)).join(",")}]`;
20125
+ }
20126
+ const record = value;
20127
+ const keys = Object.keys(record).sort();
20128
+ return `{${keys.map((key) => `${JSON.stringify(key)}:${stableJson2(record[key])}`).join(",")}}`;
20129
+ }
20130
+ function buildRequestAnomalyFingerprint(input) {
20131
+ return (0, import_node_crypto4.createHash)("sha256").update(
20132
+ stableJson2({
20133
+ kind: "request_status_anomaly",
20134
+ project_id: input.projectId,
20135
+ service_name: input.serviceName,
20136
+ environment: input.environment,
20137
+ method: input.method,
20138
+ route_template: input.routeTemplate,
20139
+ response_status: input.responseStatus
20140
+ })
20141
+ ).digest("hex");
20142
+ }
20143
+ function buildRequestAnomalyTitle(input) {
20144
+ return `Request anomaly: ${input.method} ${input.routeTemplate} returned ${input.responseStatus} repeatedly`;
20145
+ }
20146
+ function toUnixSeconds(occurredAt) {
20147
+ return Math.floor(new Date(occurredAt).getTime() / 1e3);
20148
+ }
20149
+ function countOccurrencesInWindow(events, windowSeconds) {
20150
+ const latestEvent = events.at(-1);
20151
+ if (latestEvent === void 0) {
20152
+ return 0;
20153
+ }
20154
+ const latestOccurredAt = toUnixSeconds(latestEvent.occurred_at);
20155
+ const lowerBound = latestOccurredAt - windowSeconds + 1;
20156
+ return events.filter((event) => {
20157
+ const occurredAt = toUnixSeconds(event.occurred_at);
20158
+ return occurredAt >= lowerBound && occurredAt <= latestOccurredAt;
20159
+ }).length;
20160
+ }
20161
+ function passesRequestAnomalyThreshold(events, threshold) {
20162
+ const occurrences5m = countOccurrencesInWindow(events, 5 * 60);
20163
+ const occurrences1h = countOccurrencesInWindow(events, 60 * 60);
20164
+ const baseline1hPer5m = occurrences1h / 12;
20165
+ const ratio = occurrences5m / Math.max(baseline1hPer5m, 1);
20166
+ return occurrences5m >= threshold.minimum_occurrences_5m && ratio >= threshold.minimum_ratio_5m_to_1h;
20167
+ }
20168
+ function collectRequestAnomalyAggregates(batches, capturePreset) {
20169
+ const grouped = /* @__PURE__ */ new Map();
20170
+ for (const batch of batches) {
20171
+ for (const event of batch.events) {
20172
+ if (event.event_type !== "request_event" || classifyEnvelope(event, capturePreset) !== "context_signal") {
20173
+ continue;
20174
+ }
20175
+ const normalizedEvent = normalizeEvent(event);
20176
+ const responseStatus = normalizedEvent.http_status;
20177
+ const method = normalizedEvent.http_method;
20178
+ const routeTemplate = normalizedEvent.route_template;
20179
+ const threshold = getRequestAnomalyThreshold({ responseStatus, capturePreset });
20180
+ if (threshold === null || responseStatus === null || method === null || routeTemplate === null) {
20181
+ continue;
20182
+ }
20183
+ const projectId = requireProjectId(event);
20184
+ const incidentFingerprint = buildRequestAnomalyFingerprint({
20185
+ projectId,
20186
+ serviceName: event.service.name,
20187
+ environment: event.service.environment,
20188
+ method,
20189
+ routeTemplate,
20190
+ responseStatus
20191
+ });
20192
+ const incidentId = deriveIncidentId(projectId, event.service.name, event.service.environment, incidentFingerprint);
20193
+ const aggregate = grouped.get(incidentId) ?? {
20194
+ incidentId,
20195
+ projectId,
20196
+ serviceName: event.service.name,
20197
+ environment: event.service.environment,
20198
+ fingerprint: incidentFingerprint,
20199
+ matchedFields: /* @__PURE__ */ new Set(["request_anomaly", "route_template", "http_method", "http_status", "environment"]),
20200
+ newEvents: [],
20201
+ mergedIncidentIds: /* @__PURE__ */ new Set([incidentId]),
20202
+ signalEventTypes: /* @__PURE__ */ new Set(["request_event"]),
20203
+ traceIds: /* @__PURE__ */ new Set(),
20204
+ title: buildRequestAnomalyTitle({ method, routeTemplate, responseStatus }),
20205
+ kind: "request_anomaly",
20206
+ severity: "medium"
20207
+ };
20208
+ aggregate.newEvents.push(event);
20209
+ grouped.set(incidentId, aggregate);
20210
+ }
20211
+ }
20212
+ return [...grouped.values()].filter((aggregate) => {
20213
+ const latestEvent = aggregate.newEvents.at(-1);
20214
+ if (latestEvent === void 0 || latestEvent.event_type !== "request_event") {
20215
+ return false;
20216
+ }
20217
+ const threshold = getRequestAnomalyThreshold({
20218
+ responseStatus: normalizeEvent(latestEvent).http_status,
20219
+ capturePreset
20220
+ });
20221
+ return threshold !== null && passesRequestAnomalyThreshold(aggregate.newEvents, threshold);
20222
+ }).sort((left, right) => left.incidentId.localeCompare(right.incidentId));
20223
+ }
19909
20224
  function buildBundleContext(incident) {
19910
20225
  return {
19911
20226
  incident_id: incident.incident_id,
@@ -19935,12 +20250,12 @@ function formatServiceSummary(services) {
19935
20250
  }
19936
20251
  function formatProcessOutput(summary) {
19937
20252
  if (!summary.processed) {
19938
- return summary.message ?? "No new events to process.";
20253
+ return summary.message;
19939
20254
  }
19940
20255
  return [
19941
20256
  `Processed ${summary.events_processed} events from ${summary.files_processed} files into ${summary.incidents_processed} incidents.`,
19942
20257
  ...formatServiceSummary(summary.services),
19943
- `Last processed event file: ${summary.last_processed_event_file ?? "none"}`
20258
+ `Last processed event file: ${summary.last_processed_event_file}`
19944
20259
  ].join("\n");
19945
20260
  }
19946
20261
  async function pathExists4(path, stat) {
@@ -20180,6 +20495,7 @@ async function processCommand(input, dependencies = {}) {
20180
20495
  const statePath = (0, import_node_path6.join)(rootDirectory, LOCAL_STATE_FILE_PATH);
20181
20496
  const bundleDirectoryPath = (0, import_node_path6.join)(rootDirectory, LOCAL_BUNDLE_DIRECTORY_PATH2);
20182
20497
  const reproductionDirectoryPath = (0, import_node_path6.join)(rootDirectory, LOCAL_REPRODUCTION_DIRECTORY_PATH);
20498
+ const capturePreset = input.preset ?? "minimal";
20183
20499
  await mkdir((0, import_node_path6.join)(rootDirectory, ".debugbundle", "local"), { recursive: true });
20184
20500
  await mkdir(bundleDirectoryPath, { recursive: true });
20185
20501
  await mkdir(reproductionDirectoryPath, { recursive: true });
@@ -20187,22 +20503,26 @@ async function processCommand(input, dependencies = {}) {
20187
20503
  const eventFileNames = await pathExists4(eventsDirectoryPath, stat) ? (await readdir(eventsDirectoryPath)).filter((fileName) => fileName.endsWith(".events.json")).sort() : [];
20188
20504
  const lastProcessedEventFile = previousState?.last_processed_event_file ?? null;
20189
20505
  const newEventFileNames = lastProcessedEventFile === null ? eventFileNames : eventFileNames.filter((fileName) => fileName > lastProcessedEventFile);
20190
- if (newEventFileNames.length === 0) {
20506
+ const processAllEventFiles = input.preset !== void 0;
20507
+ const targetEventFileNames = processAllEventFiles ? eventFileNames : newEventFileNames;
20508
+ if (targetEventFileNames.length === 0) {
20191
20509
  const summary2 = buildNoNewEventsSummary(previousState?.last_processed_event_file ?? eventFileNames.at(-1) ?? null);
20192
20510
  return {
20193
20511
  exitCode: 0,
20194
20512
  output: input.json === true ? JSON.stringify(summary2) : formatProcessOutput(summary2)
20195
20513
  };
20196
20514
  }
20197
- const batches = await readEventBatches(eventsDirectoryPath, newEventFileNames, readFile);
20198
- const incidents = new Map(Object.entries(previousState?.incidents ?? {}));
20515
+ const batches = await readEventBatches(eventsDirectoryPath, targetEventFileNames, readFile);
20516
+ const incidents = new Map(
20517
+ processAllEventFiles ? [] : Object.entries(previousState?.incidents ?? {})
20518
+ );
20199
20519
  const aggregates = /* @__PURE__ */ new Map();
20200
20520
  const traceCorrelationGroups = /* @__PURE__ */ new Map();
20201
20521
  let eventsProcessed = 0;
20202
20522
  for (const batch of batches) {
20203
20523
  for (const event of batch.events) {
20204
20524
  eventsProcessed += 1;
20205
- if (!isIncidentSignalEnvelope(event)) {
20525
+ if (!isIncidentSignalEnvelope(event, capturePreset)) {
20206
20526
  continue;
20207
20527
  }
20208
20528
  const normalizedEvent = normalizeEvent(event);
@@ -20219,7 +20539,10 @@ async function processCommand(input, dependencies = {}) {
20219
20539
  newEvents: [],
20220
20540
  mergedIncidentIds: /* @__PURE__ */ new Set([incidentId]),
20221
20541
  signalEventTypes: /* @__PURE__ */ new Set(),
20222
- traceIds: /* @__PURE__ */ new Set()
20542
+ traceIds: /* @__PURE__ */ new Set(),
20543
+ title: normalizedEvent.normalized_message,
20544
+ kind: "immediate",
20545
+ severity: inferSeverity2(event, capturePreset)
20223
20546
  };
20224
20547
  for (const matchedField of inferMatchedFields(normalizedEvent)) {
20225
20548
  aggregate.matchedFields.add(matchedField);
@@ -20286,14 +20609,16 @@ async function processCommand(input, dependencies = {}) {
20286
20609
  mergedAggregatesByRoot.set(rootIncidentId, aggregateGroup);
20287
20610
  }
20288
20611
  const mergedAggregates = [...mergedAggregatesByRoot.values()].map((aggregateGroup) => mergeAggregateGroup(aggregateGroup)).sort((left, right) => left.incidentId.localeCompare(right.incidentId));
20612
+ const requestAnomalyAggregates = input.preset === void 0 ? [] : collectRequestAnomalyAggregates(batches, capturePreset);
20613
+ const finalizedAggregates = [...mergedAggregates, ...requestAnomalyAggregates].sort((left, right) => left.incidentId.localeCompare(right.incidentId));
20289
20614
  const services = /* @__PURE__ */ new Map();
20290
- for (const aggregate of mergedAggregates) {
20615
+ for (const aggregate of finalizedAggregates) {
20291
20616
  const incidentId = aggregate.incidentId;
20292
20617
  const existingIncidents = [...aggregate.mergedIncidentIds].map((mergedIncidentId) => incidents.get(mergedIncidentId)).filter((incident2) => incident2 !== void 0);
20293
20618
  const existing = existingIncidents.find((incident2) => incident2.incident_id === incidentId) ?? existingIncidents[0];
20294
20619
  const existingSourceEvents = existingIncidents.flatMap((incident2) => incident2.source_events);
20295
20620
  const combinedSourceEvents = mergeSourceEvents(existingSourceEvents, aggregate.newEvents);
20296
- const signalEvents = combinedSourceEvents.filter(isIncidentSignalEnvelope);
20621
+ const signalEvents = aggregate.kind === "request_anomaly" ? combinedSourceEvents : combinedSourceEvents.filter((event) => isIncidentSignalEnvelope(event, capturePreset));
20297
20622
  if (signalEvents.length === 0) {
20298
20623
  continue;
20299
20624
  }
@@ -20303,8 +20628,7 @@ async function processCommand(input, dependencies = {}) {
20303
20628
  continue;
20304
20629
  }
20305
20630
  const sourceEventTypes = [...new Set(signalEvents.map((event) => event.event_type))].sort();
20306
- const severity = signalEvents.map((event) => inferSeverity2(event.event_type)).sort((left, right) => severityRank(right) - severityRank(left))[0] ?? "low";
20307
- const latestNormalizedEvent = normalizeEvent(latestSignalEvent);
20631
+ const severity = signalEvents.map((event) => inferSeverity2(event, capturePreset, aggregate.kind)).sort((left, right) => severityRank(right) - severityRank(left))[0] ?? aggregate.severity;
20308
20632
  const generationNumber = signalEvents.length;
20309
20633
  const bundlePath = `${LOCAL_BUNDLE_DIRECTORY_PATH2}/${incidentId}.bundle.json`;
20310
20634
  const reproductionPath = `${LOCAL_REPRODUCTION_DIRECTORY_PATH}/${incidentId}.reproduction.json`;
@@ -20319,7 +20643,7 @@ async function processCommand(input, dependencies = {}) {
20319
20643
  environment: aggregate.environment,
20320
20644
  fingerprint: aggregate.fingerprint,
20321
20645
  fingerprint_version: FINGERPRINT_VERSION,
20322
- title: latestNormalizedEvent.normalized_message,
20646
+ title: aggregate.title,
20323
20647
  severity,
20324
20648
  status: existingIncidents.some((incidentState) => incidentState.status === "resolved") ? "open" : existing?.status ?? "open",
20325
20649
  first_seen_at: firstSignalEvent.occurred_at,
@@ -20380,21 +20704,22 @@ async function processCommand(input, dependencies = {}) {
20380
20704
  incidents.set(incidentId, incident);
20381
20705
  services.set(incident.service_name, (services.get(incident.service_name) ?? 0) + 1);
20382
20706
  }
20707
+ const finalProcessedEventFile = targetEventFileNames[targetEventFileNames.length - 1];
20383
20708
  const nextState = {
20384
20709
  version: 1,
20385
- last_processed_event_file: newEventFileNames.at(-1) ?? previousState?.last_processed_event_file ?? null,
20710
+ last_processed_event_file: finalProcessedEventFile,
20386
20711
  incidents: Object.fromEntries([...incidents.entries()].sort(([left], [right]) => left.localeCompare(right)))
20387
20712
  };
20388
20713
  await writeFile(statePath, serializeState(nextState));
20389
20714
  const summary = buildProcessedSummary({
20390
20715
  filesProcessed: newEventFileNames.length,
20391
20716
  eventsProcessed,
20392
- incidentsProcessed: mergedAggregates.length,
20717
+ incidentsProcessed: finalizedAggregates.length,
20393
20718
  services: [...services.entries()].sort(([left], [right]) => left.localeCompare(right)).map(([service, count]) => ({
20394
20719
  service,
20395
20720
  incidents: count
20396
20721
  })),
20397
- lastProcessedEventFile: nextState.last_processed_event_file
20722
+ lastProcessedEventFile: finalProcessedEventFile
20398
20723
  });
20399
20724
  return {
20400
20725
  exitCode: 0,
@@ -20510,7 +20835,11 @@ function parseLocalIncident(candidate) {
20510
20835
  }
20511
20836
  const serviceRuntime = candidate["service_runtime"];
20512
20837
  const serviceFramework = candidate["service_framework"];
20513
- const incidentReason = deriveIncidentReasonFromSourceEventTypes(candidate["source_event_types"]);
20838
+ const incidentReason = candidate["matched_fields"].includes("request_anomaly") ? deriveIncidentReasonFromSignal({
20839
+ event_type: "request_event",
20840
+ event_class: "incident_signal",
20841
+ request_anomaly: true
20842
+ }) : deriveIncidentReasonFromSourceEventTypes(candidate["source_event_types"]);
20514
20843
  if (serviceRuntime !== null && typeof serviceRuntime !== "string") {
20515
20844
  throw createReadError(400, "invalid_local_state");
20516
20845
  }
@@ -20816,14 +21145,14 @@ function localFailureStepName(checks) {
20816
21145
  function cloudVerificationRunId(now) {
20817
21146
  return now.toISOString().replace(/[-:.TZ]/g, "").slice(0, 14);
20818
21147
  }
20819
- function requestFailure5xxReason() {
21148
+ function requestFailureReason() {
20820
21149
  const incidentReason = deriveIncidentReasonFromSignal({
20821
21150
  event_type: "request_event",
20822
21151
  event_class: "incident_signal",
20823
21152
  response_status: 503
20824
21153
  });
20825
21154
  if (incidentReason === null) {
20826
- throw new Error("request_failure_5xx_reason_unavailable");
21155
+ throw new Error("request_failure_reason_unavailable");
20827
21156
  }
20828
21157
  return incidentReason;
20829
21158
  }
@@ -21098,7 +21427,7 @@ async function verifyCloudCommand(input, dependencies = {}) {
21098
21427
  const verification = {
21099
21428
  mode: "active_5xx",
21100
21429
  bundle_status: "unknown",
21101
- classification_reason: requestFailure5xxReason()
21430
+ classification_reason: requestFailureReason()
21102
21431
  };
21103
21432
  const errors = [];
21104
21433
  let exitCode = 0;
@@ -21150,7 +21479,7 @@ async function verifyCloudCommand(input, dependencies = {}) {
21150
21479
  if (candidate !== void 0) {
21151
21480
  incidentId = candidate.incident_id;
21152
21481
  verification.incident_id = candidate.incident_id;
21153
- verification.classification_reason = candidate.incident_reason ?? requestFailure5xxReason();
21482
+ verification.classification_reason = candidate.incident_reason ?? requestFailureReason();
21154
21483
  break;
21155
21484
  }
21156
21485
  if (attempt < pollAttempts) {
@@ -22927,8 +23256,71 @@ function createSetupMcpTools(commands) {
22927
23256
  };
22928
23257
  }
22929
23258
 
22930
- // src/token-tools.ts
23259
+ // src/slack-tools.ts
22931
23260
  function mapMcpError12(error) {
23261
+ if (error instanceof SlackApiError) {
23262
+ throw new Error(`mcp_tool_error:${error.code}`);
23263
+ }
23264
+ throw new Error("mcp_tool_error:unknown_error");
23265
+ }
23266
+ function createSlackMcpTools(api) {
23267
+ return {
23268
+ async list_slack_destinations(input) {
23269
+ try {
23270
+ return {
23271
+ destinations: await api.listSlackDestinations({
23272
+ bearerToken: String(input["bearerToken"]),
23273
+ projectId: String(input["projectId"])
23274
+ })
23275
+ };
23276
+ } catch (error) {
23277
+ mapMcpError12(error);
23278
+ }
23279
+ },
23280
+ async get_slack_connect_url(input) {
23281
+ try {
23282
+ return {
23283
+ install_url: await api.getSlackInstallUrl({
23284
+ bearerToken: String(input["bearerToken"]),
23285
+ projectId: String(input["projectId"]),
23286
+ ...typeof input["returnTo"] === "string" ? { returnTo: input["returnTo"] } : {}
23287
+ })
23288
+ };
23289
+ } catch (error) {
23290
+ mapMcpError12(error);
23291
+ }
23292
+ },
23293
+ async test_slack_destination(input) {
23294
+ try {
23295
+ return {
23296
+ delivery: await api.testSlackDestination({
23297
+ bearerToken: String(input["bearerToken"]),
23298
+ projectId: String(input["projectId"]),
23299
+ destinationId: String(input["destinationId"])
23300
+ })
23301
+ };
23302
+ } catch (error) {
23303
+ mapMcpError12(error);
23304
+ }
23305
+ },
23306
+ async delete_slack_destination(input) {
23307
+ try {
23308
+ return {
23309
+ destination: await api.deleteSlackDestination({
23310
+ bearerToken: String(input["bearerToken"]),
23311
+ projectId: String(input["projectId"]),
23312
+ destinationId: String(input["destinationId"])
23313
+ })
23314
+ };
23315
+ } catch (error) {
23316
+ mapMcpError12(error);
23317
+ }
23318
+ }
23319
+ };
23320
+ }
23321
+
23322
+ // src/token-tools.ts
23323
+ function mapMcpError13(error) {
22932
23324
  if (error instanceof TokenManagementApiError) {
22933
23325
  throw new Error(`mcp_tool_error:${error.code}`);
22934
23326
  }
@@ -22949,7 +23341,7 @@ function createTokenMcpTools(api) {
22949
23341
  tokens: await api.listProjectTokens(requestInput)
22950
23342
  };
22951
23343
  } catch (error) {
22952
- mapMcpError12(error);
23344
+ mapMcpError13(error);
22953
23345
  }
22954
23346
  },
22955
23347
  async create_project_token(input) {
@@ -22962,7 +23354,7 @@ function createTokenMcpTools(api) {
22962
23354
  })
22963
23355
  };
22964
23356
  } catch (error) {
22965
- mapMcpError12(error);
23357
+ mapMcpError13(error);
22966
23358
  }
22967
23359
  },
22968
23360
  async revoke_project_token(input) {
@@ -22975,7 +23367,7 @@ function createTokenMcpTools(api) {
22975
23367
  })
22976
23368
  };
22977
23369
  } catch (error) {
22978
- mapMcpError12(error);
23370
+ mapMcpError13(error);
22979
23371
  }
22980
23372
  },
22981
23373
  async list_member_tokens(input) {
@@ -22990,7 +23382,7 @@ function createTokenMcpTools(api) {
22990
23382
  tokens: await api.listMemberTokens(requestInput)
22991
23383
  };
22992
23384
  } catch (error) {
22993
- mapMcpError12(error);
23385
+ mapMcpError13(error);
22994
23386
  }
22995
23387
  },
22996
23388
  async create_member_token(input) {
@@ -23002,7 +23394,7 @@ function createTokenMcpTools(api) {
23002
23394
  })
23003
23395
  };
23004
23396
  } catch (error) {
23005
- mapMcpError12(error);
23397
+ mapMcpError13(error);
23006
23398
  }
23007
23399
  },
23008
23400
  async revoke_member_token(input) {
@@ -23014,14 +23406,14 @@ function createTokenMcpTools(api) {
23014
23406
  })
23015
23407
  };
23016
23408
  } catch (error) {
23017
- mapMcpError12(error);
23409
+ mapMcpError13(error);
23018
23410
  }
23019
23411
  }
23020
23412
  };
23021
23413
  }
23022
23414
 
23023
23415
  // src/webhook-tools.ts
23024
- function mapMcpError13(error) {
23416
+ function mapMcpError14(error) {
23025
23417
  if (error instanceof WebhookApiError) {
23026
23418
  throw new Error(`mcp_tool_error:${error.code}`);
23027
23419
  }
@@ -23042,7 +23434,7 @@ function createWebhookMcpTools(api) {
23042
23434
  webhooks: await api.listWebhooks(requestInput)
23043
23435
  };
23044
23436
  } catch (error) {
23045
- mapMcpError13(error);
23437
+ mapMcpError14(error);
23046
23438
  }
23047
23439
  },
23048
23440
  async create_webhook(input) {
@@ -23063,7 +23455,7 @@ function createWebhookMcpTools(api) {
23063
23455
  webhook: await api.createWebhook(requestInput)
23064
23456
  };
23065
23457
  } catch (error) {
23066
- mapMcpError13(error);
23458
+ mapMcpError14(error);
23067
23459
  }
23068
23460
  },
23069
23461
  async update_webhook(input) {
@@ -23088,7 +23480,7 @@ function createWebhookMcpTools(api) {
23088
23480
  webhook: await api.updateWebhook(requestInput)
23089
23481
  };
23090
23482
  } catch (error) {
23091
- mapMcpError13(error);
23483
+ mapMcpError14(error);
23092
23484
  }
23093
23485
  },
23094
23486
  async delete_webhook(input) {
@@ -23100,7 +23492,7 @@ function createWebhookMcpTools(api) {
23100
23492
  })
23101
23493
  };
23102
23494
  } catch (error) {
23103
- mapMcpError13(error);
23495
+ mapMcpError14(error);
23104
23496
  }
23105
23497
  },
23106
23498
  async test_webhook(input) {
@@ -23116,7 +23508,7 @@ function createWebhookMcpTools(api) {
23116
23508
  delivery: await api.testWebhook(requestInput)
23117
23509
  };
23118
23510
  } catch (error) {
23119
- mapMcpError13(error);
23511
+ mapMcpError14(error);
23120
23512
  }
23121
23513
  },
23122
23514
  async list_webhook_deliveries(input) {
@@ -23132,7 +23524,7 @@ function createWebhookMcpTools(api) {
23132
23524
  deliveries: await api.listWebhookDeliveries(requestInput)
23133
23525
  };
23134
23526
  } catch (error) {
23135
- mapMcpError13(error);
23527
+ mapMcpError14(error);
23136
23528
  }
23137
23529
  },
23138
23530
  async retry_webhook_delivery(input) {
@@ -23143,14 +23535,14 @@ function createWebhookMcpTools(api) {
23143
23535
  deliveryId: String(input["deliveryId"])
23144
23536
  });
23145
23537
  } catch (error) {
23146
- mapMcpError13(error);
23538
+ mapMcpError14(error);
23147
23539
  }
23148
23540
  }
23149
23541
  };
23150
23542
  }
23151
23543
 
23152
23544
  // src/weekly-report-tools.ts
23153
- function mapMcpError14(error) {
23545
+ function mapMcpError15(error) {
23154
23546
  if (error instanceof WeeklyReportApiError) {
23155
23547
  throw new Error(`mcp_tool_error:${error.code}`);
23156
23548
  }
@@ -23168,7 +23560,7 @@ function createWeeklyReportMcpTools(api) {
23168
23560
  })
23169
23561
  };
23170
23562
  } catch (error) {
23171
- mapMcpError14(error);
23563
+ mapMcpError15(error);
23172
23564
  }
23173
23565
  },
23174
23566
  async create_weekly_report_channel(input) {
@@ -23184,7 +23576,7 @@ function createWeeklyReportMcpTools(api) {
23184
23576
  })
23185
23577
  };
23186
23578
  } catch (error) {
23187
- mapMcpError14(error);
23579
+ mapMcpError15(error);
23188
23580
  }
23189
23581
  },
23190
23582
  async update_weekly_report_channel(input) {
@@ -23199,7 +23591,7 @@ function createWeeklyReportMcpTools(api) {
23199
23591
  })
23200
23592
  };
23201
23593
  } catch (error) {
23202
- mapMcpError14(error);
23594
+ mapMcpError15(error);
23203
23595
  }
23204
23596
  },
23205
23597
  async delete_weekly_report_channel(input) {
@@ -23211,7 +23603,7 @@ function createWeeklyReportMcpTools(api) {
23211
23603
  })
23212
23604
  };
23213
23605
  } catch (error) {
23214
- mapMcpError14(error);
23606
+ mapMcpError15(error);
23215
23607
  }
23216
23608
  }
23217
23609
  };
@@ -23262,6 +23654,7 @@ async function createDefaultMcpTools(input = {}) {
23262
23654
  ...createServicesMcpTools(retrievalApi),
23263
23655
  ...createTokenMcpTools(createTokenManagementApi(httpClient)),
23264
23656
  ...createWebhookMcpTools(createWebhookApi(httpClient)),
23657
+ ...createSlackMcpTools(createSlackApi(httpClient)),
23265
23658
  ...createWeeklyReportMcpTools(createWeeklyReportApi(httpClient)),
23266
23659
  ...createAlertMcpTools(createAlertApi(httpClient)),
23267
23660
  ...createProjectMcpTools(createProjectManagementApi(httpClient)),
@@ -24946,6 +25339,45 @@ var MCP_TOOL_CATALOG = [
24946
25339
  deliveryId: external_exports.string()
24947
25340
  })
24948
25341
  },
25342
+ {
25343
+ name: "list_slack_destinations",
25344
+ group: "slack",
25345
+ description: "List reusable connected Slack destinations for a project organization.",
25346
+ inputSchema: external_exports.object({
25347
+ bearerToken: external_exports.string(),
25348
+ projectId: external_exports.string()
25349
+ })
25350
+ },
25351
+ {
25352
+ name: "get_slack_connect_url",
25353
+ group: "slack",
25354
+ description: "Return a browser Slack connect URL for a project.",
25355
+ inputSchema: external_exports.object({
25356
+ bearerToken: external_exports.string(),
25357
+ projectId: external_exports.string(),
25358
+ returnTo: external_exports.string().optional()
25359
+ })
25360
+ },
25361
+ {
25362
+ name: "test_slack_destination",
25363
+ group: "slack",
25364
+ description: "Send a test message to a connected Slack destination.",
25365
+ inputSchema: external_exports.object({
25366
+ bearerToken: external_exports.string(),
25367
+ projectId: external_exports.string(),
25368
+ destinationId: external_exports.string()
25369
+ })
25370
+ },
25371
+ {
25372
+ name: "delete_slack_destination",
25373
+ group: "slack",
25374
+ description: "Delete a connected Slack destination from a project organization.",
25375
+ inputSchema: external_exports.object({
25376
+ bearerToken: external_exports.string(),
25377
+ projectId: external_exports.string(),
25378
+ destinationId: external_exports.string()
25379
+ })
25380
+ },
24949
25381
  {
24950
25382
  name: "list_weekly_report_channels",
24951
25383
  group: "weekly_reports",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@debugbundle/mcp",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "private": false,
5
5
  "description": "Model Context Protocol server for DebugBundle",
6
6
  "license": "AGPL-3.0-only",