@builder.io/ai-utils 0.85.0 → 0.85.2

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.85.0",
3
+ "version": "0.85.2",
4
4
  "description": "Builder.io AI utils",
5
5
  "files": [
6
6
  "src"
package/src/events.d.ts CHANGED
@@ -598,6 +598,8 @@ export type BotMentionGitHubInternalPrV1 = FusionEventVariant<"bot.mention.githu
598
598
  /** Numeric GitHub comment ID (for reaction / reply threading) */
599
599
  commentNumericId?: number;
600
600
  commentType: "issue" | "pr";
601
+ /** Whether the normal bot-mention pipeline should post successful agent text. */
602
+ postSuccessResultComment?: boolean;
601
603
  /** GitHub Enterprise hostname, if applicable */
602
604
  hostname?: string;
603
605
  /** For review comments: the comment being replied to */
@@ -450,6 +450,7 @@ interface Role {
450
450
  export interface Organization {
451
451
  name?: string;
452
452
  id?: string;
453
+ "@originId"?: string;
453
454
  createdDate?: number;
454
455
  intent?: string;
455
456
  ecommerceBackend?: string;
package/src/projects.d.ts CHANGED
@@ -231,6 +231,17 @@ export declare const STARTER_REPO = "BuilderIO/fusion-starter";
231
231
  export declare const DSI_PREVIEW_REPO = "BuilderIO/dsi-starter";
232
232
  export declare const DSI_PLACEHOLDER_REPO = "BuilderIO/dsi-placeholder";
233
233
  export declare const AGENT_NATIVE_STARTER_REPO = "BuilderIO/builder-agent-native-starter";
234
+ /**
235
+ * Whether a repo/workspace-folder refers to the Agent Native starter. Hostable
236
+ * Agent Native projects are born with their own `repoName` (the eagerly
237
+ * provisioned internal host), so the starter is only identifiable via
238
+ * `templateRepoUrl` — the read-only clone source. Matching either keeps both
239
+ * hostable and plain-clone projects working.
240
+ */
241
+ export declare const matchesAgentNativeStarter: (repo: {
242
+ repoName?: string | null;
243
+ templateRepoUrl?: string | null;
244
+ }) => boolean;
234
245
  export declare const EXAMPLE_OR_STARTER_REPOS: string[];
235
246
  export declare const EXAMPLE_OR_STARTER_REPOS_URLS: string[];
236
247
  export interface GitBackupUploadUrlResult {
@@ -628,6 +639,70 @@ export interface NewBranch extends BranchSharedData, RequiredBranchSharedData {
628
639
  */
629
640
  export type Branch = NewBranch | LegacyBranch;
630
641
  export declare const checkIsNewBranch: (branch: Branch) => branch is NewBranch;
642
+ /**
643
+ * Request options for `GET /projects/branches/list`. Owner/org scope comes from
644
+ * the authenticated request context; `ownerId` here is a superuser-only override.
645
+ */
646
+ export declare const ListBranchesOptionsSchema: z.ZodObject<{
647
+ projectId: z.ZodOptional<z.ZodString>;
648
+ projectIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
649
+ kanbanColumn: z.ZodOptional<z.ZodEnum<{
650
+ archived: "archived";
651
+ "awaiting-feedback": "awaiting-feedback";
652
+ "in-progress": "in-progress";
653
+ "in-review": "in-review";
654
+ merged: "merged";
655
+ }>>;
656
+ states: z.ZodOptional<z.ZodArray<z.ZodEnum<{
657
+ active: "active";
658
+ archived: "archived";
659
+ deleted: "deleted";
660
+ }>>>;
661
+ includeHidden: z.ZodOptional<z.ZodBoolean>;
662
+ search: z.ZodOptional<z.ZodString>;
663
+ category: z.ZodOptional<z.ZodArray<z.ZodEnum<{
664
+ feature: "feature";
665
+ fix: "fix";
666
+ other: "other";
667
+ research: "research";
668
+ }>>>;
669
+ prStatus: z.ZodOptional<z.ZodArray<z.ZodEnum<{
670
+ closed: "closed";
671
+ draft: "draft";
672
+ merged: "merged";
673
+ none: "none";
674
+ open: "open";
675
+ }>>>;
676
+ createdBy: z.ZodOptional<z.ZodString>;
677
+ assignedTo: z.ZodOptional<z.ZodString>;
678
+ notYetReviewed: z.ZodOptional<z.ZodBoolean>;
679
+ showOnlyRepoConnected: z.ZodOptional<z.ZodBoolean>;
680
+ sortBy: z.ZodOptional<z.ZodEnum<{
681
+ alphabetical: "alphabetical";
682
+ dateCreated: "dateCreated";
683
+ recentlyUpdated: "recentlyUpdated";
684
+ }>>;
685
+ sortByLastActivity: z.ZodOptional<z.ZodBoolean>;
686
+ sortDirection: z.ZodOptional<z.ZodEnum<{
687
+ asc: "asc";
688
+ desc: "desc";
689
+ }>>;
690
+ limit: z.ZodOptional<z.ZodNumber>;
691
+ cursor: z.ZodOptional<z.ZodString>;
692
+ ownerId: z.ZodOptional<z.ZodString>;
693
+ }, z.core.$strip>;
694
+ export type ListBranchesOptions = z.infer<typeof ListBranchesOptionsSchema>;
695
+ /** Response body for the paginated branches list endpoint. */
696
+ export interface ListBranchesResult {
697
+ branches: Branch[];
698
+ /** Opaque cursor to pass as `cursor` for the next page, or null when there is no next page. */
699
+ nextCursor: string | null;
700
+ hasNextPage: boolean;
701
+ /** Total number of branches matching the filters, across all pages. */
702
+ totalCount: number;
703
+ /** True when `totalCount` comes from the eventually-consistent Mongo index, so it may lag reality slightly. */
704
+ totalCountIsApproximate?: boolean;
705
+ }
631
706
  export type CpuKind = "performance" | "standard" | "shared";
632
707
  export type MachineAutoStop = "stop" | "off" | "suspend";
633
708
  export interface ProjectRolePermissions {
@@ -1491,6 +1566,14 @@ export declare const ProjectHostingSchema: z.ZodObject<{
1491
1566
  }>>;
1492
1567
  publishedCommit: z.ZodOptional<z.ZodString>;
1493
1568
  autoDeploy: z.ZodOptional<z.ZodBoolean>;
1569
+ unpublishTrigger: z.ZodOptional<z.ZodEnum<{
1570
+ system: "system";
1571
+ user: "user";
1572
+ }>>;
1573
+ systemUnpublishReason: z.ZodOptional<z.ZodEnum<{
1574
+ "bandwidth-cap": "bandwidth-cap";
1575
+ "web-request-cap": "web-request-cap";
1576
+ }>>;
1494
1577
  domainIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
1495
1578
  }, z.core.$strip>;
1496
1579
  export type ProjectHosting = z.infer<typeof ProjectHostingSchema>;
@@ -1549,6 +1632,36 @@ export declare const HostingSlugSchema: z.ZodObject<{
1549
1632
  createdBy: z.ZodString;
1550
1633
  }, z.core.$strip>;
1551
1634
  export type HostingSlug = z.infer<typeof HostingSlugSchema>;
1635
+ /**
1636
+ * Per-Neon-project charged-through watermarks inside a space's hosting usage
1637
+ * snapshot. Storage folds root + child branch bytes-month into one field.
1638
+ */
1639
+ export declare const HostingNeonProjectUsageSchema: z.ZodObject<{
1640
+ computeUnitSecondsChargedThrough: z.ZodNumber;
1641
+ storageBytesMonthChargedThrough: z.ZodNumber;
1642
+ transferBytesChargedThrough: z.ZodNumber;
1643
+ }, z.core.$strip>;
1644
+ export type HostingNeonProjectUsage = z.infer<typeof HostingNeonProjectUsageSchema>;
1645
+ /**
1646
+ * Reconciler idempotency watermark for a space's Netlify + Neon hosting costs,
1647
+ * stored at `hostingUsageSnapshots/{spaceId}`. Netlify is one account-level
1648
+ * scalar; Neon is a per-project map. `freeDeployCount` gates the free 5/mo
1649
+ * deploy cap (cost itself is inside Netlify's partner_account_usage total).
1650
+ */
1651
+ export declare const HostingUsageSnapshotSchema: z.ZodObject<{
1652
+ ownerId: z.ZodString;
1653
+ periodKey: z.ZodString;
1654
+ netlifyConsumptionCycleStart: z.ZodString;
1655
+ netlifyCreditsChargedThrough: z.ZodNumber;
1656
+ neonByProject: z.ZodRecord<z.ZodString, z.ZodObject<{
1657
+ computeUnitSecondsChargedThrough: z.ZodNumber;
1658
+ storageBytesMonthChargedThrough: z.ZodNumber;
1659
+ transferBytesChargedThrough: z.ZodNumber;
1660
+ }, z.core.$strip>>;
1661
+ freeDeployCount: z.ZodNumber;
1662
+ updatedAt: z.ZodNumber;
1663
+ }, z.core.$strip>;
1664
+ export type HostingUsageSnapshot = z.infer<typeof HostingUsageSnapshotSchema>;
1552
1665
  /**
1553
1666
  * Deploy lease lock with fencing token, stored at
1554
1667
  * `hosting-locks/{projectId}__{checkoutBranch}` (the key uses the resolved ref
package/src/projects.js CHANGED
@@ -38,6 +38,19 @@ export const STARTER_REPO = "BuilderIO/fusion-starter";
38
38
  export const DSI_PREVIEW_REPO = "BuilderIO/dsi-starter";
39
39
  export const DSI_PLACEHOLDER_REPO = "BuilderIO/dsi-placeholder";
40
40
  export const AGENT_NATIVE_STARTER_REPO = "BuilderIO/builder-agent-native-starter";
41
+ /**
42
+ * Whether a repo/workspace-folder refers to the Agent Native starter. Hostable
43
+ * Agent Native projects are born with their own `repoName` (the eagerly
44
+ * provisioned internal host), so the starter is only identifiable via
45
+ * `templateRepoUrl` — the read-only clone source. Matching either keeps both
46
+ * hostable and plain-clone projects working.
47
+ */
48
+ export const matchesAgentNativeStarter = (repo) => {
49
+ var _a;
50
+ if (repo.repoName === AGENT_NATIVE_STARTER_REPO)
51
+ return true;
52
+ return !!((_a = repo.templateRepoUrl) === null || _a === void 0 ? void 0 : _a.includes(AGENT_NATIVE_STARTER_REPO));
53
+ };
41
54
  export const EXAMPLE_OR_STARTER_REPOS = [
42
55
  ...EXAMPLE_REPOS,
43
56
  STARTER_REPO,
@@ -49,6 +62,91 @@ export const EXAMPLE_OR_STARTER_REPOS_URLS = EXAMPLE_OR_STARTER_REPOS.map((repo)
49
62
  export const checkIsNewBranch = (branch) => {
50
63
  return "projectId" in branch;
51
64
  };
65
+ /**
66
+ * Request options for `GET /projects/branches/list`. Owner/org scope comes from
67
+ * the authenticated request context; `ownerId` here is a superuser-only override.
68
+ */
69
+ export const ListBranchesOptionsSchema = z.object({
70
+ projectId: z.string().optional().meta({
71
+ description: "Filter to a single project. Omit to list all branches in the owner's org.",
72
+ }),
73
+ projectIds: z.array(z.string()).optional().meta({
74
+ description: "Filter to any of these projects, unioned with projectId.",
75
+ }),
76
+ kanbanColumn: z
77
+ .enum([
78
+ "in-progress",
79
+ "in-review",
80
+ "awaiting-feedback",
81
+ "merged",
82
+ "archived",
83
+ ])
84
+ .optional()
85
+ .meta({
86
+ description: "Kanban column to scope to. Derives column membership from state, PR url/status and reviewers, and overrides `states`.",
87
+ }),
88
+ states: z
89
+ .array(z.enum(["active", "deleted", "archived"]))
90
+ .optional()
91
+ .meta({
92
+ description: 'Lifecycle states to include. Defaults to ["active"]. Ignored when kanbanColumn is set.',
93
+ }),
94
+ includeHidden: z.boolean().optional().meta({
95
+ description: "Superuser-only; superusers see hidden branches by default and pass false to opt out. Ignored for non-superusers.",
96
+ }),
97
+ search: z.string().optional().meta({
98
+ description: "Case-insensitive substring match over friendlyName, name and description.",
99
+ }),
100
+ category: z
101
+ .array(z.enum(["feature", "fix", "research", "other"]))
102
+ .optional()
103
+ .meta({ description: "Filter by auto-assigned work category." }),
104
+ prStatus: z
105
+ .array(z.enum(["open", "draft", "merged", "closed", "none"]))
106
+ .optional()
107
+ .meta({
108
+ description: "Filter by derived PR status (none = no PR opened yet).",
109
+ }),
110
+ createdBy: z
111
+ .string()
112
+ .optional()
113
+ .meta({ description: "Only branches created by this user id." }),
114
+ assignedTo: z
115
+ .string()
116
+ .optional()
117
+ .meta({ description: "Only branches where this user id is a reviewer." }),
118
+ notYetReviewed: z.boolean().optional().meta({
119
+ description: "With assignedTo, exclude branches whose reviews already contain that user.",
120
+ }),
121
+ showOnlyRepoConnected: z.boolean().optional().meta({
122
+ description: "Exclude the default main branch and commit-mode branches.",
123
+ }),
124
+ sortBy: z
125
+ .enum(["recentlyUpdated", "alphabetical", "dateCreated"])
126
+ .optional()
127
+ .meta({
128
+ description: "Sort key. recentlyUpdated (default) sorts by lastUpdatedAt.",
129
+ }),
130
+ sortByLastActivity: z.boolean().optional().meta({
131
+ description: "When true, recentlyUpdated keys off lastActivityAt (falling back to lastUpdatedAt). Ignored for other sort keys.",
132
+ }),
133
+ sortDirection: z.enum(["asc", "desc"]).optional().meta({
134
+ description: "Defaults to desc for date sorts, asc for alphabetical.",
135
+ }),
136
+ limit: z
137
+ .number()
138
+ .int()
139
+ .min(1)
140
+ .max(200)
141
+ .optional()
142
+ .meta({ description: "Page size, clamped to 1–200. Defaults to 50." }),
143
+ cursor: z.string().optional().meta({
144
+ description: "Opaque pagination cursor from a prior response's nextCursor.",
145
+ }),
146
+ ownerId: z.string().optional().meta({
147
+ description: "Superuser-only: list branches for a different owner/org.",
148
+ }),
149
+ });
52
150
  /** Grace period before scheduled Neon deletions run (reconciler + schedule APIs). */
53
151
  export const NEON_DELETION_GRACE_PERIOD_MS = 30 * 24 * 60 * 60 * 1000;
54
152
  /**
@@ -328,6 +426,26 @@ export const ProjectHostingSchema = z.object({
328
426
  .boolean()
329
427
  .optional()
330
428
  .meta({ description: "auto-deploy on push/merge" }),
429
+ /**
430
+ * Who last drove the site into `publishStatus: "unpublished"`. Absent is
431
+ * treated as `"user"`. `"system"` means free-tier usage-cap enforcement took
432
+ * the site down (deleted the HTTPRoute, kept the Netlify site) and the
433
+ * reconciler **may** auto-restore it — routing-only, no rebuild — once the cap
434
+ * clears. `"user"` (or absent) means the user unpublished, so the reconciler
435
+ * must leave it down. The publish/unpublish hosting lock keeps this honest:
436
+ * whichever operation wins the lock is the one that writes `publishStatus`
437
+ * and this field, so they never disagree.
438
+ */
439
+ unpublishTrigger: z.enum(["system", "user"]).optional().meta({
440
+ description: "Who last unpublished the site. Absent === user. 'system' lets the reconciler auto-restore a usage-cap takedown; 'user' does not.",
441
+ }),
442
+ /**
443
+ * Why the system unpublished the site. Set iff `unpublishTrigger === "system"`;
444
+ * drives the projects-grid "suspended" card and takedown observability.
445
+ */
446
+ systemUnpublishReason: z
447
+ .enum(["bandwidth-cap", "web-request-cap"])
448
+ .optional(),
331
449
  domainIds: z.array(z.string()).optional().meta({
332
450
  description: "Doc keys (normalized domains) in `hosting-domains` attached to this project. The domain docs are the source of truth; this is only the back-pointer.",
333
451
  }),
@@ -411,6 +529,42 @@ export const HostingSlugSchema = z.object({
411
529
  description: "user ID of the user who first clicked Publish on the project",
412
530
  }),
413
531
  });
532
+ /**
533
+ * Per-Neon-project charged-through watermarks inside a space's hosting usage
534
+ * snapshot. Storage folds root + child branch bytes-month into one field.
535
+ */
536
+ export const HostingNeonProjectUsageSchema = z.object({
537
+ computeUnitSecondsChargedThrough: z.number(),
538
+ storageBytesMonthChargedThrough: z.number().meta({
539
+ description: "root + child branch bytes-month already billed",
540
+ }),
541
+ transferBytesChargedThrough: z.number().meta({
542
+ description: "public network transfer bytes already billed",
543
+ }),
544
+ });
545
+ /**
546
+ * Reconciler idempotency watermark for a space's Netlify + Neon hosting costs,
547
+ * stored at `hostingUsageSnapshots/{spaceId}`. Netlify is one account-level
548
+ * scalar; Neon is a per-project map. `freeDeployCount` gates the free 5/mo
549
+ * deploy cap (cost itself is inside Netlify's partner_account_usage total).
550
+ */
551
+ export const HostingUsageSnapshotSchema = z.object({
552
+ ownerId: z.string(),
553
+ periodKey: z.string().meta({
554
+ description: "Builder billing cycle; resets the Neon watermarks",
555
+ }),
556
+ netlifyConsumptionCycleStart: z.string().meta({
557
+ description: "Netlify consumption_cycle_start_date; watermark resets when this advances",
558
+ }),
559
+ netlifyCreditsChargedThrough: z.number().meta({
560
+ description: "Netlify credits already billed this Netlify cycle",
561
+ }),
562
+ neonByProject: z.record(z.string(), HostingNeonProjectUsageSchema),
563
+ freeDeployCount: z.number().meta({
564
+ description: "Deploys this Builder cycle, for the free 5/mo cap",
565
+ }),
566
+ updatedAt: z.number(),
567
+ });
414
568
  /**
415
569
  * Deploy lease lock with fencing token, stored at
416
570
  * `hosting-locks/{projectId}__{checkoutBranch}` (the key uses the resolved ref
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,22 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { AGENT_NATIVE_STARTER_REPO, matchesAgentNativeStarter, } from "./projects";
3
+ describe("matchesAgentNativeStarter", () => {
4
+ it("matches when repoName is the starter", () => {
5
+ expect(matchesAgentNativeStarter({ repoName: AGENT_NATIVE_STARTER_REPO })).toBe(true);
6
+ });
7
+ it("matches hostable projects via templateRepoUrl even when repoName is the internal host", () => {
8
+ expect(matchesAgentNativeStarter({
9
+ repoName: "builder-internal-host/some-project",
10
+ templateRepoUrl: "https://github.com/BuilderIO/builder-agent-native-starter",
11
+ })).toBe(true);
12
+ });
13
+ it("does not match unrelated projects", () => {
14
+ expect(matchesAgentNativeStarter({
15
+ repoName: "acme/dashboard",
16
+ templateRepoUrl: "https://github.com/BuilderIO/fusion-starter",
17
+ })).toBe(false);
18
+ });
19
+ it("does not match when both fields are absent", () => {
20
+ expect(matchesAgentNativeStarter({})).toBe(false);
21
+ });
22
+ });