@builder.io/ai-utils 0.82.0 → 0.83.0-dev.202607211718.99c5f9cf8

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.
@@ -0,0 +1,82 @@
1
+ import { z } from "zod";
2
+ export declare const BUILDER_EMBEDDING_MODEL = "builder-multimodal-embedding";
3
+ export declare const BUILDER_EMBEDDING_DIMENSIONS = 1024;
4
+ export declare const BUILDER_EMBEDDING_IMAGE_MIME_TYPES: readonly ["image/png", "image/jpeg", "image/webp", "image/gif"];
5
+ export declare const BuilderEmbeddingImageSchema: z.ZodObject<{
6
+ mimeType: z.ZodEnum<{
7
+ "image/gif": "image/gif";
8
+ "image/jpeg": "image/jpeg";
9
+ "image/png": "image/png";
10
+ "image/webp": "image/webp";
11
+ }>;
12
+ data: z.ZodString;
13
+ }, z.core.$strip>;
14
+ export declare const BuilderEmbeddingInputSchema: z.ZodObject<{
15
+ text: z.ZodOptional<z.ZodString>;
16
+ images: z.ZodDefault<z.ZodArray<z.ZodObject<{
17
+ mimeType: z.ZodEnum<{
18
+ "image/gif": "image/gif";
19
+ "image/jpeg": "image/jpeg";
20
+ "image/png": "image/png";
21
+ "image/webp": "image/webp";
22
+ }>;
23
+ data: z.ZodString;
24
+ }, z.core.$strip>>>;
25
+ }, z.core.$strip>;
26
+ export declare const BuilderEmbeddingsRequestSchema: z.ZodObject<{
27
+ model: z.ZodDefault<z.ZodEnum<{
28
+ auto: "auto";
29
+ "builder-multimodal-embedding": "builder-multimodal-embedding";
30
+ }>>;
31
+ inputType: z.ZodDefault<z.ZodEnum<{
32
+ document: "document";
33
+ query: "query";
34
+ }>>;
35
+ inputs: z.ZodArray<z.ZodObject<{
36
+ text: z.ZodOptional<z.ZodString>;
37
+ images: z.ZodDefault<z.ZodArray<z.ZodObject<{
38
+ mimeType: z.ZodEnum<{
39
+ "image/gif": "image/gif";
40
+ "image/jpeg": "image/jpeg";
41
+ "image/png": "image/png";
42
+ "image/webp": "image/webp";
43
+ }>;
44
+ data: z.ZodString;
45
+ }, z.core.$strip>>>;
46
+ }, z.core.$strip>>;
47
+ source: z.ZodOptional<z.ZodObject<{
48
+ appId: z.ZodOptional<z.ZodString>;
49
+ feature: z.ZodOptional<z.ZodString>;
50
+ resourceId: z.ZodOptional<z.ZodString>;
51
+ userId: z.ZodOptional<z.ZodString>;
52
+ }, z.core.$strip>>;
53
+ metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
54
+ }, z.core.$strip>;
55
+ export type BuilderEmbeddingsRequestInput = z.input<typeof BuilderEmbeddingsRequestSchema>;
56
+ export type BuilderEmbeddingsRequest = z.output<typeof BuilderEmbeddingsRequestSchema>;
57
+ export declare const BuilderEmbeddingsResponseSchema: z.ZodObject<{
58
+ id: z.ZodString;
59
+ object: z.ZodLiteral<"list">;
60
+ model: z.ZodObject<{
61
+ publicId: z.ZodLiteral<"builder-multimodal-embedding">;
62
+ provider: z.ZodLiteral<"voyage">;
63
+ providerModel: z.ZodLiteral<"voyage-multimodal-3.5">;
64
+ version: z.ZodLiteral<"3.5">;
65
+ dimensions: z.ZodLiteral<1024>;
66
+ }, z.core.$strip>;
67
+ inputType: z.ZodEnum<{
68
+ document: "document";
69
+ query: "query";
70
+ }>;
71
+ data: z.ZodArray<z.ZodObject<{
72
+ object: z.ZodLiteral<"embedding">;
73
+ index: z.ZodNumber;
74
+ embedding: z.ZodArray<z.ZodNumber>;
75
+ }, z.core.$strip>>;
76
+ usage: z.ZodObject<{
77
+ textTokens: z.ZodNumber;
78
+ imagePixels: z.ZodNumber;
79
+ totalTokens: z.ZodNumber;
80
+ }, z.core.$strip>;
81
+ }, z.core.$strip>;
82
+ export type BuilderEmbeddingsResponse = z.infer<typeof BuilderEmbeddingsResponseSchema>;
@@ -0,0 +1,106 @@
1
+ import { z } from "zod";
2
+ export const BUILDER_EMBEDDING_MODEL = "builder-multimodal-embedding";
3
+ export const BUILDER_EMBEDDING_DIMENSIONS = 1024;
4
+ export const BUILDER_EMBEDDING_IMAGE_MIME_TYPES = [
5
+ "image/png",
6
+ "image/jpeg",
7
+ "image/webp",
8
+ "image/gif",
9
+ ];
10
+ const MAX_IMAGE_BASE64_LENGTH = 14000000;
11
+ const MAX_REQUEST_BASE64_LENGTH = 24000000;
12
+ const MAX_REQUEST_TEXT_LENGTH = 256000;
13
+ export const BuilderEmbeddingImageSchema = z.object({
14
+ mimeType: z.enum(BUILDER_EMBEDDING_IMAGE_MIME_TYPES),
15
+ data: z
16
+ .string()
17
+ .min(1)
18
+ .max(MAX_IMAGE_BASE64_LENGTH)
19
+ .regex(/^[A-Za-z0-9+/]*={0,2}$/, "Image data must be raw base64."),
20
+ });
21
+ export const BuilderEmbeddingInputSchema = z
22
+ .object({
23
+ text: z.string().trim().min(1).max(32000).optional(),
24
+ images: z.array(BuilderEmbeddingImageSchema).max(6).default([]),
25
+ })
26
+ .superRefine((input, ctx) => {
27
+ if (!input.text && input.images.length === 0) {
28
+ ctx.addIssue({
29
+ code: "custom",
30
+ message: "Each input must contain text, an image, or both.",
31
+ });
32
+ }
33
+ });
34
+ export const BuilderEmbeddingsRequestSchema = z
35
+ .object({
36
+ model: z.enum(["auto", BUILDER_EMBEDDING_MODEL]).default("auto"),
37
+ inputType: z.enum(["query", "document"]).default("document"),
38
+ inputs: z.array(BuilderEmbeddingInputSchema).min(1).max(32),
39
+ source: z
40
+ .object({
41
+ appId: z.string().max(100).optional(),
42
+ feature: z.string().max(100).optional(),
43
+ resourceId: z.string().max(200).optional(),
44
+ userId: z.string().max(200).optional(),
45
+ })
46
+ .optional(),
47
+ metadata: z.record(z.string().max(100), z.string().max(1000)).optional(),
48
+ })
49
+ .superRefine((request, ctx) => {
50
+ var _a;
51
+ var _b;
52
+ let base64Length = 0;
53
+ let textLength = 0;
54
+ for (const input of request.inputs) {
55
+ textLength += (_b = (_a = input.text) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0;
56
+ for (const image of input.images)
57
+ base64Length += image.data.length;
58
+ }
59
+ if (base64Length > MAX_REQUEST_BASE64_LENGTH) {
60
+ ctx.addIssue({
61
+ code: "too_big",
62
+ maximum: MAX_REQUEST_BASE64_LENGTH,
63
+ origin: "string",
64
+ inclusive: true,
65
+ message: "Combined image data is too large.",
66
+ path: ["inputs"],
67
+ });
68
+ }
69
+ if (textLength > MAX_REQUEST_TEXT_LENGTH) {
70
+ ctx.addIssue({
71
+ code: "too_big",
72
+ maximum: MAX_REQUEST_TEXT_LENGTH,
73
+ origin: "string",
74
+ inclusive: true,
75
+ message: "Combined text input is too large.",
76
+ path: ["inputs"],
77
+ });
78
+ }
79
+ })
80
+ .meta({
81
+ description: "Request body for POST /agent-native/embeddings/v1/embeddings.",
82
+ });
83
+ export const BuilderEmbeddingsResponseSchema = z.object({
84
+ id: z.string(),
85
+ object: z.literal("list"),
86
+ model: z.object({
87
+ publicId: z.literal(BUILDER_EMBEDDING_MODEL),
88
+ provider: z.literal("voyage"),
89
+ providerModel: z.literal("voyage-multimodal-3.5"),
90
+ version: z.literal("3.5"),
91
+ dimensions: z.literal(BUILDER_EMBEDDING_DIMENSIONS),
92
+ }),
93
+ inputType: z.enum(["query", "document"]),
94
+ data: z.array(z.object({
95
+ object: z.literal("embedding"),
96
+ index: z.number().int().nonnegative(),
97
+ embedding: z
98
+ .array(z.number().finite())
99
+ .length(BUILDER_EMBEDDING_DIMENSIONS),
100
+ })),
101
+ usage: z.object({
102
+ textTokens: z.number().int().nonnegative(),
103
+ imagePixels: z.number().int().nonnegative(),
104
+ totalTokens: z.number().int().nonnegative(),
105
+ }),
106
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,43 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { BUILDER_EMBEDDING_MODEL, BuilderEmbeddingsRequestSchema, } from "./embeddings";
3
+ describe("BuilderEmbeddingsRequestSchema", () => {
4
+ it("accepts retrieval text and multimodal inputs", () => {
5
+ var _a;
6
+ const parsed = BuilderEmbeddingsRequestSchema.parse({
7
+ model: BUILDER_EMBEDDING_MODEL,
8
+ inputType: "document",
9
+ inputs: [
10
+ { text: "A product launch slide" },
11
+ {
12
+ text: "A warm editorial campaign",
13
+ images: [{ mimeType: "image/png", data: "aGVsbG8=" }],
14
+ },
15
+ ],
16
+ });
17
+ expect(parsed.inputs).toHaveLength(2);
18
+ expect((_a = parsed.inputs[1]) === null || _a === void 0 ? void 0 : _a.images).toHaveLength(1);
19
+ });
20
+ it("rejects empty inputs and data URLs", () => {
21
+ expect(() => BuilderEmbeddingsRequestSchema.parse({ inputs: [{}] })).toThrow(/text, an image, or both/);
22
+ expect(() => BuilderEmbeddingsRequestSchema.parse({
23
+ inputs: [
24
+ {
25
+ images: [
26
+ {
27
+ mimeType: "image/png",
28
+ data: "data:image/png;base64,aGVsbG8=",
29
+ },
30
+ ],
31
+ },
32
+ ],
33
+ })).toThrow(/raw base64/);
34
+ });
35
+ it("rejects unsupported image formats and oversized batches", () => {
36
+ expect(() => BuilderEmbeddingsRequestSchema.parse({
37
+ inputs: [{ images: [{ mimeType: "image/svg+xml", data: "PHN2Zz4=" }] }],
38
+ })).toThrow();
39
+ expect(() => BuilderEmbeddingsRequestSchema.parse({
40
+ inputs: Array.from({ length: 33 }, () => ({ text: "text" })),
41
+ })).toThrow();
42
+ });
43
+ });
package/src/events.d.ts CHANGED
@@ -1296,7 +1296,33 @@ export declare const McpPrototypePulledV1: {
1296
1296
  eventName: "mcp.prototype.pulled";
1297
1297
  version: "1";
1298
1298
  };
1299
- export type FusionEvent = ClientDevtoolsSessionStartedEvent | ClientDevtoolsSessionIdleEventV1 | ClientDevtoolsToolCallRequestV1 | ClientDevtoolsToolCallV1 | ClientDevtoolsToolResultV1 | ClientDevtoolsBuildMigratedV1 | ClientDevtoolsBuildCompletedV1 | ClientDevtoolsBuildUploadedV1 | ClientDevtoolsBuildFailedV1 | FusionProjectCreatedV1 | SetupAgentCompletedV1 | GitPrMergedV1 | GitPrCreatedV1 | GitPrClosedV1 | ForceSetupAgentV1 | ClawMessageSentV1 | CodegenCompletionV1 | CodegenUserPromptV1 | GitWebhooksRegisterV1 | FusionProjectSettingsUpdatedV1 | VideoRecordingCompletedV1 | TimelineRecordingReadyV1 | FusionBranchCreatedV1 | FusionContainerStartedV1 | FusionContainerFailedV1 | FusionBranchFailedV1 | BotMentionGitHubExternalPrV1 | BotMentionGitHubInternalPrV1 | BotMentionGitLabPrV1 | BotMentionBitbucketPrV1 | BotMentionAzurePrV1 | ReviewSubmittedV1 | PrReviewRequestedV1 | FigmaDecodeJobV1 | ProjectSnapshotRefreshV1 | ProjectSnapshotCapturedV1 | ProjectSnapshotCreatedV1 | ProjectSnapshotFailedV1 | ProjectSnapshotReadyCheckV1 | ProjectSnapshotPodWatchV1 | McpPrototypePulledV1;
1299
+ export type HostingCustomDomainCertCheckV1 = FusionEventVariant<"hosting.custom-domain.cert-check", {
1300
+ domain: string;
1301
+ projectId: string;
1302
+ certId: string;
1303
+ startedAtMs: number;
1304
+ timeoutMs: number;
1305
+ }, {
1306
+ projectId: string;
1307
+ }, 1>;
1308
+ export declare const HostingCustomDomainCertCheckV1: {
1309
+ eventName: "hosting.custom-domain.cert-check";
1310
+ version: "1";
1311
+ };
1312
+ export type HostingCustomDomainDelegationCheckV1 = FusionEventVariant<"hosting.custom-domain.delegation-check", {
1313
+ domain: string;
1314
+ projectId: string;
1315
+ attemptId: string;
1316
+ startedAtMs: number;
1317
+ timeoutMs: number;
1318
+ }, {
1319
+ projectId: string;
1320
+ }, 1>;
1321
+ export declare const HostingCustomDomainDelegationCheckV1: {
1322
+ eventName: "hosting.custom-domain.delegation-check";
1323
+ version: "1";
1324
+ };
1325
+ export type FusionEvent = ClientDevtoolsSessionStartedEvent | ClientDevtoolsSessionIdleEventV1 | ClientDevtoolsToolCallRequestV1 | ClientDevtoolsToolCallV1 | ClientDevtoolsToolResultV1 | ClientDevtoolsBuildMigratedV1 | ClientDevtoolsBuildCompletedV1 | ClientDevtoolsBuildUploadedV1 | ClientDevtoolsBuildFailedV1 | FusionProjectCreatedV1 | SetupAgentCompletedV1 | GitPrMergedV1 | GitPrCreatedV1 | GitPrClosedV1 | ForceSetupAgentV1 | ClawMessageSentV1 | CodegenCompletionV1 | CodegenUserPromptV1 | GitWebhooksRegisterV1 | FusionProjectSettingsUpdatedV1 | VideoRecordingCompletedV1 | TimelineRecordingReadyV1 | FusionBranchCreatedV1 | FusionContainerStartedV1 | FusionContainerFailedV1 | FusionBranchFailedV1 | BotMentionGitHubExternalPrV1 | BotMentionGitHubInternalPrV1 | BotMentionGitLabPrV1 | BotMentionBitbucketPrV1 | BotMentionAzurePrV1 | ReviewSubmittedV1 | PrReviewRequestedV1 | FigmaDecodeJobV1 | ProjectSnapshotRefreshV1 | ProjectSnapshotCapturedV1 | ProjectSnapshotCreatedV1 | ProjectSnapshotFailedV1 | ProjectSnapshotReadyCheckV1 | ProjectSnapshotPodWatchV1 | McpPrototypePulledV1 | HostingCustomDomainCertCheckV1 | HostingCustomDomainDelegationCheckV1;
1300
1326
  export interface ModelPermissionRequiredEvent {
1301
1327
  type: "assistant.model.permission.required";
1302
1328
  data: {
package/src/events.js CHANGED
@@ -162,3 +162,11 @@ export const McpPrototypePulledV1 = {
162
162
  eventName: "mcp.prototype.pulled",
163
163
  version: "1",
164
164
  };
165
+ export const HostingCustomDomainCertCheckV1 = {
166
+ eventName: "hosting.custom-domain.cert-check",
167
+ version: "1",
168
+ };
169
+ export const HostingCustomDomainDelegationCheckV1 = {
170
+ eventName: "hosting.custom-domain.delegation-check",
171
+ version: "1",
172
+ };
package/src/index.d.ts CHANGED
@@ -21,4 +21,5 @@ export * from "./single-tenancy.js";
21
21
  export * from "./design-systems.js";
22
22
  export * from "./editor-ai.js";
23
23
  export * from "./realtime.js";
24
+ export * from "./embeddings.js";
24
25
  export { connectivityErrorCodeToLikelyCause, mapConnectivityErrorMessage, } from "./connectivity/error-codes.js";
package/src/index.js CHANGED
@@ -21,4 +21,5 @@ export * from "./single-tenancy.js";
21
21
  export * from "./design-systems.js";
22
22
  export * from "./editor-ai.js";
23
23
  export * from "./realtime.js";
24
+ export * from "./embeddings.js";
24
25
  export { connectivityErrorCodeToLikelyCause, mapConnectivityErrorMessage, } from "./connectivity/error-codes.js";
@@ -1,5 +1,11 @@
1
1
  import type { PrivacyMode, ReviewEffort } from "./codegen";
2
2
  import type { EnvironmentVariable } from "./common-schemas";
3
+ export interface GitlabEnterpriseSetupValue {
4
+ host: string;
5
+ secondaryHost?: string;
6
+ setupType: "oauth" | "pat";
7
+ clientId?: string;
8
+ }
3
9
  export interface GithubEnterpriseSetupValue {
4
10
  host: string;
5
11
  clientId: string;
@@ -19,6 +25,8 @@ export interface GitlabEnterprisePATValue {
19
25
  host: string;
20
26
  botUsername: string;
21
27
  secondaryHost?: string;
28
+ createdBy?: string;
29
+ createdAt?: number;
22
30
  }
23
31
  export interface GitlabCloudFallbackToken {
24
32
  token: string;
@@ -79,6 +87,7 @@ interface OrganizationSettings {
79
87
  isUserPluginIntegrationRequestGranted?: boolean;
80
88
  shopify?: boolean;
81
89
  githubEnterpriseSetupValue?: GithubEnterpriseSetupValue;
90
+ gitlabEnterpriseSetupValue?: GitlabEnterpriseSetupValue;
82
91
  gitlabEnterprisePAT?: GitlabEnterprisePATValue;
83
92
  gitlabCloudFallbackToken?: GitlabCloudFallbackToken;
84
93
  azureCloudFallbackToken?: AzureCloudFallbackToken;
@@ -0,0 +1,116 @@
1
+ import { z } from "zod";
2
+ export declare const CURRENT_PERF_REPORT_SCHEMA_VERSION = 1;
3
+ export declare const PerfIncidentTypeSchema: z.ZodEnum<{
4
+ hang: "hang";
5
+ "memory-pressure": "memory-pressure";
6
+ "recovered-hang": "recovered-hang";
7
+ "recovered-unclean-exit": "recovered-unclean-exit";
8
+ "renderer-crash": "renderer-crash";
9
+ }>;
10
+ export type PerfIncidentType = z.infer<typeof PerfIncidentTypeSchema>;
11
+ export declare const PerfProcessSchema: z.ZodObject<{
12
+ kind: z.ZodEnum<{
13
+ browser: "browser";
14
+ child: "child";
15
+ gpu: "gpu";
16
+ renderer: "renderer";
17
+ utility: "utility";
18
+ }>;
19
+ pid: z.ZodNumber;
20
+ memoryKb: z.ZodNumber;
21
+ cpuPercent: z.ZodNumber;
22
+ tabId: z.ZodOptional<z.ZodString>;
23
+ }, z.core.$strip>;
24
+ export type PerfProcess = z.infer<typeof PerfProcessSchema>;
25
+ export declare const PerfTabSchema: z.ZodObject<{
26
+ tabId: z.ZodString;
27
+ route: z.ZodOptional<z.ZodString>;
28
+ feature: z.ZodOptional<z.ZodString>;
29
+ jsHeapUsedKb: z.ZodOptional<z.ZodNumber>;
30
+ jsHeapLimitKb: z.ZodOptional<z.ZodNumber>;
31
+ subsystems: z.ZodRecord<z.ZodString, z.ZodNumber>;
32
+ livenessLatencyMs: z.ZodOptional<z.ZodNumber>;
33
+ }, z.core.$strip>;
34
+ export type PerfTab = z.infer<typeof PerfTabSchema>;
35
+ export declare const PerfSampleSchema: z.ZodObject<{
36
+ t: z.ZodNumber;
37
+ desktopFootprintKb: z.ZodNumber;
38
+ freeMemoryKb: z.ZodNumber;
39
+ perTabHeapKb: z.ZodRecord<z.ZodString, z.ZodNumber>;
40
+ }, z.core.$strip>;
41
+ export type PerfSample = z.infer<typeof PerfSampleSchema>;
42
+ export declare const PerfReportSchema: z.ZodObject<{
43
+ schemaVersion: z.ZodNumber;
44
+ incidentType: z.ZodEnum<{
45
+ hang: "hang";
46
+ "memory-pressure": "memory-pressure";
47
+ "recovered-hang": "recovered-hang";
48
+ "recovered-unclean-exit": "recovered-unclean-exit";
49
+ "renderer-crash": "renderer-crash";
50
+ }>;
51
+ sessionId: z.ZodString;
52
+ deviceId: z.ZodString;
53
+ appVersion: z.ZodString;
54
+ platform: z.ZodEnum<{
55
+ darwin: "darwin";
56
+ linux: "linux";
57
+ win32: "win32";
58
+ }>;
59
+ channel: z.ZodEnum<{
60
+ alpha: "alpha";
61
+ stable: "stable";
62
+ }>;
63
+ timestamp: z.ZodNumber;
64
+ machine: z.ZodObject<{
65
+ totalMemoryKb: z.ZodNumber;
66
+ freeMemoryKb: z.ZodNumber;
67
+ desktopFootprintKb: z.ZodNumber;
68
+ }, z.core.$strip>;
69
+ processes: z.ZodArray<z.ZodObject<{
70
+ kind: z.ZodEnum<{
71
+ browser: "browser";
72
+ child: "child";
73
+ gpu: "gpu";
74
+ renderer: "renderer";
75
+ utility: "utility";
76
+ }>;
77
+ pid: z.ZodNumber;
78
+ memoryKb: z.ZodNumber;
79
+ cpuPercent: z.ZodNumber;
80
+ tabId: z.ZodOptional<z.ZodString>;
81
+ }, z.core.$strip>>;
82
+ tabs: z.ZodArray<z.ZodObject<{
83
+ tabId: z.ZodString;
84
+ route: z.ZodOptional<z.ZodString>;
85
+ feature: z.ZodOptional<z.ZodString>;
86
+ jsHeapUsedKb: z.ZodOptional<z.ZodNumber>;
87
+ jsHeapLimitKb: z.ZodOptional<z.ZodNumber>;
88
+ subsystems: z.ZodRecord<z.ZodString, z.ZodNumber>;
89
+ livenessLatencyMs: z.ZodOptional<z.ZodNumber>;
90
+ }, z.core.$strip>>;
91
+ crash: z.ZodOptional<z.ZodObject<{
92
+ reason: z.ZodString;
93
+ exitCode: z.ZodOptional<z.ZodNumber>;
94
+ oom: z.ZodOptional<z.ZodBoolean>;
95
+ }, z.core.$strip>>;
96
+ hang: z.ZodOptional<z.ZodObject<{
97
+ startedAt: z.ZodNumber;
98
+ durationMs: z.ZodNumber;
99
+ recovered: z.ZodBoolean;
100
+ surface: z.ZodString;
101
+ }, z.core.$strip>>;
102
+ samples: z.ZodArray<z.ZodObject<{
103
+ t: z.ZodNumber;
104
+ desktopFootprintKb: z.ZodNumber;
105
+ freeMemoryKb: z.ZodNumber;
106
+ perTabHeapKb: z.ZodRecord<z.ZodString, z.ZodNumber>;
107
+ }, z.core.$strip>>;
108
+ }, z.core.$strip>;
109
+ export type PerfReport = z.infer<typeof PerfReportSchema>;
110
+ /**
111
+ * Collapses opaque identifiers out of a route/URL so a report can't carry a
112
+ * project/space id. Keeps the structural shape (which screen was open) while
113
+ * masking the specific resource.
114
+ */
115
+ export declare function redactPerfRoute(route: string | undefined): string | undefined;
116
+ export declare function redactPerfReport(report: PerfReport): PerfReport;
@@ -0,0 +1,122 @@
1
+ import { z } from "zod";
2
+ // ============================================================================
3
+ // Desktop performance & crash monitoring wire contract.
4
+ //
5
+ // Canonical schema. The Electron desktop app lives in a separate workspace and
6
+ // cannot import this file, so it keeps a hand-mirrored TypeScript type in
7
+ // code/packages/electron-app/src/perf-report.types.ts. Keep the two in sync and
8
+ // bump CURRENT_PERF_REPORT_SCHEMA_VERSION on any breaking shape change.
9
+ // ============================================================================
10
+ export const CURRENT_PERF_REPORT_SCHEMA_VERSION = 1;
11
+ export const PerfIncidentTypeSchema = z.enum([
12
+ "renderer-crash",
13
+ "hang",
14
+ "recovered-hang",
15
+ "memory-pressure",
16
+ "recovered-unclean-exit",
17
+ ]);
18
+ export const PerfProcessSchema = z.object({
19
+ kind: z.enum(["browser", "renderer", "gpu", "utility", "child"]),
20
+ pid: z.number(),
21
+ memoryKb: z.number(),
22
+ cpuPercent: z.number(),
23
+ tabId: z.string().optional(),
24
+ });
25
+ export const PerfTabSchema = z.object({
26
+ tabId: z.string(),
27
+ route: z.string().optional(),
28
+ feature: z.string().optional(),
29
+ jsHeapUsedKb: z.number().optional(),
30
+ jsHeapLimitKb: z.number().optional(),
31
+ subsystems: z.record(z.string(), z.number()),
32
+ livenessLatencyMs: z.number().optional(),
33
+ });
34
+ export const PerfSampleSchema = z.object({
35
+ t: z.number(),
36
+ desktopFootprintKb: z.number(),
37
+ freeMemoryKb: z.number(),
38
+ perTabHeapKb: z.record(z.string(), z.number()),
39
+ });
40
+ export const PerfReportSchema = z.object({
41
+ schemaVersion: z.number(),
42
+ incidentType: PerfIncidentTypeSchema,
43
+ sessionId: z.string(),
44
+ deviceId: z.string(),
45
+ appVersion: z.string(),
46
+ platform: z.enum(["darwin", "win32", "linux"]),
47
+ channel: z.enum(["stable", "alpha"]),
48
+ timestamp: z.number(),
49
+ machine: z.object({
50
+ totalMemoryKb: z.number(),
51
+ freeMemoryKb: z.number(),
52
+ desktopFootprintKb: z.number(),
53
+ }),
54
+ processes: z.array(PerfProcessSchema),
55
+ tabs: z.array(PerfTabSchema),
56
+ crash: z
57
+ .object({
58
+ reason: z.string(),
59
+ exitCode: z.number().optional(),
60
+ oom: z.boolean().optional(),
61
+ })
62
+ .optional(),
63
+ hang: z
64
+ .object({
65
+ startedAt: z.number(),
66
+ durationMs: z.number(),
67
+ recovered: z.boolean(),
68
+ surface: z.string(),
69
+ })
70
+ .optional(),
71
+ samples: z.array(PerfSampleSchema),
72
+ });
73
+ // ---------------------------------------------------------------------------
74
+ // Route/URL redaction. The desktop client redacts before sending; this runs
75
+ // again at ingest (defense in depth) because the service cannot import the
76
+ // renderer's redactSnapshot helper across the workspace boundary.
77
+ // ---------------------------------------------------------------------------
78
+ const PROJECT_ID_SEGMENTS = new Set([
79
+ "projects",
80
+ "content",
81
+ "fusion",
82
+ "space",
83
+ "org",
84
+ "organization",
85
+ ]);
86
+ /**
87
+ * Collapses opaque identifiers out of a route/URL so a report can't carry a
88
+ * project/space id. Keeps the structural shape (which screen was open) while
89
+ * masking the specific resource.
90
+ */
91
+ export function redactPerfRoute(route) {
92
+ if (!route) {
93
+ return route;
94
+ }
95
+ const [pathPart] = route.split("?");
96
+ const segments = pathPart.split("/");
97
+ const redacted = segments.map((segment, index) => {
98
+ var _a;
99
+ if (!segment) {
100
+ return segment;
101
+ }
102
+ const previous = (_a = segments[index - 1]) === null || _a === void 0 ? void 0 : _a.toLowerCase();
103
+ if (previous && PROJECT_ID_SEGMENTS.has(previous)) {
104
+ return ":id";
105
+ }
106
+ // Bare id-looking segments (uuids, long hex/base62 tokens).
107
+ if (/^[0-9a-f]{16,}$/i.test(segment) || /^[0-9a-z]{20,}$/i.test(segment)) {
108
+ return ":id";
109
+ }
110
+ return segment;
111
+ });
112
+ return redacted.join("/");
113
+ }
114
+ export function redactPerfReport(report) {
115
+ return {
116
+ ...report,
117
+ tabs: report.tabs.map((tab) => ({
118
+ ...tab,
119
+ route: redactPerfRoute(tab.route),
120
+ })),
121
+ };
122
+ }