@agent-native/core 0.80.9 → 0.80.10

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 (111) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +7 -0
  3. package/corpus/core/package.json +1 -1
  4. package/corpus/core/src/cli/design-connect.ts +28 -2
  5. package/corpus/core/src/cli/skills.ts +45 -24
  6. package/corpus/templates/design/DESIGN-STUDIO-PLAN.md +717 -0
  7. package/corpus/templates/design/actions/add-breakpoint.ts +143 -0
  8. package/corpus/templates/design/actions/add-localhost-screens.ts +5 -0
  9. package/corpus/templates/design/actions/apply-a11y-fix.ts +317 -0
  10. package/corpus/templates/design/actions/apply-component-prop-edit.ts +433 -0
  11. package/corpus/templates/design/actions/apply-design-state.ts +200 -0
  12. package/corpus/templates/design/actions/apply-design-token-edit.ts +202 -0
  13. package/corpus/templates/design/actions/apply-motion-edit.ts +450 -0
  14. package/corpus/templates/design/actions/apply-shader-fill.ts +404 -0
  15. package/corpus/templates/design/actions/apply-visual-edit.ts +191 -5
  16. package/corpus/templates/design/actions/capture-design-state.ts +224 -0
  17. package/corpus/templates/design/actions/connect-builder-app.ts +162 -0
  18. package/corpus/templates/design/actions/create-component.ts +447 -0
  19. package/corpus/templates/design/actions/create-design-branch.ts +263 -0
  20. package/corpus/templates/design/actions/create-design-state.ts +162 -0
  21. package/corpus/templates/design/actions/delete-design-state.ts +55 -0
  22. package/corpus/templates/design/actions/deploy-design-preview.ts +275 -0
  23. package/corpus/templates/design/actions/get-component-details.ts +242 -0
  24. package/corpus/templates/design/actions/get-design-branch-diff.ts +362 -0
  25. package/corpus/templates/design/actions/get-design-review.ts +314 -0
  26. package/corpus/templates/design/actions/get-design-surface-index.ts +582 -0
  27. package/corpus/templates/design/actions/get-motion-timeline.ts +99 -0
  28. package/corpus/templates/design/actions/index-components.ts +258 -0
  29. package/corpus/templates/design/actions/index-design-tokens.ts +273 -0
  30. package/corpus/templates/design/actions/list-design-extensions.ts +309 -0
  31. package/corpus/templates/design/actions/list-design-source-capabilities.ts +153 -0
  32. package/corpus/templates/design/actions/list-design-states.ts +72 -0
  33. package/corpus/templates/design/actions/migrate-inline-design-to-app.ts +298 -0
  34. package/corpus/templates/design/actions/open-component-source.ts +237 -0
  35. package/corpus/templates/design/actions/preview-component-prop-edit.ts +254 -0
  36. package/corpus/templates/design/actions/preview-design-token-edit.ts +113 -0
  37. package/corpus/templates/design/actions/preview-shader-fill.ts +189 -0
  38. package/corpus/templates/design/actions/remove-breakpoint.ts +103 -0
  39. package/corpus/templates/design/actions/remove-motion-timeline.ts +196 -0
  40. package/corpus/templates/design/actions/run-design-audit.ts +421 -0
  41. package/corpus/templates/design/actions/run-design-extension-action.ts +284 -0
  42. package/corpus/templates/design/actions/set-active-breakpoint.ts +39 -0
  43. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +1414 -71
  44. package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +632 -43
  45. package/corpus/templates/design/app/components/design/DrawOverlay.tsx +1 -1
  46. package/corpus/templates/design/app/components/design/EditPanel.tsx +1391 -52
  47. package/corpus/templates/design/app/components/design/LayersPanel.tsx +4 -3
  48. package/corpus/templates/design/app/components/design/LocalSourceEditBanner.tsx +157 -0
  49. package/corpus/templates/design/app/components/design/MotionDock.tsx +1062 -0
  50. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +1052 -98
  51. package/corpus/templates/design/app/components/design/ReviewPanel.tsx +759 -0
  52. package/corpus/templates/design/app/components/design/StatesPanel.tsx +579 -0
  53. package/corpus/templates/design/app/components/design/TokensPanel.tsx +573 -0
  54. package/corpus/templates/design/app/components/design/canvas-primitive-style.ts +241 -0
  55. package/corpus/templates/design/app/components/design/index.ts +16 -0
  56. package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +15 -6
  57. package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +66 -3
  58. package/corpus/templates/design/app/components/design/inspector/InspectorAiActions.tsx +145 -0
  59. package/corpus/templates/design/app/components/design/inspector/SHADER_INTEGRATION.md +42 -12
  60. package/corpus/templates/design/app/components/visual-editor/DrawOverlay.tsx +1 -1
  61. package/corpus/templates/design/app/hooks/useAgentEditRequest.ts +131 -0
  62. package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +3 -1
  63. package/corpus/templates/design/app/i18n/zh-TW.ts +31 -0
  64. package/corpus/templates/design/app/i18n-data.ts +269 -0
  65. package/corpus/templates/design/app/pages/DesignEditor.tsx +2478 -386
  66. package/corpus/templates/design/changelog/2026-06-29-added-a-review-panel-in-the-design-editor-s-inspector-with-a.md +6 -0
  67. package/corpus/templates/design/changelog/2026-06-29-copying-and-pasting-layers-no-longer-shows-success-notificat.md +6 -0
  68. package/corpus/templates/design/changelog/2026-06-29-device-presets-in-all-screens-view-resize-the-selected-previ.md +6 -0
  69. package/corpus/templates/design/changelog/2026-06-29-layer-and-canvas-drags-keep-the-layer-list-stable-while-chan.md +6 -0
  70. package/corpus/templates/design/changelog/2026-06-29-layer-moves-can-be-undone-and-redone-without-flashing-the-ca.md +6 -0
  71. package/corpus/templates/design/changelog/2026-06-29-screen-previews-use-a-single-blue-hover-border-in-all-screen.md +6 -0
  72. package/corpus/templates/design/changelog/2026-06-29-selected-containers-show-draggable-padding-and-gap-guides-on.md +6 -0
  73. package/corpus/templates/design/changelog/2026-06-29-the-design-editor-adds-a-studio-layer-with-tokens-respon.md +6 -0
  74. package/corpus/templates/design/changelog/2026-06-30-accessibility-findings-now-offer-a-one-click-fix.md +6 -0
  75. package/corpus/templates/design/changelog/2026-06-30-component-instances-now-have-editable-props-in-the-inspe.md +6 -0
  76. package/corpus/templates/design/changelog/2026-06-30-inspect-code-now-shows-the-elements-opening-tag-at-a-gla.md +6 -0
  77. package/corpus/templates/design/changelog/2026-06-30-shader-fill-presets-can-now-be-applied-to-an-element.md +6 -0
  78. package/corpus/templates/design/changelog/2026-06-30-the-motion-timeline-can-now-add-a-track-to-any-element-a.md +6 -0
  79. package/corpus/templates/design/changelog/2026-06-30-visual-editor-selection-layer-paint-and-drawing-controls-are-more-reliable.md +6 -0
  80. package/corpus/templates/design/e2e/helpers.ts +10 -4
  81. package/corpus/templates/design/server/db/schema.ts +123 -0
  82. package/corpus/templates/design/server/plugins/db.ts +90 -0
  83. package/corpus/templates/design/shared/builder-app.ts +297 -0
  84. package/corpus/templates/design/shared/capability-resolver.ts +123 -0
  85. package/corpus/templates/design/shared/capture-sanitize.ts +70 -0
  86. package/corpus/templates/design/shared/code-layer.ts +1016 -71
  87. package/corpus/templates/design/shared/component-model.ts +239 -0
  88. package/corpus/templates/design/shared/design-review.ts +275 -0
  89. package/corpus/templates/design/shared/design-source-capabilities.ts +286 -0
  90. package/corpus/templates/design/shared/design-state.ts +112 -0
  91. package/corpus/templates/design/shared/design-surface-index.ts +258 -0
  92. package/corpus/templates/design/shared/motion-compiler.ts +278 -0
  93. package/corpus/templates/design/shared/motion-timeline.ts +193 -0
  94. package/corpus/templates/design/shared/responsive-classes.ts +452 -0
  95. package/corpus/templates/design/shared/shader-fill.ts +323 -0
  96. package/corpus/templates/design/shared/source-mode.ts +245 -0
  97. package/corpus/templates/plan/app/components/plan/CanvasArea.tsx +6 -2
  98. package/corpus/templates/plan/changelog/2026-06-29-plan-canvases-open-without-an-initial-pan-and-zoom-flicker.md +6 -0
  99. package/dist/cli/design-connect.d.ts.map +1 -1
  100. package/dist/cli/design-connect.js +28 -2
  101. package/dist/cli/design-connect.js.map +1 -1
  102. package/dist/cli/skills.d.ts.map +1 -1
  103. package/dist/cli/skills.js +37 -21
  104. package/dist/cli/skills.js.map +1 -1
  105. package/dist/collab/routes.d.ts +1 -1
  106. package/dist/file-upload/actions/upload-image.d.ts +2 -2
  107. package/dist/notifications/routes.d.ts +2 -2
  108. package/dist/observability/routes.d.ts +7 -7
  109. package/dist/resources/handlers.d.ts +2 -2
  110. package/dist/server/transcribe-voice.d.ts +1 -1
  111. package/package.json +1 -1
@@ -0,0 +1,263 @@
1
+ /**
2
+ * create-design-branch — start/link a Builder-hosted branch for a fusion-backed
3
+ * design (§6.6 of DESIGN-STUDIO-PLAN.md).
4
+ *
5
+ * Behaviour:
6
+ * - **Capability gate (server-side):** re-checks `branch` capability from the
7
+ * design's source. When `unavailable` (inline/localhost), returns a CTA
8
+ * explaining what the user needs to do — never fakes a branch call.
9
+ * - **Builder gate:** if `resolveIsBuilderBranchingEnabled()` returns false,
10
+ * returns a `connectRequired` CTA pointing at the Builder connect flow.
11
+ * - **Branch creation:** calls `runBuilderAgent()` with a scoped prompt that
12
+ * asks the agent to create/link a branch for the design. The Builder cloud
13
+ * agent runs in a sandbox and returns `{ branchName, projectId, url, status }`.
14
+ * - **Persistence:** stores the returned branch metadata into the design's `data`
15
+ * JSON blob under a `branches` key, keyed by `branchName`. This is additive —
16
+ * existing data is preserved.
17
+ * - **Design version snapshot:** creates a `design_versions` row capturing the
18
+ * pre-branch HTML state so the branch can be diffed/rolled back via
19
+ * `get-design-branch-diff`.
20
+ *
21
+ * Per DESIGN-STUDIO-PLAN.md §3.3, the original inline design is never dropped;
22
+ * migration is preview-only until the user approves cutover.
23
+ */
24
+
25
+ import { defineAction } from "@agent-native/core";
26
+ import {
27
+ runBuilderAgent,
28
+ resolveBuilderBranchProjectId,
29
+ resolveIsBuilderBranchingEnabled,
30
+ } from "@agent-native/core/server";
31
+ import { getRequestUserEmail } from "@agent-native/core/server/request-context";
32
+ import { assertAccess, resolveAccess } from "@agent-native/core/sharing";
33
+ import { eq } from "drizzle-orm";
34
+ import { nanoid } from "nanoid";
35
+ import { z } from "zod";
36
+
37
+ import { getDb, schema } from "../server/db/index.js";
38
+ import "../server/db/index.js"; // ensure registerShareableResource runs
39
+ import {
40
+ resolveSourceCapabilities,
41
+ resolveFusionCapabilities,
42
+ } from "../shared/capability-resolver.js";
43
+ import { hasCapability } from "../shared/design-source-capabilities.js";
44
+ import { normalizeDesignSourceType } from "../shared/source-mode.js";
45
+
46
+ // ─── Helpers ──────────────────────────────────────────────────────────────────
47
+
48
+ /** Safely parse the design's data JSON blob. */
49
+ function parseDesignData(raw: unknown): Record<string, unknown> {
50
+ if (typeof raw !== "string") return {};
51
+ try {
52
+ const parsed: unknown = JSON.parse(raw);
53
+ if (
54
+ parsed !== null &&
55
+ typeof parsed === "object" &&
56
+ !Array.isArray(parsed)
57
+ ) {
58
+ return parsed as Record<string, unknown>;
59
+ }
60
+ } catch {
61
+ // Stale or invalid JSON — return empty.
62
+ }
63
+ return {};
64
+ }
65
+
66
+ /**
67
+ * Build a concise branch-creation prompt for the Builder cloud agent.
68
+ * The prompt identifies the design by title and requests a branch.
69
+ */
70
+ function buildBranchPrompt(
71
+ designTitle: string,
72
+ purpose: string | undefined,
73
+ designId: string,
74
+ ): string {
75
+ const purposeLine = purpose?.trim()
76
+ ? `Purpose / context: ${purpose.trim()}`
77
+ : "Purpose: explore / iterate on this design as a real app branch.";
78
+ return [
79
+ `Create a new branch for the design "${designTitle}" (id: ${designId}).`,
80
+ purposeLine,
81
+ "The branch should reflect the current design state and be ready for",
82
+ "iterative edits via the Builder Visual Editor.",
83
+ ].join("\n");
84
+ }
85
+
86
+ // ─── Action ───────────────────────────────────────────────────────────────────
87
+
88
+ export default defineAction({
89
+ description:
90
+ "Start or link a Builder-hosted branch for a fusion-backed design. " +
91
+ "Requires the design's source to advertise the 'branch' capability (fusion tier) " +
92
+ "AND Builder.io to be connected. " +
93
+ "For inline/localhost designs without Builder, returns a ctaRequired response " +
94
+ "with a 'Make it real' upgrade message — never fakes a branch call. " +
95
+ "On success, persists branch metadata (branchName, url, status, projectId) " +
96
+ "into the design's data blob and creates a design_versions snapshot so the " +
97
+ "pre-branch state can be diffed/rolled back via get-design-branch-diff.",
98
+ schema: z.object({
99
+ designId: z.string().describe("Design project ID to create a branch for"),
100
+ branchName: z
101
+ .string()
102
+ .optional()
103
+ .describe(
104
+ "Desired branch name. Builder may normalise or suffix it to ensure " +
105
+ "uniqueness. Omit to let Builder auto-generate a name.",
106
+ ),
107
+ purpose: z
108
+ .string()
109
+ .optional()
110
+ .describe(
111
+ "Optional short description of what this branch is for (fed to the " +
112
+ "Builder cloud agent as context).",
113
+ ),
114
+ }),
115
+ run: async ({ designId, branchName, purpose }) => {
116
+ const db = getDb();
117
+
118
+ // ── Access check (editor level required for branch creation) ────────────
119
+ await assertAccess("design", designId, "editor");
120
+ const access = await resolveAccess("design", designId);
121
+ if (!access) throw new Error("Design not found");
122
+
123
+ const resource = access.resource as {
124
+ title?: string;
125
+ data?: unknown;
126
+ };
127
+
128
+ // ── Source type + capability check ──────────────────────────────────────
129
+ const designData = parseDesignData(resource.data);
130
+ const sourceType =
131
+ normalizeDesignSourceType(designData["sourceType"]) ?? "inline";
132
+
133
+ // For fusion sources, resolve the Builder connection status first so that
134
+ // resolveFusionCapabilities returns the CONNECTED map (with branch/deploy
135
+ // available) when Builder is actually wired up. For inline/localhost the
136
+ // generic resolver is sufficient — those sources never have branch.
137
+ const builderEnabled =
138
+ sourceType === "fusion"
139
+ ? await resolveIsBuilderBranchingEnabled()
140
+ : false;
141
+ const caps =
142
+ sourceType === "fusion"
143
+ ? resolveFusionCapabilities(builderEnabled)
144
+ : resolveSourceCapabilities(sourceType);
145
+
146
+ if (!hasCapability(caps, "branch")) {
147
+ // Inline or localhost designs don't support branching. Return a CTA.
148
+ // For a disconnected fusion source the connect-builder CTA applies.
149
+ const isFusion = sourceType === "fusion";
150
+ return {
151
+ designId,
152
+ sourceType,
153
+ ctaRequired: true,
154
+ ctaKind: isFusion
155
+ ? ("connect-builder" as const)
156
+ : ("make-it-real" as const),
157
+ ctaMessage: isFusion
158
+ ? "Builder is not yet connected. Connect Builder.io to create branches for this design."
159
+ : "Branching requires a Builder-hosted app. Use 'Make it real' to upgrade this inline design to a real-app source, then create branches.",
160
+ branch: null,
161
+ versionId: null,
162
+ };
163
+ }
164
+
165
+ // At this point sourceType === "fusion" and builderEnabled === true,
166
+ // so no separate Builder gate is needed — the capability check above
167
+ // already required a connected Builder to set branch=available.
168
+
169
+ // ── Snapshot the current design state before branching ──────────────────
170
+ // Fetch all files so the snapshot captures the full pre-branch state.
171
+ const files = await db
172
+ .select({
173
+ id: schema.designFiles.id,
174
+ filename: schema.designFiles.filename,
175
+ content: schema.designFiles.content,
176
+ })
177
+ .from(schema.designFiles)
178
+ .where(eq(schema.designFiles.designId, designId));
179
+
180
+ const now = new Date().toISOString();
181
+ const versionId = `dv_${nanoid(12)}`;
182
+
183
+ const snapshot = JSON.stringify({
184
+ designId,
185
+ snapshotKind: "pre-branch",
186
+ branchPurpose: purpose ?? null,
187
+ files: files.map((f) => ({
188
+ id: f.id,
189
+ filename: f.filename,
190
+ content: f.content,
191
+ bytes: f.content?.length ?? 0,
192
+ })),
193
+ capturedAt: now,
194
+ });
195
+
196
+ await db.insert(schema.designVersions).values({
197
+ id: versionId,
198
+ designId,
199
+ label: `Pre-branch snapshot — ${now}`,
200
+ snapshot,
201
+ createdAt: now,
202
+ });
203
+
204
+ // ── Run the Builder cloud agent to create the branch ────────────────────
205
+ const projectId = await resolveBuilderBranchProjectId();
206
+ const userEmail = getRequestUserEmail();
207
+ if (!userEmail) throw new Error("No authenticated user");
208
+
209
+ const designTitle =
210
+ typeof resource.title === "string" && resource.title.trim()
211
+ ? resource.title.trim()
212
+ : "Design";
213
+
214
+ const builderResult = await runBuilderAgent({
215
+ prompt: buildBranchPrompt(designTitle, purpose, designId),
216
+ projectId,
217
+ branchName: branchName?.trim() || undefined,
218
+ userEmail,
219
+ });
220
+
221
+ // ── Persist branch metadata into the design's data blob ─────────────────
222
+ const existingBranches = Array.isArray(designData["branches"])
223
+ ? (designData["branches"] as unknown[])
224
+ : [];
225
+
226
+ const branchEntry = {
227
+ branchName: builderResult.branchName,
228
+ projectId: builderResult.projectId,
229
+ url: builderResult.url,
230
+ status: builderResult.status,
231
+ purpose: purpose ?? null,
232
+ preSnapshotVersionId: versionId,
233
+ createdAt: now,
234
+ };
235
+
236
+ const updatedData = JSON.stringify({
237
+ ...designData,
238
+ branches: [...existingBranches, branchEntry],
239
+ // Upgrade the source type to fusion once a Builder branch is provisioned,
240
+ // so the capability matrix reflects the new real-app tier.
241
+ sourceType: "fusion",
242
+ });
243
+
244
+ await db
245
+ .update(schema.designs)
246
+ .set({ data: updatedData, updatedAt: now })
247
+ .where(eq(schema.designs.id, designId));
248
+
249
+ return {
250
+ designId,
251
+ sourceType: "fusion" as const,
252
+ ctaRequired: false,
253
+ ctaKind: null,
254
+ ctaMessage: null,
255
+ branch: branchEntry,
256
+ versionId,
257
+ note:
258
+ "The design source has been upgraded to 'fusion'. The pre-branch state " +
259
+ "is captured in the design version snapshot so you can diff/rollback via " +
260
+ "get-design-branch-diff.",
261
+ };
262
+ },
263
+ });
@@ -0,0 +1,162 @@
1
+ import { defineAction } from "@agent-native/core";
2
+ import {
3
+ getRequestOrgId,
4
+ getRequestUserEmail,
5
+ } from "@agent-native/core/server/request-context";
6
+ import { assertAccess } from "@agent-native/core/sharing";
7
+ import { nanoid } from "nanoid";
8
+ import { z } from "zod";
9
+
10
+ import { getDb, schema } from "../server/db/index.js";
11
+ import "../server/db/index.js"; // ensure registerShareableResource runs
12
+ import {
13
+ CAPTURE_DATA_MAX_BYTES,
14
+ sanitizeCaptureData,
15
+ } from "../shared/capture-sanitize.js";
16
+
17
+ export default defineAction({
18
+ description:
19
+ "Create a new named design state, data fixture, or live-capture row for a design. " +
20
+ "Use kind='state' for alternate DOM/Alpine snapshots (Loading, Empty, Error), " +
21
+ "kind='fixture' for static data payloads, " +
22
+ "and kind='capture' (see capture-design-state) to record running-app state via the bridge. " +
23
+ "The new row is returned with its generated id.",
24
+ schema: z.object({
25
+ designId: z.string().describe("Design project ID"),
26
+ name: z
27
+ .string()
28
+ .min(1)
29
+ .describe(
30
+ "Human-readable name shown in the States panel (e.g. 'Loading', 'Empty cart', 'Logged out').",
31
+ ),
32
+ kind: z
33
+ .enum(["state", "fixture", "capture"])
34
+ .default("state")
35
+ .describe("Kind of entry to create."),
36
+ breakpoint: z
37
+ .enum(["auto", "desktop", "tablet", "mobile"])
38
+ .default("auto")
39
+ .describe(
40
+ "Breakpoint context for this state. 'auto' applies at all breakpoints.",
41
+ ),
42
+ sourceRef: z
43
+ .string()
44
+ .optional()
45
+ .describe(
46
+ "Opaque source reference: fileId for inline designs, routeId for localhost/fusion. Omit to scope to the entire design.",
47
+ ),
48
+ route: z
49
+ .string()
50
+ .optional()
51
+ .describe(
52
+ "App route path (e.g. '/dashboard') for fixture/capture kinds.",
53
+ ),
54
+ fixtureData: z
55
+ .preprocess(
56
+ (v) => (typeof v === "string" ? JSON.parse(v) : v),
57
+ z.record(z.string(), z.unknown()),
58
+ )
59
+ .optional()
60
+ .describe(
61
+ "Static data payload (props, query params, mock API responses) for fixture kind.",
62
+ ),
63
+ captureData: z
64
+ .preprocess(
65
+ (v) => (typeof v === "string" ? JSON.parse(v) : v),
66
+ z.record(z.string(), z.unknown()),
67
+ )
68
+ .optional()
69
+ .describe(
70
+ "Serialised DOM/Alpine snapshot or captured component tree for state/capture kinds.",
71
+ ),
72
+ previewRef: z
73
+ .string()
74
+ .optional()
75
+ .describe(
76
+ "Reference to a design_version snapshot or preview image URL produced when this state was captured.",
77
+ ),
78
+ }),
79
+ run: async ({
80
+ designId,
81
+ name,
82
+ kind,
83
+ breakpoint,
84
+ sourceRef,
85
+ route,
86
+ fixtureData,
87
+ captureData,
88
+ previewRef,
89
+ }) => {
90
+ await assertAccess("design", designId, "editor");
91
+
92
+ const db = getDb();
93
+ const id = nanoid();
94
+ const now = new Date().toISOString();
95
+ const ownerEmail = getRequestUserEmail();
96
+ if (!ownerEmail) throw new Error("no authenticated user");
97
+ const orgId = getRequestOrgId();
98
+
99
+ // Sanitise captured DOM/markup (stored-XSS guard) and enforce a size cap so
100
+ // a single row can't bloat the DB or the shareable content it feeds.
101
+ const sanitizedCaptureData = captureData
102
+ ? sanitizeCaptureData(captureData)
103
+ : null;
104
+ const captureDataJson = sanitizedCaptureData
105
+ ? JSON.stringify(sanitizedCaptureData)
106
+ : null;
107
+ if (
108
+ captureDataJson !== null &&
109
+ Buffer.byteLength(captureDataJson, "utf8") > CAPTURE_DATA_MAX_BYTES
110
+ ) {
111
+ throw new Error(
112
+ `captureData exceeds the ${Math.round(
113
+ CAPTURE_DATA_MAX_BYTES / 1024,
114
+ )}KB limit. Use a smaller DOM snapshot or trim the payload.`,
115
+ );
116
+ }
117
+
118
+ const sanitizedFixtureData = fixtureData
119
+ ? sanitizeCaptureData(fixtureData)
120
+ : null;
121
+ const fixtureDataJson = sanitizedFixtureData
122
+ ? JSON.stringify(sanitizedFixtureData)
123
+ : null;
124
+ if (
125
+ fixtureDataJson !== null &&
126
+ Buffer.byteLength(fixtureDataJson, "utf8") > CAPTURE_DATA_MAX_BYTES
127
+ ) {
128
+ throw new Error(
129
+ `fixtureData exceeds the ${Math.round(
130
+ CAPTURE_DATA_MAX_BYTES / 1024,
131
+ )}KB limit. Trim the payload before creating this fixture.`,
132
+ );
133
+ }
134
+
135
+ await db.insert(schema.designState).values({
136
+ id,
137
+ designId,
138
+ sourceRef: sourceRef ?? null,
139
+ name,
140
+ kind,
141
+ breakpoint,
142
+ route: route ?? null,
143
+ fixtureData: fixtureDataJson,
144
+ captureData: captureDataJson,
145
+ previewRef: previewRef ?? null,
146
+ createdAt: now,
147
+ updatedAt: now,
148
+ ownerEmail,
149
+ orgId,
150
+ });
151
+
152
+ return {
153
+ id,
154
+ designId,
155
+ name,
156
+ kind,
157
+ breakpoint,
158
+ sourceRef: sourceRef ?? null,
159
+ createdAt: now,
160
+ };
161
+ },
162
+ });
@@ -0,0 +1,55 @@
1
+ import { defineAction } from "@agent-native/core";
2
+ import { assertAccess } from "@agent-native/core/sharing";
3
+ import { and, eq } from "drizzle-orm";
4
+ import { z } from "zod";
5
+
6
+ import { getDb, schema } from "../server/db/index.js";
7
+ import "../server/db/index.js"; // ensure registerShareableResource runs
8
+
9
+ export default defineAction({
10
+ description:
11
+ "Delete a design state, fixture, or capture row. " +
12
+ "This is irreversible; the design itself is unaffected.",
13
+ schema: z.object({
14
+ id: z.string().describe("design_state row id to delete"),
15
+ designId: z
16
+ .string()
17
+ .describe(
18
+ "Design project ID (required for access check; must match the state's design_id).",
19
+ ),
20
+ }),
21
+ run: async ({ id, designId }) => {
22
+ await assertAccess("design", designId, "editor");
23
+
24
+ const db = getDb();
25
+
26
+ // Verify the row belongs to this design before deleting.
27
+ const [existing] = await db
28
+ .select({ id: schema.designState.id, name: schema.designState.name })
29
+ .from(schema.designState)
30
+ .where(
31
+ and(
32
+ eq(schema.designState.id, id),
33
+ eq(schema.designState.designId, designId),
34
+ ),
35
+ )
36
+ .limit(1);
37
+
38
+ if (!existing) {
39
+ throw new Error(
40
+ `design_state row '${id}' not found for design '${designId}'.`,
41
+ );
42
+ }
43
+
44
+ await db
45
+ .delete(schema.designState)
46
+ .where(
47
+ and(
48
+ eq(schema.designState.id, id),
49
+ eq(schema.designState.designId, designId),
50
+ ),
51
+ );
52
+
53
+ return { deleted: true, id, designId, name: existing.name };
54
+ },
55
+ });