@builder.io/ai-utils 0.77.0 → 0.79.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@builder.io/ai-utils",
3
- "version": "0.77.0",
3
+ "version": "0.79.1",
4
4
  "description": "Builder.io AI utils",
5
5
  "files": [
6
6
  "src"
package/src/codegen.d.ts CHANGED
@@ -1213,7 +1213,6 @@ export declare const SpawnBranchToolInputSchema: z.ZodObject<{
1213
1213
  }>>;
1214
1214
  attachment_urls: z.ZodOptional<z.ZodArray<z.ZodString>>;
1215
1215
  git_base_branch: z.ZodOptional<z.ZodString>;
1216
- auto_archive_on_idle: z.ZodOptional<z.ZodBoolean>;
1217
1216
  }, z.core.$strip>;
1218
1217
  export type SpawnBranchToolInput = z.infer<typeof SpawnBranchToolInputSchema>;
1219
1218
  export declare const CreateProjectToolInputSchema: z.ZodObject<{
@@ -1925,7 +1924,6 @@ export declare const CodeGenToolMapSchema: z.ZodObject<{
1925
1924
  }>>;
1926
1925
  attachment_urls: z.ZodOptional<z.ZodArray<z.ZodString>>;
1927
1926
  git_base_branch: z.ZodOptional<z.ZodString>;
1928
- auto_archive_on_idle: z.ZodOptional<z.ZodBoolean>;
1929
1927
  }, z.core.$strip>;
1930
1928
  CreateProject: z.ZodObject<{
1931
1929
  repo_url: z.ZodString;
@@ -3809,6 +3807,7 @@ export interface GenerateCodeEventUser {
3809
3807
  id: string;
3810
3808
  previousId: string | undefined;
3811
3809
  displayPrompt: string | undefined;
3810
+ attachments?: Array<Attachment>;
3812
3811
  user: UserSource;
3813
3812
  compacting: boolean;
3814
3813
  sessionMode: SessionMode;
@@ -3995,6 +3994,12 @@ export interface WorkspaceFolder {
3995
3994
  name?: string;
3996
3995
  originalRepoUrl?: string;
3997
3996
  repoUrl?: string;
3997
+ /**
3998
+ * Read-only source to clone when bootstrapping the repo. When set, the CLI clones
3999
+ * this instead of `repoUrl`, then sets origin to `repoUrl` (the real push remote).
4000
+ * Optional, so older CLIs/configs that ignore it stay compatible.
4001
+ */
4002
+ templateRepoUrl?: string;
3998
4003
  repoName?: string;
3999
4004
  resetGit?: boolean;
4000
4005
  branchName?: string;
@@ -4022,6 +4027,8 @@ export interface GitRepoContext {
4022
4027
  repoUrl?: string;
4023
4028
  /** Clean repo URL without auth tokens, for stable storage and refs */
4024
4029
  originalRepoUrl?: string;
4030
+ /** Read-only source to clone when bootstrapping (distinct from the push remote). */
4031
+ templateRepoUrl?: string;
4025
4032
  /** Repo name (e.g., owner/repo) */
4026
4033
  repoName?: string;
4027
4034
  /**
@@ -4170,6 +4177,8 @@ export interface FusionConfig {
4170
4177
  */
4171
4178
  figmaDecodeJobId?: string;
4172
4179
  featureBranch?: string;
4180
+ /** When set, init checks out this exact git commit after cloning featureBranch. */
4181
+ checkoutCommit?: string;
4173
4182
  aiBranch?: string;
4174
4183
  /** Whether this is a fork PR - affects git operations (read-only, can't push) */
4175
4184
  isFork?: boolean;
package/src/codegen.js CHANGED
@@ -1247,7 +1247,6 @@ export const SpawnBranchToolInputSchema = z
1247
1247
  model: z.enum(["auto", "opus", "sonnet", "haiku"]).optional(),
1248
1248
  attachment_urls: z.array(z.string()).optional(),
1249
1249
  git_base_branch: z.string().optional(),
1250
- auto_archive_on_idle: z.boolean().optional(),
1251
1250
  })
1252
1251
  .meta({ title: "SpawnBranchToolInput" });
1253
1252
  export const CreateProjectToolInputSchema = z
@@ -53,7 +53,7 @@ export interface GenerateDesignSystemErrorResponse {
53
53
  }
54
54
  export declare const GENERATE_DESIGN_SYSTEM_MAX_FILE_BYTES: number;
55
55
  export declare const GENERATE_DESIGN_SYSTEM_MAX_ATTACHMENTS = 50;
56
- export declare const GENERATE_DESIGN_SYSTEM_MIN_ATTACHMENTS = 1;
56
+ export declare const GENERATE_DESIGN_SYSTEM_MIN_ATTACHMENTS = 0;
57
57
  export declare const generateDesignSystemBodySchema: z.ZodObject<{
58
58
  projectName: z.ZodOptional<z.ZodString>;
59
59
  devToolsVersion: z.ZodOptional<z.ZodString>;
@@ -7,7 +7,7 @@ import { z } from "zod";
7
7
  export const GITHUB_REPO_URL_REGEX = /^https:\/\/github\.com\/[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+(?:\.git)?\/?$/;
8
8
  export const GENERATE_DESIGN_SYSTEM_MAX_FILE_BYTES = 2 * 1024 * 1024 * 1024;
9
9
  export const GENERATE_DESIGN_SYSTEM_MAX_ATTACHMENTS = 50;
10
- export const GENERATE_DESIGN_SYSTEM_MIN_ATTACHMENTS = 1;
10
+ export const GENERATE_DESIGN_SYSTEM_MIN_ATTACHMENTS = 0;
11
11
  export const generateDesignSystemBodySchema = z.object({
12
12
  projectName: z.string().trim().min(1).max(200).optional(),
13
13
  devToolsVersion: z.string().trim().min(1).max(64).optional(),
package/src/events.d.ts CHANGED
@@ -1219,6 +1219,18 @@ export declare const ClientDevtoolsToolResultV1: {
1219
1219
  eventName: "client.devtools.tool.result";
1220
1220
  version: "1";
1221
1221
  };
1222
+ export type ClientDevtoolsBuildMigratedV1 = FusionEventVariant<"client.devtools.build.migrated", {
1223
+ deployId: string;
1224
+ projectId: string;
1225
+ migrationApplied?: boolean;
1226
+ }, {
1227
+ projectId: string;
1228
+ deployId: string;
1229
+ }, 1>;
1230
+ export declare const ClientDevtoolsBuildMigratedV1: {
1231
+ eventName: "client.devtools.build.migrated";
1232
+ version: "1";
1233
+ };
1222
1234
  export type ClientDevtoolsBuildCompletedV1 = FusionEventVariant<"client.devtools.build.completed", {
1223
1235
  deployId: string;
1224
1236
  projectId: string;
@@ -1233,6 +1245,16 @@ export declare const ClientDevtoolsBuildCompletedV1: {
1233
1245
  export type ClientDevtoolsBuildUploadedV1 = FusionEventVariant<"client.devtools.build.uploaded", {
1234
1246
  deployId: string;
1235
1247
  projectId: string;
1248
+ /** Git commit SHA the deploy pod built (git rev-parse HEAD). */
1249
+ gitCommit?: string;
1250
+ /**
1251
+ * Production env from the repo's `builder.config.json` (`hosting.environment.prod`).
1252
+ * The file only exists on the deploy pod, so the pod transports it here for the
1253
+ * service to merge with the Firestore prod env. These are committed plaintext
1254
+ * values with no secret concept; Firestore prod env (which can be secret) is read
1255
+ * directly in the handler and is not sent through the event.
1256
+ */
1257
+ prodEnvFromBuilderConfig?: Record<string, string>;
1236
1258
  }, {
1237
1259
  projectId: string;
1238
1260
  deployId: string;
@@ -1264,7 +1286,7 @@ export declare const McpPrototypePulledV1: {
1264
1286
  eventName: "mcp.prototype.pulled";
1265
1287
  version: "1";
1266
1288
  };
1267
- export type FusionEvent = ClientDevtoolsSessionStartedEvent | ClientDevtoolsSessionIdleEventV1 | ClientDevtoolsToolCallRequestV1 | ClientDevtoolsToolCallV1 | ClientDevtoolsToolResultV1 | 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;
1289
+ 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;
1268
1290
  export interface ModelPermissionRequiredEvent {
1269
1291
  type: "assistant.model.permission.required";
1270
1292
  data: {
package/src/events.js CHANGED
@@ -142,6 +142,10 @@ export const ClientDevtoolsToolResultV1 = {
142
142
  eventName: "client.devtools.tool.result",
143
143
  version: "1",
144
144
  };
145
+ export const ClientDevtoolsBuildMigratedV1 = {
146
+ eventName: "client.devtools.build.migrated",
147
+ version: "1",
148
+ };
145
149
  export const ClientDevtoolsBuildCompletedV1 = {
146
150
  eventName: "client.devtools.build.completed",
147
151
  version: "1",
@@ -71,6 +71,8 @@ interface OrganizationSettings {
71
71
  enableOrgInsights?: boolean;
72
72
  enableBuilderHosting?: boolean;
73
73
  enableFusionHosting?: boolean;
74
+ netlifyAXPartnerAccountId?: string;
75
+ netlifyAXPartnerAccountSlug?: string;
74
76
  allowLegacyPlanSubscriptions?: boolean;
75
77
  enforceMaxUsers?: boolean;
76
78
  ssoRestrictedMode?: boolean;
package/src/projects.d.ts CHANGED
@@ -395,9 +395,13 @@ export interface PartialBranchData {
395
395
  lastCommitHash?: GitSnapshot | null;
396
396
  lastCommitDate?: number | null;
397
397
  commitMode?: CommitMode;
398
+ /** @internal When false, merging this branch's PR does not archive it. Seeded from project.settings at creation. */
399
+ autoCloseBranchOnMerge?: boolean;
398
400
  useHomeDir?: boolean;
399
401
  agentType?: AgentType;
400
402
  checkoutBranch?: string | null;
403
+ /** When set, the deploy pod checks out this exact git commit after cloning checkoutBranch. */
404
+ checkoutCommit?: string | null;
401
405
  prNumber?: number;
402
406
  /** Whether this branch is for a fork PR - affects git operations (read-only, can't push) */
403
407
  isFork?: boolean | null;
@@ -549,12 +553,16 @@ export interface BranchSharedData {
549
553
  /** Public kube hostnames for this branch (no scheme), when more than one domain is available. */
550
554
  kubeHostnames?: string[] | null;
551
555
  checkoutBranch?: string | null;
556
+ /** When set, the deploy pod checks out this exact git commit after cloning checkoutBranch. */
557
+ checkoutCommit?: string | null;
552
558
  /** Whether this branch is for a fork PR - affects git operations (read-only, can't push) */
553
559
  isFork?: boolean | null;
554
560
  vscodeTunnelUrl?: string | null;
555
561
  vscodeTunnelName?: string | null;
556
562
  vscodeTunnelExpiresAt?: string | null;
557
563
  commitMode?: CommitMode;
564
+ /** @internal When false, merging this branch's PR does not archive it. Seeded from project.settings at creation. */
565
+ autoCloseBranchOnMerge?: boolean;
558
566
  /** Timestamp when an AI agent started activity on this branch, null when inactive (in milliseconds since epoch) */
559
567
  lastAgentActivityAt?: number | null;
560
568
  /** Timestamp of last activity on this branch - includes both agent and user activity (in milliseconds since epoch) */
@@ -755,6 +763,10 @@ export interface Project {
755
763
  customAgents?: CustomAgentDefinition[];
756
764
  /** How the AI commits changes: direct commits, draft PRs, or ready-for-review PRs. Default: "draft-prs". */
757
765
  commitMode?: CommitMode;
766
+ /** @internal Default for new branches' autoCloseBranchOnMerge. When false, merging a PR does not archive the branch. Read only at branch-creation time. Default: true. */
767
+ autoCloseBranchOnMerge?: boolean;
768
+ /** Whether merging the PR from the editor is enabled. */
769
+ enableMergePR?: boolean;
758
770
  /** Default visibility for new branches: shared (team-visible) or private. Default: "private". */
759
771
  defaultBranchType?: "shared" | "private";
760
772
  /** Custom container image path (e.g. ghcr.io/org/image:tag). Overrides nodeVersion when set. */
@@ -884,6 +896,8 @@ export interface Project {
884
896
  isFromUserTemplate?: boolean;
885
897
  /** ID of the template used to create this project, if any. */
886
898
  templateId?: string | null;
899
+ /** @internal Read-only source repo cloned to bootstrap the project. Immutable. Absent for non-template projects. Distinct from `repoUrl`, which is always the real push remote. */
900
+ templateRepoUrl?: string;
887
901
  /** Local filesystem path for desktop-app projects. */
888
902
  localPath?: string | null;
889
903
  /** The state of the project. Use `isProjectDeleted()` helper for backwards-compatible checks. */
@@ -996,6 +1010,29 @@ export declare const isBranchArchived: (branch: Branch) => boolean;
996
1010
  * Check if a project is deleted, supporting both `state` and legacy `deleted` fields.
997
1011
  */
998
1012
  export declare const isProjectDeleted: (project: Project) => boolean;
1013
+ /**
1014
+ * Canonical "is this project connected to a real remote" predicate. The single
1015
+ * source of truth for "connected", replacing scattered repoFullName-truthiness checks.
1016
+ */
1017
+ export declare const hasConnectedRepo: (project: Pick<Project, "repoUrl">) => boolean;
1018
+ /**
1019
+ * Derived state: a template-derived project that has a clone source but no real
1020
+ * remote yet. Eager creation (v0) keeps this false; it becomes meaningful under
1021
+ * future lazy repo creation.
1022
+ */
1023
+ export declare const isAwaitingRepoCreation: (project: Pick<Project, "templateRepoUrl" | "repoUrl">) => boolean;
1024
+ /**
1025
+ * Derived state: the clone source is a separate template repo, so origin must be
1026
+ * re-pointed to the real remote after cloning. Once the internal-host repo is
1027
+ * seeded the template URL is wiped, so this flips to false and clones target the
1028
+ * real remote directly.
1029
+ */
1030
+ export declare const clonesFromTemplate: (project: Pick<Project, "templateRepoUrl" | "repoUrl">) => boolean;
1031
+ /**
1032
+ * Fail-fast accessor for crash-tier call sites: returns the real remote or throws
1033
+ * a clear error instead of letting `new URL(undefined)` blow up downstream.
1034
+ */
1035
+ export declare const getRepoUrlOrThrow: (project: Pick<Project, "repoUrl">) => string;
999
1036
  export interface ProjectWithBranches extends Project {
1000
1037
  branches: Record<string, Branch>;
1001
1038
  }
@@ -1031,7 +1068,43 @@ export interface CreateProjectOptions {
1031
1068
  useInternalHost?: boolean;
1032
1069
  /** Store branches in the standalone `branches` collection vs embedded in the project doc. */
1033
1070
  useBranchesCollection?: boolean;
1071
+ /** @internal Read-only source repo cloned to bootstrap the project. Carried through to Firestore. */
1072
+ templateRepoUrl?: string;
1073
+ /** @internal First-class hosting config copied from the template. Carried through to Firestore. */
1074
+ hosting?: ProjectHosting;
1075
+ /** @internal When true, provision the internalHost repo eagerly at creation. Hardcoded true for v0. */
1076
+ eagerRepoCreate?: boolean;
1034
1077
  }
1078
+ /**
1079
+ * Source-agnostic description of where a new project comes from. A project can be
1080
+ * created from a plain repo URL (connected) or from a template id; template is just
1081
+ * one source kind. Consumed by the shared `createProjectFromSource` create path.
1082
+ */
1083
+ export declare const ProjectSourceSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
1084
+ kind: z.ZodLiteral<"repo">;
1085
+ repoUrl: z.ZodString;
1086
+ }, z.core.$strip>, z.ZodObject<{
1087
+ kind: z.ZodLiteral<"template">;
1088
+ templateId: z.ZodString;
1089
+ }, z.core.$strip>], "kind">;
1090
+ export type ProjectSource = z.infer<typeof ProjectSourceSchema>;
1091
+ /**
1092
+ * Request body for `POST /projects/create`. Identity/credentials come from the
1093
+ * authenticated request context, never the body.
1094
+ */
1095
+ export declare const CreateProjectRequestSchema: z.ZodObject<{
1096
+ source: z.ZodDiscriminatedUnion<[z.ZodObject<{
1097
+ kind: z.ZodLiteral<"repo">;
1098
+ repoUrl: z.ZodString;
1099
+ }, z.core.$strip>, z.ZodObject<{
1100
+ kind: z.ZodLiteral<"template">;
1101
+ templateId: z.ZodString;
1102
+ }, z.core.$strip>], "kind">;
1103
+ name: z.ZodString;
1104
+ initialMessage: z.ZodOptional<z.ZodString>;
1105
+ designSystems: z.ZodOptional<z.ZodArray<z.ZodString>>;
1106
+ }, z.core.$strip>;
1107
+ export type CreateProjectRequest = z.infer<typeof CreateProjectRequestSchema>;
1035
1108
  export interface BuilderMyContext {
1036
1109
  success: boolean;
1037
1110
  projectId: string;
@@ -1064,6 +1137,8 @@ export interface CreateBranchOptions {
1064
1137
  hidden?: boolean;
1065
1138
  featureFlags?: Record<string, boolean>;
1066
1139
  checkoutBranch?: string | null;
1140
+ /** When set, the deploy pod checks out this exact git commit after cloning checkoutBranch. */
1141
+ checkoutCommit?: string | null;
1067
1142
  canHandleTools?: (keyof CodeGenToolMap)[];
1068
1143
  skipPromptAnalysis?: boolean;
1069
1144
  useKube?: boolean;
@@ -1329,6 +1404,7 @@ export interface ShouldDeletePodResponse {
1329
1404
  /**
1330
1405
  * Deploy lifecycle status:
1331
1406
  * - queued - inital state although we don't currently queue deploys per se
1407
+ * - migrating — deploy pod is preparing production database state before the build
1332
1408
  * - building — deploy pod is running `npm run build`
1333
1409
  * - uploading — build succeeded; artifacts being uploaded to GCS (transient,
1334
1410
  * between `client.devtools.build.completed` and `client.devtools.build.uploaded`)
@@ -1345,12 +1421,13 @@ export declare const DeployStatusSchema: z.ZodEnum<{
1345
1421
  deploying: "deploying";
1346
1422
  failed: "failed";
1347
1423
  live: "live";
1424
+ migrating: "migrating";
1348
1425
  queued: "queued";
1349
1426
  uploading: "uploading";
1350
1427
  }>;
1351
1428
  export type DeployStatus = z.infer<typeof DeployStatusSchema>;
1352
1429
  /** Deploy statuses that are terminal — never transitioned out of. */
1353
- export declare const TERMINAL_DEPLOY_STATUSES: Set<"building" | "canceled" | "deploying" | "failed" | "live" | "queued" | "uploading">;
1430
+ export declare const TERMINAL_DEPLOY_STATUSES: Set<"building" | "canceled" | "deploying" | "failed" | "live" | "migrating" | "queued" | "uploading">;
1354
1431
  export declare const HostingPublishStatusSchema: z.ZodEnum<{
1355
1432
  published: "published";
1356
1433
  publishing: "publishing";
@@ -1406,14 +1483,21 @@ export declare const ProjectHostingSchema: z.ZodObject<{
1406
1483
  deploying: "deploying";
1407
1484
  failed: "failed";
1408
1485
  live: "live";
1486
+ migrating: "migrating";
1409
1487
  queued: "queued";
1410
1488
  uploading: "uploading";
1411
1489
  }>>;
1490
+ publishedCommit: z.ZodOptional<z.ZodString>;
1412
1491
  customDomain: z.ZodOptional<z.ZodString>;
1413
1492
  customDomainVerified: z.ZodOptional<z.ZodBoolean>;
1414
1493
  autoDeploy: z.ZodOptional<z.ZodBoolean>;
1415
1494
  }, z.core.$strip>;
1416
1495
  export type ProjectHosting = z.infer<typeof ProjectHostingSchema>;
1496
+ /**
1497
+ * Canonical "is this project hostable" predicate. Keys off the explicit
1498
+ * `hosting.enabled` flag rather than inferring from build-config presence.
1499
+ */
1500
+ export declare const isProjectHostable: (hosting: Pick<ProjectHosting, "enabled"> | undefined | null) => boolean;
1417
1501
  /**
1418
1502
  * A single deploy record, stored at `deploys/{deployId}`. The Deploy.id is the document key.
1419
1503
  */
@@ -1422,6 +1506,8 @@ export declare const DeploySchema: z.ZodObject<{
1422
1506
  projectId: z.ZodString;
1423
1507
  ownerId: z.ZodString;
1424
1508
  checkoutBranch: z.ZodOptional<z.ZodString>;
1509
+ checkoutCommit: z.ZodOptional<z.ZodString>;
1510
+ builtCommit: z.ZodOptional<z.ZodString>;
1425
1511
  deployBranchId: z.ZodOptional<z.ZodString>;
1426
1512
  deployBranchName: z.ZodOptional<z.ZodString>;
1427
1513
  devBranchId: z.ZodOptional<z.ZodString>;
@@ -1431,11 +1517,13 @@ export declare const DeploySchema: z.ZodObject<{
1431
1517
  deploying: "deploying";
1432
1518
  failed: "failed";
1433
1519
  live: "live";
1520
+ migrating: "migrating";
1434
1521
  queued: "queued";
1435
1522
  uploading: "uploading";
1436
1523
  }>;
1437
1524
  netlifyDeployId: z.ZodOptional<z.ZodString>;
1438
1525
  url: z.ZodOptional<z.ZodString>;
1526
+ migrationApplied: z.ZodOptional<z.ZodBoolean>;
1439
1527
  error: z.ZodOptional<z.ZodString>;
1440
1528
  createdAt: z.ZodNumber;
1441
1529
  completedAt: z.ZodOptional<z.ZodNumber>;
@@ -1491,12 +1579,16 @@ export type HostingLock = z.infer<typeof HostingLockSchema>;
1491
1579
  */
1492
1580
  export declare const DeployListItemSchema: z.ZodObject<{
1493
1581
  id: z.ZodString;
1582
+ checkoutBranch: z.ZodOptional<z.ZodString>;
1583
+ checkoutCommit: z.ZodOptional<z.ZodString>;
1584
+ builtCommit: z.ZodOptional<z.ZodString>;
1494
1585
  status: z.ZodEnum<{
1495
1586
  building: "building";
1496
1587
  canceled: "canceled";
1497
1588
  deploying: "deploying";
1498
1589
  failed: "failed";
1499
1590
  live: "live";
1591
+ migrating: "migrating";
1500
1592
  queued: "queued";
1501
1593
  uploading: "uploading";
1502
1594
  }>;
@@ -1562,6 +1654,7 @@ export type ReserveSlugResponse = z.infer<typeof ReserveSlugResponseSchema>;
1562
1654
  export declare const DeployRequestSchema: z.ZodObject<{
1563
1655
  projectId: z.ZodString;
1564
1656
  checkoutBranch: z.ZodOptional<z.ZodString>;
1657
+ checkoutCommit: z.ZodOptional<z.ZodString>;
1565
1658
  }, z.core.$strip>;
1566
1659
  export type DeployRequest = z.infer<typeof DeployRequestSchema>;
1567
1660
  export declare const DeployResponseSchema: z.ZodObject<{
@@ -1572,6 +1665,7 @@ export declare const DeployResponseSchema: z.ZodObject<{
1572
1665
  deploying: "deploying";
1573
1666
  failed: "failed";
1574
1667
  live: "live";
1668
+ migrating: "migrating";
1575
1669
  queued: "queued";
1576
1670
  uploading: "uploading";
1577
1671
  }>;
@@ -1593,12 +1687,16 @@ export type GetDeploysRequest = z.infer<typeof GetDeploysRequestSchema>;
1593
1687
  export declare const GetDeploysResponseSchema: z.ZodObject<{
1594
1688
  deploys: z.ZodArray<z.ZodObject<{
1595
1689
  id: z.ZodString;
1690
+ checkoutBranch: z.ZodOptional<z.ZodString>;
1691
+ checkoutCommit: z.ZodOptional<z.ZodString>;
1692
+ builtCommit: z.ZodOptional<z.ZodString>;
1596
1693
  status: z.ZodEnum<{
1597
1694
  building: "building";
1598
1695
  canceled: "canceled";
1599
1696
  deploying: "deploying";
1600
1697
  failed: "failed";
1601
1698
  live: "live";
1699
+ migrating: "migrating";
1602
1700
  queued: "queued";
1603
1701
  uploading: "uploading";
1604
1702
  }>;
package/src/projects.js CHANGED
@@ -88,6 +88,61 @@ export const isBranchArchived = (branch) => {
88
88
  export const isProjectDeleted = (project) => {
89
89
  return getProjectState(project) === "deleted";
90
90
  };
91
+ /**
92
+ * Canonical "is this project connected to a real remote" predicate. The single
93
+ * source of truth for "connected", replacing scattered repoFullName-truthiness checks.
94
+ */
95
+ export const hasConnectedRepo = (project) => {
96
+ return !!project.repoUrl;
97
+ };
98
+ /**
99
+ * Derived state: a template-derived project that has a clone source but no real
100
+ * remote yet. Eager creation (v0) keeps this false; it becomes meaningful under
101
+ * future lazy repo creation.
102
+ */
103
+ export const isAwaitingRepoCreation = (project) => {
104
+ return !!project.templateRepoUrl && !project.repoUrl;
105
+ };
106
+ /**
107
+ * Derived state: the clone source is a separate template repo, so origin must be
108
+ * re-pointed to the real remote after cloning. Once the internal-host repo is
109
+ * seeded the template URL is wiped, so this flips to false and clones target the
110
+ * real remote directly.
111
+ */
112
+ export const clonesFromTemplate = (project) => {
113
+ return (!!project.templateRepoUrl && project.templateRepoUrl !== project.repoUrl);
114
+ };
115
+ /**
116
+ * Fail-fast accessor for crash-tier call sites: returns the real remote or throws
117
+ * a clear error instead of letting `new URL(undefined)` blow up downstream.
118
+ */
119
+ export const getRepoUrlOrThrow = (project) => {
120
+ if (!project.repoUrl) {
121
+ throw new Error("Project repo has not been created yet");
122
+ }
123
+ return project.repoUrl;
124
+ };
125
+ /**
126
+ * Source-agnostic description of where a new project comes from. A project can be
127
+ * created from a plain repo URL (connected) or from a template id; template is just
128
+ * one source kind. Consumed by the shared `createProjectFromSource` create path.
129
+ */
130
+ export const ProjectSourceSchema = z.discriminatedUnion("kind", [
131
+ z.object({ kind: z.literal("repo"), repoUrl: z.string().url() }),
132
+ z.object({ kind: z.literal("template"), templateId: z.string() }),
133
+ ]);
134
+ /**
135
+ * Request body for `POST /projects/create`. Identity/credentials come from the
136
+ * authenticated request context, never the body.
137
+ */
138
+ export const CreateProjectRequestSchema = z.object({
139
+ source: ProjectSourceSchema,
140
+ name: z.string(),
141
+ /** Optional first message to start the agent with after creation. Template source only (current constraint). */
142
+ initialMessage: z.string().optional(),
143
+ /** Design system ids to seed into project settings at creation. */
144
+ designSystems: z.array(z.string()).optional(),
145
+ });
91
146
  export const MEMORY_PICKED_FIELDS = [
92
147
  "id",
93
148
  "updatedAt",
@@ -183,6 +238,7 @@ export function parseExitPlanMode(chunk) {
183
238
  /**
184
239
  * Deploy lifecycle status:
185
240
  * - queued - inital state although we don't currently queue deploys per se
241
+ * - migrating — deploy pod is preparing production database state before the build
186
242
  * - building — deploy pod is running `npm run build`
187
243
  * - uploading — build succeeded; artifacts being uploaded to GCS (transient,
188
244
  * between `client.devtools.build.completed` and `client.devtools.build.uploaded`)
@@ -196,6 +252,7 @@ export function parseExitPlanMode(chunk) {
196
252
  export const DeployStatusSchema = z.enum([
197
253
  "queued",
198
254
  "building",
255
+ "migrating",
199
256
  "uploading",
200
257
  "deploying",
201
258
  "live",
@@ -263,6 +320,8 @@ export const ProjectHostingSchema = z.object({
263
320
  lastDeployId: z.string().optional(),
264
321
  lastDeployAt: z.number().optional(),
265
322
  lastDeployStatus: DeployStatusSchema.optional(),
323
+ /** Git commit currently live at the project's hosting URL (set on go-live). */
324
+ publishedCommit: z.string().optional(),
266
325
  customDomain: z.string().optional(),
267
326
  customDomainVerified: z.boolean().optional(),
268
327
  autoDeploy: z
@@ -270,6 +329,13 @@ export const ProjectHostingSchema = z.object({
270
329
  .optional()
271
330
  .meta({ description: "auto-deploy on push/merge" }),
272
331
  });
332
+ /**
333
+ * Canonical "is this project hostable" predicate. Keys off the explicit
334
+ * `hosting.enabled` flag rather than inferring from build-config presence.
335
+ */
336
+ export const isProjectHostable = (hosting) => {
337
+ return (hosting === null || hosting === void 0 ? void 0 : hosting.enabled) === true;
338
+ };
273
339
  /**
274
340
  * A single deploy record, stored at `deploys/{deployId}`. The Deploy.id is the document key.
275
341
  */
@@ -285,6 +351,20 @@ export const DeploySchema = z.object({
285
351
  "for row 2 in v2). This is the value passed as `checkoutBranch` when the " +
286
352
  "deploy pod was created.",
287
353
  }),
354
+ checkoutCommit: z
355
+ .string()
356
+ .optional()
357
+ .meta({
358
+ description: "Exact git commit hash deployed. When set, the deploy pod pinned to this " +
359
+ "commit instead of the branch tip.",
360
+ }),
361
+ builtCommit: z
362
+ .string()
363
+ .optional()
364
+ .meta({
365
+ description: "Actual git commit hash built by the deploy pod (from git rev-parse HEAD " +
366
+ "after checkout). Set when build.uploaded fires.",
367
+ }),
288
368
  deployBranchId: z.string().optional().meta({
289
369
  description: 'Fusion branch id of the ephemeral hidden deploy branch (`type: "deploy"`) that ran the build.',
290
370
  }),
@@ -297,6 +377,9 @@ export const DeploySchema = z.object({
297
377
  status: DeployStatusSchema,
298
378
  netlifyDeployId: z.string().optional(),
299
379
  url: z.string().optional(),
380
+ migrationApplied: z.boolean().optional().meta({
381
+ description: "Whether Drizzle migrations were applied during this deploy. Absent when no migration ran.",
382
+ }),
300
383
  error: z.string().optional().meta({
301
384
  description: "Single human-readable line on failure; raw build logs not surfaced in v0",
302
385
  }),
@@ -363,6 +446,9 @@ export const DeployListItemSchema = DeploySchema.pick({
363
446
  createdAt: true,
364
447
  completedAt: true,
365
448
  error: true,
449
+ checkoutBranch: true,
450
+ checkoutCommit: true,
451
+ builtCommit: true,
366
452
  }).meta({ title: "DeployListItem" });
367
453
  /**
368
454
  * Shape of the `manifest.json` the deploy pod uploads to GCS alongside
@@ -488,9 +574,13 @@ export const ReserveSlugRequestSchema = z.object({
488
574
  export const ReserveSlugResponseSchema = z.object({
489
575
  slug: z.string(),
490
576
  });
577
+ const gitCommitHashSchema = z
578
+ .string()
579
+ .regex(/^[0-9a-f]{7,40}$/i, "Must be a valid git commit hash (7–40 hex chars)");
491
580
  export const DeployRequestSchema = z.object({
492
581
  projectId: z.string(),
493
582
  checkoutBranch: z.string().optional(),
583
+ checkoutCommit: gitCommitHashSchema.optional(),
494
584
  });
495
585
  export const DeployResponseSchema = z.object({
496
586
  deployId: z.string(),