@agent-native/core 0.84.16 → 0.84.18

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 (31) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +12 -0
  3. package/corpus/core/package.json +1 -1
  4. package/corpus/core/src/client/session-replay.ts +2 -2
  5. package/corpus/templates/analytics/changelog/2026-07-01-session-replay-recordings-upload-reliably-in-production.md +6 -0
  6. package/corpus/templates/analytics/server/handlers/session-replay.ts +46 -5
  7. package/corpus/templates/calendar/app/components/booking/TimeSlotPicker.tsx +10 -0
  8. package/corpus/templates/calendar/app/components/calendar/GoogleConnectBanner.tsx +15 -6
  9. package/corpus/templates/calendar/app/components/calendar/GoogleSetupWizard.tsx +22 -0
  10. package/corpus/templates/calendar/app/hooks/use-bookings.ts +6 -2
  11. package/corpus/templates/calendar/app/i18n/zh-TW.ts +4 -0
  12. package/corpus/templates/calendar/app/i18n-data.ts +107 -0
  13. package/corpus/templates/calendar/app/lib/google-oauth-setup.ts +17 -0
  14. package/corpus/templates/calendar/app/pages/BookingLinksPage.tsx +9 -1
  15. package/corpus/templates/calendar/app/pages/BookingPage.tsx +40 -26
  16. package/corpus/templates/calendar/app/pages/Settings.tsx +3 -1
  17. package/corpus/templates/calendar/changelog/2026-07-01-booking-links-now-show-an-error-when-calendar-availability-c.md +6 -0
  18. package/corpus/templates/calendar/server/handlers/bookings.ts +78 -13
  19. package/corpus/templates/calendar/server/handlers/google-auth.ts +3 -2
  20. package/corpus/templates/calendar/server/lib/google-calendar.ts +16 -2
  21. package/corpus/templates/design/app/lib/design-system-preview.ts +69 -0
  22. package/corpus/templates/design/app/pages/DesignSystems.tsx +71 -50
  23. package/corpus/templates/design/changelog/2026-07-01-design-systems-no-longer-crash-on-responsive-tokens.md +6 -0
  24. package/dist/client/session-replay.js +2 -2
  25. package/dist/client/session-replay.js.map +1 -1
  26. package/dist/collab/awareness.d.ts +2 -2
  27. package/dist/collab/awareness.d.ts.map +1 -1
  28. package/dist/notifications/routes.d.ts +3 -3
  29. package/dist/observability/routes.d.ts +3 -3
  30. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  31. package/package.json +1 -1
@@ -163,6 +163,7 @@ type ConflictItem = { start: string; end: string };
163
163
  type ConflictResult = { items: ConflictItem[]; unavailableReason?: string };
164
164
  type BookingLinkRow = typeof schema.bookingLinks.$inferSelect;
165
165
  type ConflictDb = Pick<ReturnType<typeof getDb>, "select">;
166
+ const BOOKING_SLOT_STEP_MINUTES = 30;
166
167
 
167
168
  type LocalDateTimeParts = {
168
169
  year: number;
@@ -341,6 +342,27 @@ function dateEndIso(date: string, timezone: string): string {
341
342
  ).toISOString();
342
343
  }
343
344
 
345
+ function formatAvailabilityUnavailableReason(email?: string): string {
346
+ return email
347
+ ? `Calendar availability unavailable for ${email}`
348
+ : "Calendar availability unavailable";
349
+ }
350
+
351
+ function unavailableAvailabilityResponse(event: H3Event) {
352
+ setResponseStatus(event, 503);
353
+ return {
354
+ error:
355
+ "The host's calendar availability could not be checked. Please try again later.",
356
+ code: "calendar_availability_unavailable",
357
+ };
358
+ }
359
+
360
+ function formatLocalTime(totalMinutes: number): string {
361
+ const hour = Math.floor(totalMinutes / 60);
362
+ const minute = totalMinutes % 60;
363
+ return `${String(hour).padStart(2, "0")}:${String(minute).padStart(2, "0")}`;
364
+ }
365
+
344
366
  async function resolveAvailabilityContext({
345
367
  slug,
346
368
  db = getDb(),
@@ -397,7 +419,7 @@ async function resolveAvailabilityContext({
397
419
  };
398
420
  }
399
421
 
400
- async function getConflictItems({
422
+ export async function getConflictItems({
401
423
  db = getDb(),
402
424
  ownerEmail,
403
425
  hostEmails,
@@ -424,7 +446,18 @@ async function getConflictItems({
424
446
  );
425
447
  const freeBusyResolvedHosts = new Set<string>();
426
448
 
427
- if (await googleCalendar.isConnected(ownerEmail)) {
449
+ const ownerConnected = ownerEmail
450
+ ? await googleCalendar.isConnected(ownerEmail)
451
+ : false;
452
+
453
+ if (ownerEmail && !ownerConnected) {
454
+ return {
455
+ items: [],
456
+ unavailableReason: formatAvailabilityUnavailableReason(ownerEmail),
457
+ };
458
+ }
459
+
460
+ if (ownerConnected) {
428
461
  try {
429
462
  if (requiredHosts.length > 0) {
430
463
  const freeBusy = await googleCalendar.getFreeBusy(
@@ -434,6 +467,14 @@ async function getConflictItems({
434
467
  ownerEmail,
435
468
  timezone,
436
469
  );
470
+ if (freeBusy.errors.length > 0) {
471
+ return {
472
+ items: [],
473
+ unavailableReason: formatAvailabilityUnavailableReason(
474
+ freeBusy.errors[0]?.email || ownerEmail,
475
+ ),
476
+ };
477
+ }
437
478
  for (const [email, calendar] of Object.entries(freeBusy.calendars)) {
438
479
  const normalizedEmail = email.toLowerCase();
439
480
  if (!calendar.errors || calendar.errors.length === 0) {
@@ -448,11 +489,16 @@ async function getConflictItems({
448
489
  }
449
490
  }
450
491
 
451
- const { events: googleEvents } = await googleCalendar.listEvents(
452
- rangeStartIso,
453
- rangeEndIso,
454
- ownerEmail,
455
- );
492
+ const { events: googleEvents, errors: googleEventErrors } =
493
+ await googleCalendar.listEvents(rangeStartIso, rangeEndIso, ownerEmail);
494
+ if (googleEventErrors.length > 0) {
495
+ return {
496
+ items: [],
497
+ unavailableReason: formatAvailabilityUnavailableReason(
498
+ googleEventErrors[0]?.email || ownerEmail,
499
+ ),
500
+ };
501
+ }
456
502
  conflictItems.push(
457
503
  ...googleEvents.filter(eventBlocksAvailability).map((event) => ({
458
504
  start: event.start,
@@ -460,7 +506,10 @@ async function getConflictItems({
460
506
  })),
461
507
  );
462
508
  } catch {
463
- // Continue without Google events if API fails
509
+ return {
510
+ items: [],
511
+ unavailableReason: formatAvailabilityUnavailableReason(ownerEmail),
512
+ };
464
513
  }
465
514
  }
466
515
 
@@ -501,7 +550,7 @@ async function getConflictItems({
501
550
  return { items: conflictItems };
502
551
  }
503
552
 
504
- function generateAvailableSlotsForDate({
553
+ export function generateAvailableSlotsForDate({
505
554
  date,
506
555
  duration,
507
556
  config,
@@ -571,7 +620,17 @@ function generateAvailableSlotsForDate({
571
620
  const slotEnd = zonedTimeToUtc(date, scheduleSlot.end, timezone);
572
621
  if (slotEnd <= slotStart) continue;
573
622
 
574
- let current = new Date(slotStart);
623
+ const scheduleStartMinutes = startHour * 60 + startMin;
624
+ const firstSlotStartMinutes =
625
+ Math.ceil(scheduleStartMinutes / BOOKING_SLOT_STEP_MINUTES) *
626
+ BOOKING_SLOT_STEP_MINUTES;
627
+ if (firstSlotStartMinutes >= 24 * 60) continue;
628
+
629
+ let current = zonedTimeToUtc(
630
+ date,
631
+ formatLocalTime(firstSlotStartMinutes),
632
+ timezone,
633
+ );
575
634
 
576
635
  while (current.getTime() + slotDuration * 60 * 1000 <= slotEnd.getTime()) {
577
636
  const candidateStart = new Date(current);
@@ -598,7 +657,9 @@ function generateAvailableSlotsForDate({
598
657
  });
599
658
  }
600
659
 
601
- current = new Date(current.getTime() + slotDuration * 60 * 1000);
660
+ current = new Date(
661
+ current.getTime() + BOOKING_SLOT_STEP_MINUTES * 60 * 1000,
662
+ );
602
663
  }
603
664
  }
604
665
 
@@ -1147,7 +1208,9 @@ export const getAvailableSlots = defineEventHandler(async (event: H3Event) => {
1147
1208
  rangeEndIso: dateEndIso(rangeEnd, timezone),
1148
1209
  timezone,
1149
1210
  });
1150
- if (conflictResult.unavailableReason) return { dates: [] };
1211
+ if (conflictResult.unavailableReason) {
1212
+ return unavailableAvailabilityResponse(event);
1213
+ }
1151
1214
  const dates: string[] = [];
1152
1215
  for (
1153
1216
  let cursor = new Date(from!);
@@ -1177,7 +1240,9 @@ export const getAvailableSlots = defineEventHandler(async (event: H3Event) => {
1177
1240
  rangeEndIso: dateEndIso(date, timezone),
1178
1241
  timezone,
1179
1242
  });
1180
- if (conflictResult.unavailableReason) return { slots: [] };
1243
+ if (conflictResult.unavailableReason) {
1244
+ return unavailableAvailabilityResponse(event);
1245
+ }
1181
1246
  const availableSlots = generateAvailableSlotsForDate({
1182
1247
  date,
1183
1248
  duration,
@@ -5,6 +5,7 @@ import {
5
5
  isElectron,
6
6
  getAppUrl,
7
7
  resolveGoogleSignInCredentials,
8
+ resolveGoogleProviderCredentials,
8
9
  resolveOAuthRedirectUri,
9
10
  encodeOAuthState,
10
11
  decodeOAuthState,
@@ -74,8 +75,8 @@ async function resolveCalendarOAuthCredentials(event: H3Event) {
74
75
  clientSecret: await resolveSecret("GOOGLE_CLIENT_SECRET"),
75
76
  }),
76
77
  );
77
- if (!clientId || !clientSecret) return null;
78
- return { clientId, clientSecret };
78
+ if (clientId && clientSecret) return { clientId, clientSecret };
79
+ return resolveGoogleProviderCredentials();
79
80
  }
80
81
 
81
82
  function isCalendarConnectRequest(
@@ -10,6 +10,8 @@ import {
10
10
  getRequestOrgId,
11
11
  resolveSecret,
12
12
  runWithRequestContext,
13
+ resolveGoogleProviderCredentials,
14
+ resolveGoogleLegacyProviderCredentials,
13
15
  } from "@agent-native/core/server";
14
16
 
15
17
  import type {
@@ -95,8 +97,20 @@ async function readCredentialPair(
95
97
  resolveSecret(clientIdKey),
96
98
  resolveSecret(clientSecretKey),
97
99
  ]);
98
- if (!clientId || !clientSecret) return null;
99
- return { clientId, clientSecret };
100
+ if (clientId && clientSecret) return { clientId, clientSecret };
101
+ if (
102
+ clientIdKey === "GOOGLE_CLIENT_ID" &&
103
+ clientSecretKey === "GOOGLE_CLIENT_SECRET"
104
+ ) {
105
+ return resolveGoogleProviderCredentials();
106
+ }
107
+ if (
108
+ clientIdKey === "GOOGLE_LEGACY_CLIENT_ID" &&
109
+ clientSecretKey === "GOOGLE_LEGACY_CLIENT_SECRET"
110
+ ) {
111
+ return resolveGoogleLegacyProviderCredentials();
112
+ }
113
+ return null;
100
114
  }
101
115
 
102
116
  async function resolveGoogleProviderCredentialCandidates(
@@ -0,0 +1,69 @@
1
+ const DEFAULT_OBJECT_ENTRY_LIMIT = 8;
2
+
3
+ export function formatDesignTokenValue(value: unknown): string | undefined {
4
+ return formatTokenValue(value, new Set());
5
+ }
6
+
7
+ export function getCssColorToken(value: unknown): string | undefined {
8
+ if (typeof value === "string") {
9
+ const trimmed = value.trim();
10
+ return trimmed || undefined;
11
+ }
12
+
13
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
14
+ return undefined;
15
+ }
16
+
17
+ for (const nested of Object.values(value)) {
18
+ const candidate = getCssColorToken(nested);
19
+ if (candidate) return candidate;
20
+ }
21
+
22
+ return undefined;
23
+ }
24
+
25
+ function formatTokenValue(
26
+ value: unknown,
27
+ seenObjects: Set<object>,
28
+ ): string | undefined {
29
+ if (value === null || value === undefined) return undefined;
30
+
31
+ if (typeof value === "string") {
32
+ const trimmed = value.trim();
33
+ return trimmed || undefined;
34
+ }
35
+
36
+ if (typeof value === "number" || typeof value === "boolean") {
37
+ return String(value);
38
+ }
39
+
40
+ if (Array.isArray(value)) {
41
+ const parts = value
42
+ .map((item) => formatTokenValue(item, seenObjects))
43
+ .filter((item): item is string => Boolean(item));
44
+ return parts.length > 0 ? parts.join(", ") : undefined;
45
+ }
46
+
47
+ if (typeof value !== "object") return undefined;
48
+
49
+ if (seenObjects.has(value)) return undefined;
50
+ seenObjects.add(value);
51
+
52
+ const entries = Object.entries(value)
53
+ .map(([key, nestedValue]) => {
54
+ const formatted = formatTokenValue(nestedValue, seenObjects);
55
+ return formatted ? `${labelizeDesignTokenKey(key)}: ${formatted}` : null;
56
+ })
57
+ .filter((entry): entry is string => Boolean(entry))
58
+ .slice(0, DEFAULT_OBJECT_ENTRY_LIMIT);
59
+
60
+ seenObjects.delete(value);
61
+ return entries.length > 0 ? entries.join(", ") : undefined;
62
+ }
63
+
64
+ function labelizeDesignTokenKey(key: string) {
65
+ return key
66
+ .replace(/([A-Z])/g, " $1")
67
+ .replace(/[-_]/g, " ")
68
+ .replace(/^./, (char) => char.toUpperCase());
69
+ }
@@ -65,6 +65,10 @@ import {
65
65
  TooltipContent,
66
66
  TooltipTrigger,
67
67
  } from "@/components/ui/tooltip";
68
+ import {
69
+ formatDesignTokenValue,
70
+ getCssColorToken,
71
+ } from "@/lib/design-system-preview";
68
72
 
69
73
  interface DesignSystem {
70
74
  id: string;
@@ -83,25 +87,25 @@ interface DesignSystem {
83
87
 
84
88
  interface DesignSystemData {
85
89
  colors?: {
86
- primary?: string;
87
- secondary?: string;
88
- accent?: string;
89
- background?: string;
90
- surface?: string;
91
- text?: string;
92
- textMuted?: string;
90
+ primary?: unknown;
91
+ secondary?: unknown;
92
+ accent?: unknown;
93
+ background?: unknown;
94
+ surface?: unknown;
95
+ text?: unknown;
96
+ textMuted?: unknown;
93
97
  };
94
98
  typography?: {
95
- headingFont?: string;
96
- bodyFont?: string;
97
- headingWeight?: string;
98
- bodyWeight?: string;
99
+ headingFont?: unknown;
100
+ bodyFont?: unknown;
101
+ headingWeight?: unknown;
102
+ bodyWeight?: unknown;
99
103
  };
100
- spacing?: Record<string, string | undefined>;
101
- borders?: Record<string, string | undefined>;
104
+ spacing?: Record<string, unknown>;
105
+ borders?: Record<string, unknown>;
102
106
  logos?: Array<{ url?: string; name?: string; variant?: string }>;
103
- defaults?: Record<string, string | undefined>;
104
- notes?: string;
107
+ defaults?: Record<string, unknown>;
108
+ notes?: unknown;
105
109
  }
106
110
 
107
111
  export default function DesignSystems() {
@@ -474,6 +478,12 @@ export default function DesignSystems() {
474
478
  {designSystems.map((ds) => {
475
479
  const parsed = parseData(ds.data);
476
480
  const colors = parsed?.colors;
481
+ const primaryColor = getCssColorToken(colors?.primary);
482
+ const secondaryColor = getCssColorToken(colors?.secondary);
483
+ const accentColor = getCssColorToken(colors?.accent);
484
+ const headingFont = formatDesignTokenValue(
485
+ parsed?.typography?.headingFont,
486
+ );
477
487
  const isSelected = selectedSystemIds.has(ds.id);
478
488
  return (
479
489
  <div
@@ -498,29 +508,27 @@ export default function DesignSystems() {
498
508
  >
499
509
  {/* Color preview */}
500
510
  <div className="aspect-video bg-muted/50 flex items-center justify-center gap-2 p-4">
501
- {colors?.primary && (
511
+ {primaryColor && (
502
512
  <div
503
513
  className="w-10 h-10 rounded-lg"
504
- style={{ backgroundColor: colors.primary }}
514
+ style={{ backgroundColor: primaryColor }}
505
515
  />
506
516
  )}
507
- {colors?.secondary && (
517
+ {secondaryColor && (
508
518
  <div
509
519
  className="w-10 h-10 rounded-lg"
510
- style={{ backgroundColor: colors.secondary }}
520
+ style={{ backgroundColor: secondaryColor }}
511
521
  />
512
522
  )}
513
- {colors?.accent && (
523
+ {accentColor && (
514
524
  <div
515
525
  className="w-10 h-10 rounded-lg"
516
- style={{ backgroundColor: colors.accent }}
526
+ style={{ backgroundColor: accentColor }}
517
527
  />
518
528
  )}
519
- {!colors?.primary &&
520
- !colors?.secondary &&
521
- !colors?.accent && (
522
- <IconPalette className="w-8 h-8 text-muted-foreground/40" />
523
- )}
529
+ {!primaryColor && !secondaryColor && !accentColor && (
530
+ <IconPalette className="w-8 h-8 text-muted-foreground/40" />
531
+ )}
524
532
  </div>
525
533
  <div className="p-4 pb-3">
526
534
  <div className="flex items-center gap-2 mb-1">
@@ -533,9 +541,9 @@ export default function DesignSystems() {
533
541
  </span>
534
542
  )}
535
543
  </div>
536
- {parsed?.typography?.headingFont && (
544
+ {headingFont && (
537
545
  <div className="text-xs text-muted-foreground/70">
538
- {parsed.typography.headingFont}
546
+ {headingFont}
539
547
  </div>
540
548
  )}
541
549
  </div>
@@ -885,10 +893,12 @@ function TokenPreview({
885
893
  key={color.label}
886
894
  className="flex min-w-0 items-center gap-3 rounded-lg border border-border bg-muted/30 p-2"
887
895
  >
888
- <div
889
- className="h-9 w-9 shrink-0 rounded-md border border-border"
890
- style={{ backgroundColor: color.value }}
891
- />
896
+ {color.swatch ? (
897
+ <div
898
+ className="h-9 w-9 shrink-0 rounded-md border border-border"
899
+ style={{ backgroundColor: color.swatch }}
900
+ />
901
+ ) : null}
892
902
  <div className="min-w-0">
893
903
  <div className="text-xs font-medium text-foreground">
894
904
  {color.label}
@@ -1001,34 +1011,47 @@ function getColorTokens(data: DesignSystemData | null, t: DesignT) {
1001
1011
  return [
1002
1012
  {
1003
1013
  label: t("designSystems.tokenPreview.colorLabels.primary"),
1004
- value: colors.primary,
1014
+ value: formatDesignTokenValue(colors.primary),
1015
+ swatch: getCssColorToken(colors.primary),
1005
1016
  },
1006
1017
  {
1007
1018
  label: t("designSystems.tokenPreview.colorLabels.secondary"),
1008
- value: colors.secondary,
1019
+ value: formatDesignTokenValue(colors.secondary),
1020
+ swatch: getCssColorToken(colors.secondary),
1009
1021
  },
1010
1022
  {
1011
1023
  label: t("designSystems.tokenPreview.colorLabels.accent"),
1012
- value: colors.accent,
1024
+ value: formatDesignTokenValue(colors.accent),
1025
+ swatch: getCssColorToken(colors.accent),
1013
1026
  },
1014
1027
  {
1015
1028
  label: t("designSystems.tokenPreview.colorLabels.background"),
1016
- value: colors.background,
1029
+ value: formatDesignTokenValue(colors.background),
1030
+ swatch: getCssColorToken(colors.background),
1017
1031
  },
1018
1032
  {
1019
1033
  label: t("designSystems.tokenPreview.colorLabels.surface"),
1020
- value: colors.surface,
1034
+ value: formatDesignTokenValue(colors.surface),
1035
+ swatch: getCssColorToken(colors.surface),
1021
1036
  },
1022
1037
  {
1023
1038
  label: t("designSystems.tokenPreview.colorLabels.text"),
1024
- value: colors.text,
1039
+ value: formatDesignTokenValue(colors.text),
1040
+ swatch: getCssColorToken(colors.text),
1025
1041
  },
1026
1042
  {
1027
1043
  label: t("designSystems.tokenPreview.colorLabels.mutedText"),
1028
- value: colors.textMuted,
1044
+ value: formatDesignTokenValue(colors.textMuted),
1045
+ swatch: getCssColorToken(colors.textMuted),
1029
1046
  },
1030
- ].filter((item): item is { label: string; value: string } =>
1031
- Boolean(item.value),
1047
+ ].filter(
1048
+ (
1049
+ item,
1050
+ ): item is {
1051
+ label: string;
1052
+ value: string;
1053
+ swatch: string | undefined;
1054
+ } => Boolean(item.value),
1032
1055
  );
1033
1056
  }
1034
1057
 
@@ -1038,19 +1061,19 @@ function getTypographyTokens(data: DesignSystemData | null, t: DesignT) {
1038
1061
  return [
1039
1062
  {
1040
1063
  label: t("designSystems.tokenPreview.typeLabels.headingFont"),
1041
- value: typography.headingFont,
1064
+ value: formatDesignTokenValue(typography.headingFont),
1042
1065
  },
1043
1066
  {
1044
1067
  label: t("designSystems.tokenPreview.typeLabels.bodyFont"),
1045
- value: typography.bodyFont,
1068
+ value: formatDesignTokenValue(typography.bodyFont),
1046
1069
  },
1047
1070
  {
1048
1071
  label: t("designSystems.tokenPreview.typeLabels.headingWeight"),
1049
- value: typography.headingWeight,
1072
+ value: formatDesignTokenValue(typography.headingWeight),
1050
1073
  },
1051
1074
  {
1052
1075
  label: t("designSystems.tokenPreview.typeLabels.bodyWeight"),
1053
- value: typography.bodyWeight,
1076
+ value: formatDesignTokenValue(typography.bodyWeight),
1054
1077
  },
1055
1078
  ].filter((item): item is { label: string; value: string } =>
1056
1079
  Boolean(item.value),
@@ -1089,12 +1112,10 @@ function getDetailTokens(
1089
1112
  ].filter((item): item is { label: string; value: string } => Boolean(item));
1090
1113
  }
1091
1114
 
1092
- function objectPreviewItems(
1093
- prefix: string,
1094
- values: Record<string, string | undefined>,
1095
- ) {
1115
+ function objectPreviewItems(prefix: string, values: Record<string, unknown>) {
1096
1116
  return Object.entries(values)
1097
- .filter((entry): entry is [string, string] => Boolean(entry[1]))
1117
+ .map(([key, value]) => [key, formatDesignTokenValue(value)] as const)
1118
+ .filter((entry): entry is readonly [string, string] => Boolean(entry[1]))
1098
1119
  .slice(0, 4)
1099
1120
  .map(([key, value]) => ({
1100
1121
  label: `${prefix}: ${labelizeKey(key)}`,
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-01
4
+ ---
5
+
6
+ Design systems no longer crash when imported tokens include responsive values.
@@ -516,7 +516,7 @@ async function gzipReplayBody(body) {
516
516
  .stream()
517
517
  .pipeThrough(new CompressionStream("gzip"));
518
518
  const compressed = await new Response(stream).arrayBuffer();
519
- return new Blob([compressed], { type: "application/json" });
519
+ return new Blob([compressed], { type: "application/octet-stream" });
520
520
  }
521
521
  catch {
522
522
  return null;
@@ -529,7 +529,7 @@ async function buildReplayUploadBody(body) {
529
529
  body: compressed,
530
530
  compressed: true,
531
531
  headers: {
532
- "Content-Type": "application/json",
532
+ "Content-Type": "application/octet-stream",
533
533
  "Content-Encoding": "gzip",
534
534
  },
535
535
  };