@agent-native/core 0.80.10 → 0.81.0

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 (267) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +53 -0
  3. package/corpus/core/package.json +1 -1
  4. package/corpus/core/src/agent/production-agent.ts +108 -15
  5. package/corpus/core/src/cli/design-connect.ts +419 -19
  6. package/corpus/core/src/client/AssistantChat.tsx +123 -26
  7. package/corpus/core/src/client/ConnectBuilderCard.tsx +1 -0
  8. package/corpus/core/src/client/chat/index.ts +1 -0
  9. package/corpus/core/src/client/chat/run-recovery.tsx +9 -7
  10. package/corpus/core/src/client/chat-view-transition.ts +33 -2
  11. package/corpus/core/src/client/composer/PromptComposer.tsx +21 -2
  12. package/corpus/core/src/client/index.ts +1 -0
  13. package/corpus/core/src/client/org/OrgSwitcher.tsx +26 -4
  14. package/corpus/core/src/client/sharing/ShareButton.tsx +3 -3
  15. package/corpus/core/src/client/sse-event-processor.ts +9 -0
  16. package/corpus/core/src/client/use-agent-chat-home-handoff.ts +31 -3
  17. package/corpus/core/src/db/client.ts +29 -2
  18. package/corpus/core/src/provider-api/index.ts +29 -0
  19. package/corpus/core/src/server/core-routes-plugin.ts +58 -20
  20. package/corpus/core/src/styles/agent-native.css +54 -0
  21. package/corpus/core/src/usage/store.ts +2 -2
  22. package/corpus/templates/analytics/app/components/layout/Layout.tsx +118 -10
  23. package/corpus/templates/analytics/app/components/layout/layout-route-policy.ts +7 -0
  24. package/corpus/templates/analytics/app/global.css +8 -0
  25. package/corpus/templates/analytics/app/hooks/use-navigation-state.ts +7 -1
  26. package/corpus/templates/analytics/app/lib/chat-handoff.ts +34 -0
  27. package/corpus/templates/analytics/app/pages/Ask.tsx +9 -18
  28. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +2 -2
  29. package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +109 -21
  30. package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +4 -6
  31. package/corpus/templates/analytics/changelog/2026-06-30-ask-opens-a-fresh-chat-unless-you-were-just-chatting-and-das.md +6 -0
  32. package/corpus/templates/analytics/changelog/2026-06-30-dashboard-email-reports-are-more-reliable-and-use-cleaner.md +6 -0
  33. package/corpus/templates/analytics/changelog/2026-06-30-dashboard-toolbar-controls-use-a-quieter-share-button-and-ve.md +6 -0
  34. package/corpus/templates/analytics/changelog/2026-06-30-session-replay-rows-show-whole-minute-durations-in-the-playl.md +6 -0
  35. package/corpus/templates/analytics/changelog/2026-06-30-session-replays-now-render-captured-styling-and-keep-the-eve.md +6 -0
  36. package/corpus/templates/analytics/server/jobs/dashboard-report.ts +16 -3
  37. package/corpus/templates/analytics/server/lib/dashboard-report.ts +31 -14
  38. package/corpus/templates/assets/.agents/skills/image-generation/SKILL.md +9 -2
  39. package/corpus/templates/assets/AGENTS.md +4 -0
  40. package/corpus/templates/assets/actions/_tool-activity.ts +46 -0
  41. package/corpus/templates/assets/actions/generate-image-batch.ts +26 -5
  42. package/corpus/templates/assets/actions/generate-image.ts +118 -67
  43. package/corpus/templates/assets/actions/list-draft-assets.ts +50 -0
  44. package/corpus/templates/assets/actions/open-asset-picker.ts +9 -0
  45. package/corpus/templates/assets/actions/rerun-generation-run.ts +52 -31
  46. package/corpus/templates/assets/app/components/create/RecentDraftsSection.tsx +100 -0
  47. package/corpus/templates/assets/app/global.css +15 -0
  48. package/corpus/templates/assets/app/hooks/use-navigation-state.ts +4 -0
  49. package/corpus/templates/assets/app/i18n/zh-TW.ts +7 -0
  50. package/corpus/templates/assets/app/i18n-data.ts +61 -0
  51. package/corpus/templates/assets/app/routes/_index.tsx +4 -0
  52. package/corpus/templates/assets/app/routes/library.tsx +233 -52
  53. package/corpus/templates/assets/changelog/2026-06-29-image-generation-can-now-render-requested-text-and-respect-b.md +6 -0
  54. package/corpus/templates/assets/changelog/2026-06-30-image-generation-no-longer-looks-stuck-while-the-provider-is-still-working.md +6 -0
  55. package/corpus/templates/assets/changelog/2026-06-30-tagged-presets-now-control-the-image-aspect-ratio.md +6 -0
  56. package/corpus/templates/assets/changelog/2026-06-30-the-assets-picker-now-supports-a-vertical-embedded-layout-fo.md +6 -0
  57. package/corpus/templates/assets/server/lib/generation.ts +135 -11
  58. package/corpus/templates/assets/shared/api.ts +4 -0
  59. package/corpus/templates/clips/actions/finalize-recording.ts +1 -12
  60. package/corpus/templates/clips/actions/list-ai-requests.ts +60 -0
  61. package/corpus/templates/clips/app/hooks/use-auto-title.ts +24 -25
  62. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +10 -5
  63. package/corpus/templates/clips/app/routes/share.$shareId.tsx +6 -8
  64. package/corpus/templates/clips/changelog/2026-06-30-extension-save-recovery-avoids-false-upload-failures.md +6 -0
  65. package/corpus/templates/clips/changelog/2026-06-30-longer-chrome-extension-recordings-now-keep-saving-reliably-.md +6 -0
  66. package/corpus/templates/clips/chrome-extension/public/manifest.json +1 -1
  67. package/corpus/templates/clips/chrome-extension/src/background.ts +19 -10
  68. package/corpus/templates/clips/chrome-extension/src/content-script.ts +38 -12
  69. package/corpus/templates/clips/chrome-extension/src/finalize-recovery.ts +224 -0
  70. package/corpus/templates/clips/chrome-extension/src/offscreen.ts +119 -16
  71. package/corpus/templates/clips/chrome-extension/src/overlay.ts +16 -0
  72. package/corpus/templates/clips/server/lib/recording-upload-state.ts +48 -0
  73. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/abort.post.ts +9 -1
  74. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +63 -13
  75. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/status.get.ts +74 -0
  76. package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +32 -0
  77. package/corpus/templates/design/AGENTS.md +27 -0
  78. package/corpus/templates/design/CODE_NATIVE_DESIGN_STUDIO_IMPLEMENTATION_PLAN.md +1 -1
  79. package/corpus/templates/design/DESIGN-STUDIO-PLAN.md +5 -5
  80. package/corpus/templates/design/actions/apply-component-prop-edit.ts +76 -25
  81. package/corpus/templates/design/actions/apply-design-state.ts +17 -4
  82. package/corpus/templates/design/actions/apply-motion-edit.ts +76 -115
  83. package/corpus/templates/design/actions/apply-shader-fill.ts +148 -56
  84. package/corpus/templates/design/actions/capture-design-state.ts +27 -4
  85. package/corpus/templates/design/actions/connect-localhost.ts +59 -22
  86. package/corpus/templates/design/actions/delete-design.ts +20 -0
  87. package/corpus/templates/design/actions/export-zip.ts +23 -8
  88. package/corpus/templates/design/actions/generate-design.ts +53 -1
  89. package/corpus/templates/design/actions/get-component-details.ts +21 -26
  90. package/corpus/templates/design/actions/get-motion-timeline.ts +135 -10
  91. package/corpus/templates/design/actions/grant-localhost-write-consent.ts +116 -0
  92. package/corpus/templates/design/actions/import-design-tokens.ts +521 -0
  93. package/corpus/templates/design/actions/index-design-tokens.ts +18 -5
  94. package/corpus/templates/design/actions/insert-design-native-asset.ts +268 -0
  95. package/corpus/templates/design/actions/insert-figma-library-asset.ts +233 -0
  96. package/corpus/templates/design/actions/list-design-extensions.ts +32 -10
  97. package/corpus/templates/design/actions/list-design-native-assets.ts +105 -0
  98. package/corpus/templates/design/actions/list-design-states.ts +19 -1
  99. package/corpus/templates/design/actions/list-figma-library-assets.ts +292 -0
  100. package/corpus/templates/design/actions/migrate-board-objects-to-file.ts +238 -0
  101. package/corpus/templates/design/actions/navigate.ts +18 -3
  102. package/corpus/templates/design/actions/open-component-source.ts +7 -2
  103. package/corpus/templates/design/actions/preview-component-prop-edit.ts +32 -26
  104. package/corpus/templates/design/actions/preview-shader-fill.ts +9 -11
  105. package/corpus/templates/design/actions/provider-api-catalog.ts +2 -2
  106. package/corpus/templates/design/actions/provider-api-docs.ts +1 -1
  107. package/corpus/templates/design/actions/provider-api-request.ts +3 -3
  108. package/corpus/templates/design/actions/revoke-localhost-write-consent.ts +48 -0
  109. package/corpus/templates/design/actions/run-design-audit.ts +74 -10
  110. package/corpus/templates/design/actions/run-design-extension-action.ts +1 -1
  111. package/corpus/templates/design/actions/view-screen.ts +1 -1
  112. package/corpus/templates/design/actions/write-local-file.ts +213 -0
  113. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +244 -3197
  114. package/corpus/templates/design/app/components/design/DesignEditorSkeleton.tsx +30 -20
  115. package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +795 -289
  116. package/corpus/templates/design/app/components/design/EditPanel.tsx +635 -234
  117. package/corpus/templates/design/app/components/design/LayersPanel.tsx +124 -32
  118. package/corpus/templates/design/app/components/design/LocalhostWriteConsentDialog.tsx +161 -0
  119. package/corpus/templates/design/app/components/design/MotionDock.tsx +244 -266
  120. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +491 -56
  121. package/corpus/templates/design/app/components/design/ReviewPanel.tsx +55 -67
  122. package/corpus/templates/design/app/components/design/StatesPanel.tsx +38 -63
  123. package/corpus/templates/design/app/components/design/TokensPanel.tsx +264 -8
  124. package/corpus/templates/design/app/components/design/bridge/codegen.ts +125 -0
  125. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +4037 -0
  126. package/corpus/templates/design/app/components/design/bridge/embedded-wheel.bridge.ts +62 -0
  127. package/corpus/templates/design/app/components/design/bridge/hit-test.bridge.ts +272 -0
  128. package/corpus/templates/design/app/components/design/bridge/nav.bridge.ts +116 -0
  129. package/corpus/templates/design/app/components/design/bridge/sample.bridge.ts +24 -0
  130. package/corpus/templates/design/app/components/design/bridge/shader-fill-preview.bridge.ts +78 -0
  131. package/corpus/templates/design/app/components/design/bridge/tsconfig.json +17 -0
  132. package/corpus/templates/design/app/components/design/bridge/tweak.bridge.ts +28 -0
  133. package/corpus/templates/design/app/components/design/bridge/zoom.bridge.ts +43 -0
  134. package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +45 -16
  135. package/corpus/templates/design/app/components/design/inspector/ImageFillControls.tsx +88 -9
  136. package/corpus/templates/design/app/components/editor/PromptDialog.tsx +94 -9
  137. package/corpus/templates/design/app/components/layout/Layout.tsx +19 -3
  138. package/corpus/templates/design/app/components/layout/Sidebar.tsx +1 -1
  139. package/corpus/templates/design/app/global.css +7 -0
  140. package/corpus/templates/design/app/hooks/use-navigation-state.ts +23 -0
  141. package/corpus/templates/design/app/i18n/zh-TW.ts +22 -0
  142. package/corpus/templates/design/app/i18n-data.ts +369 -0
  143. package/corpus/templates/design/app/lib/pending-generation.ts +1 -0
  144. package/corpus/templates/design/app/pages/DesignEditor.tsx +3024 -616
  145. package/corpus/templates/design/app/pages/Index.tsx +5 -1
  146. package/corpus/templates/design/changelog/2026-06-30-applying-the-one-click-fix-for-a-small-tap-target-accessibil.md +6 -0
  147. package/corpus/templates/design/changelog/2026-06-30-canvas-creation-screen-dragging-undo-redo-and-image-fill-edi.md +6 -0
  148. package/corpus/templates/design/changelog/2026-06-30-component-prop-edits-and-shader-fills-now-preserve-rapid-sam.md +6 -0
  149. package/corpus/templates/design/changelog/2026-06-30-design-can-browse-figma-library-components-and-insert-them-i.md +6 -0
  150. package/corpus/templates/design/changelog/2026-06-30-design-now-has-a-figma-style-left-rail-for-file-agent-assets.md +6 -0
  151. package/corpus/templates/design/changelog/2026-06-30-design-now-has-native-components-embedded-media-and-figma-im.md +6 -0
  152. package/corpus/templates/design/changelog/2026-06-30-design-preview-now-includes-a-publish-app-waitlist-option-fr.md +6 -0
  153. package/corpus/templates/design/changelog/2026-06-30-design-s-left-rail-now-matches-the-sidebar-chrome-and-uses-t.md +6 -0
  154. package/corpus/templates/design/changelog/2026-06-30-design-s-tools-create-menu-now-stays-compact-instead-of-rend.md +6 -0
  155. package/corpus/templates/design/changelog/2026-06-30-design-starter-prompts-now-begin-generating-immediately-and-.md +6 -0
  156. package/corpus/templates/design/changelog/2026-06-30-design-token-edits-now-stay-visible-in-previews-and-token-li.md +6 -0
  157. package/corpus/templates/design/changelog/2026-06-30-design-tools-now-use-a-quieter-figma-style-list-layout.md +6 -0
  158. package/corpus/templates/design/changelog/2026-06-30-drawing-rectangles-on-the-all-screens-canvas-no-longer-flash.md +6 -0
  159. package/corpus/templates/design/changelog/2026-06-30-fixed-code-native-design-studio-edits-so-component-props-mot.md +6 -0
  160. package/corpus/templates/design/changelog/2026-06-30-fixed-visual-editing-hover-overlays-and-spacing-handles-so-s.md +6 -0
  161. package/corpus/templates/design/changelog/2026-06-30-motion-edits-now-save-automatically-without-a-separate-css-a.md +6 -0
  162. package/corpus/templates/design/changelog/2026-06-30-motion-timelines-now-reopen-as-editable-tracks-after-writing.md +6 -0
  163. package/corpus/templates/design/changelog/2026-06-30-new-design-prompts-with-uploaded-screenshots-work-reliably-o.md +6 -0
  164. package/corpus/templates/design/changelog/2026-06-30-screen-previews-in-all-screens-view-now-keep-rounded-outline.md +6 -0
  165. package/corpus/templates/design/changelog/2026-06-30-selection-outlines-now-sit-flush-on-rectangles-drawn-directl.md +6 -0
  166. package/corpus/templates/design/changelog/2026-06-30-share-options-use-a-tighter-design-editor-popover-with-compa.md +6 -0
  167. package/corpus/templates/design/changelog/2026-06-30-the-collapsed-motion-panel-now-stays-tucked-under-the-left-s.md +6 -0
  168. package/corpus/templates/design/changelog/2026-06-30-the-design-inspector-now-shows-states-and-review-in-a-tighte.md +6 -0
  169. package/corpus/templates/design/changelog/2026-06-30-the-sidebar-keeps-the-organization-picker-space-stable-while.md +6 -0
  170. package/corpus/templates/design/changelog/2026-06-30-tokens-can-now-be-imported-from-pasted-notes-code-files-fold.md +6 -0
  171. package/corpus/templates/design/e2e/global-setup.ts +94 -2
  172. package/corpus/templates/design/e2e/helpers.ts +168 -65
  173. package/corpus/templates/design/package.json +3 -1
  174. package/corpus/templates/design/server/db/schema.ts +23 -0
  175. package/corpus/templates/design/server/handlers/uploads.ts +22 -1
  176. package/corpus/templates/design/server/lib/provider-api.ts +7 -4
  177. package/corpus/templates/design/server/lib/verify-write-grant.ts +108 -0
  178. package/corpus/templates/design/server/plugins/db.ts +39 -0
  179. package/corpus/templates/design/server/register-secrets.ts +38 -0
  180. package/corpus/templates/design/shared/board-file.ts +228 -0
  181. package/corpus/templates/design/shared/board-objects.ts +288 -0
  182. package/corpus/templates/design/shared/code-layer.ts +52 -0
  183. package/corpus/templates/design/shared/component-model.ts +35 -0
  184. package/corpus/templates/design/shared/motion-compiler.ts +137 -15
  185. package/corpus/templates/design/shared/motion-timeline.ts +2 -2
  186. package/corpus/templates/design/shared/resolve-tweaks.ts +32 -9
  187. package/corpus/templates/design/tsconfig.json +2 -1
  188. package/corpus/templates/slides/AGENTS.md +9 -0
  189. package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +4 -0
  190. package/corpus/templates/slides/app/components/editor/ExportMenu.tsx +4 -24
  191. package/corpus/templates/slides/app/lib/export-pdf-client.ts +29 -20
  192. package/corpus/templates/slides/app/lib/export-pptx-client.ts +365 -0
  193. package/corpus/templates/slides/app/pages/DeckEditor.tsx +12 -9
  194. package/corpus/templates/slides/app/vite-env.d.ts +18 -0
  195. package/corpus/templates/slides/changelog/2026-06-30-powerpoint-downloads-now-preserve-the-rendered-slide-layout-.md +6 -0
  196. package/corpus/templates/slides/changelog/2026-06-30-unavailable-decks-open-in-a-cleaner-full-height-centered-sta.md +6 -0
  197. package/corpus/templates/slides/package.json +1 -0
  198. package/corpus/templates/slides/vite.config.ts +1 -0
  199. package/dist/agent/production-agent.d.ts.map +1 -1
  200. package/dist/agent/production-agent.js +94 -10
  201. package/dist/agent/production-agent.js.map +1 -1
  202. package/dist/cli/design-connect.d.ts +25 -0
  203. package/dist/cli/design-connect.d.ts.map +1 -1
  204. package/dist/cli/design-connect.js +357 -18
  205. package/dist/cli/design-connect.js.map +1 -1
  206. package/dist/client/AssistantChat.d.ts +14 -0
  207. package/dist/client/AssistantChat.d.ts.map +1 -1
  208. package/dist/client/AssistantChat.js +115 -22
  209. package/dist/client/AssistantChat.js.map +1 -1
  210. package/dist/client/ConnectBuilderCard.d.ts.map +1 -1
  211. package/dist/client/ConnectBuilderCard.js +1 -0
  212. package/dist/client/ConnectBuilderCard.js.map +1 -1
  213. package/dist/client/chat/index.d.ts +1 -1
  214. package/dist/client/chat/index.d.ts.map +1 -1
  215. package/dist/client/chat/index.js +1 -1
  216. package/dist/client/chat/index.js.map +1 -1
  217. package/dist/client/chat/run-recovery.d.ts.map +1 -1
  218. package/dist/client/chat/run-recovery.js +5 -5
  219. package/dist/client/chat/run-recovery.js.map +1 -1
  220. package/dist/client/chat-view-transition.d.ts +6 -0
  221. package/dist/client/chat-view-transition.d.ts.map +1 -1
  222. package/dist/client/chat-view-transition.js +23 -2
  223. package/dist/client/chat-view-transition.js.map +1 -1
  224. package/dist/client/composer/PromptComposer.d.ts.map +1 -1
  225. package/dist/client/composer/PromptComposer.js +3 -2
  226. package/dist/client/composer/PromptComposer.js.map +1 -1
  227. package/dist/client/index.d.ts +1 -1
  228. package/dist/client/index.d.ts.map +1 -1
  229. package/dist/client/index.js +1 -1
  230. package/dist/client/index.js.map +1 -1
  231. package/dist/client/org/OrgSwitcher.d.ts.map +1 -1
  232. package/dist/client/org/OrgSwitcher.js +11 -4
  233. package/dist/client/org/OrgSwitcher.js.map +1 -1
  234. package/dist/client/sharing/ShareButton.js +3 -3
  235. package/dist/client/sharing/ShareButton.js.map +1 -1
  236. package/dist/client/sse-event-processor.d.ts.map +1 -1
  237. package/dist/client/sse-event-processor.js +10 -0
  238. package/dist/client/sse-event-processor.js.map +1 -1
  239. package/dist/client/use-agent-chat-home-handoff.d.ts +8 -2
  240. package/dist/client/use-agent-chat-home-handoff.d.ts.map +1 -1
  241. package/dist/client/use-agent-chat-home-handoff.js +20 -6
  242. package/dist/client/use-agent-chat-home-handoff.js.map +1 -1
  243. package/dist/collab/awareness.d.ts +2 -2
  244. package/dist/collab/awareness.d.ts.map +1 -1
  245. package/dist/collab/routes.d.ts +2 -2
  246. package/dist/db/client.d.ts +1 -0
  247. package/dist/db/client.d.ts.map +1 -1
  248. package/dist/db/client.js +28 -2
  249. package/dist/db/client.js.map +1 -1
  250. package/dist/notifications/routes.d.ts +3 -3
  251. package/dist/observability/routes.d.ts +3 -3
  252. package/dist/progress/routes.d.ts +1 -1
  253. package/dist/provider-api/index.d.ts +5 -4
  254. package/dist/provider-api/index.d.ts.map +1 -1
  255. package/dist/provider-api/index.js +28 -0
  256. package/dist/provider-api/index.js.map +1 -1
  257. package/dist/resources/handlers.d.ts +3 -3
  258. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  259. package/dist/server/core-routes-plugin.d.ts +25 -0
  260. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  261. package/dist/server/core-routes-plugin.js +43 -18
  262. package/dist/server/core-routes-plugin.js.map +1 -1
  263. package/dist/server/transcribe-voice.d.ts +1 -1
  264. package/dist/styles/agent-native.css +54 -0
  265. package/dist/usage/store.js +2 -2
  266. package/dist/usage/store.js.map +1 -1
  267. package/package.json +1 -1
@@ -14,7 +14,6 @@
14
14
  */
15
15
 
16
16
  import {
17
- listAppState,
18
17
  readAppState,
19
18
  writeAppState,
20
19
  } from "@agent-native/core/application-state";
@@ -36,6 +35,7 @@ import {
36
35
  import finalizeRecording from "../../../../../actions/finalize-recording.js";
37
36
  import { getDb, schema } from "../../../../db/index.js";
38
37
  import { debugLog } from "../../../../lib/debug.js";
38
+ import { sumRecordingChunkBytes } from "../../../../lib/recording-upload-state.js";
39
39
  import {
40
40
  getEventOwnerContext,
41
41
  ownerEmailMatches,
@@ -82,14 +82,6 @@ function stateNumber(
82
82
  return typeof raw === "number" && Number.isFinite(raw) ? raw : undefined;
83
83
  }
84
84
 
85
- async function sumPersistedChunkBytes(recordingId: string): Promise<number> {
86
- const chunks = await listAppState(`recording-chunks-${recordingId}-`);
87
- return chunks.reduce(
88
- (total, entry) => total + (stateNumber(entry.value, "bytes") ?? 0),
89
- 0,
90
- );
91
- }
92
-
93
85
  export default defineEventHandler(async (event: H3Event) => {
94
86
  const recordingId = getRouterParam(event, "recordingId");
95
87
  if (!recordingId) {
@@ -278,7 +270,7 @@ export default defineEventHandler(async (event: H3Event) => {
278
270
  recordingId,
279
271
  status: "failed",
280
272
  failureReason: reason,
281
- bytesReceived: await sumPersistedChunkBytes(recordingId),
273
+ bytesReceived: await sumRecordingChunkBytes(ownerEmail, recordingId),
282
274
  maxBytes: MAX_RECORDING_UPLOAD_BYTES,
283
275
  updatedAt: new Date().toISOString(),
284
276
  });
@@ -323,7 +315,10 @@ export default defineEventHandler(async (event: H3Event) => {
323
315
  const chunkKey = `recording-chunks-${recordingId}-${paddedIndex}`;
324
316
  const previousChunk = await readAppState(chunkKey);
325
317
  const previousBytes = stateNumber(previousChunk, "bytes") ?? 0;
326
- const persistedBytesBefore = await sumPersistedChunkBytes(recordingId);
318
+ const persistedBytesBefore = await sumRecordingChunkBytes(
319
+ ownerEmail,
320
+ recordingId,
321
+ );
327
322
  const nextBytes =
328
323
  Math.max(0, persistedBytesBefore - previousBytes) + bytes.byteLength;
329
324
 
@@ -339,7 +334,7 @@ export default defineEventHandler(async (event: H3Event) => {
339
334
  data: toBase64(bytes),
340
335
  createdAt: new Date().toISOString(),
341
336
  });
342
- bytesReceived = await sumPersistedChunkBytes(recordingId);
337
+ bytesReceived = await sumRecordingChunkBytes(ownerEmail, recordingId);
343
338
  if (bytesReceived > MAX_RECORDING_UPLOAD_BYTES) {
344
339
  return failRecordingTooLarge(bytesReceived);
345
340
  }
@@ -396,7 +391,7 @@ export default defineEventHandler(async (event: H3Event) => {
396
391
  // Final chunk — kick off finalize. We await so the client gets a single
397
392
  // "done" response with the final URL (instead of needing to poll).
398
393
  if (isFinal) {
399
- bytesReceived = await sumPersistedChunkBytes(recordingId);
394
+ bytesReceived = await sumRecordingChunkBytes(ownerEmail, recordingId);
400
395
  if (bytesReceived > MAX_RECORDING_UPLOAD_BYTES) {
401
396
  return failRecordingTooLarge(bytesReceived);
402
397
  }
@@ -437,6 +432,61 @@ export default defineEventHandler(async (event: H3Event) => {
437
432
  };
438
433
  } catch (err) {
439
434
  console.error("[clips] finalize-recording failed:", err);
435
+ const [committed] = await db
436
+ .select({
437
+ id: schema.recordings.id,
438
+ status: schema.recordings.status,
439
+ videoUrl: schema.recordings.videoUrl,
440
+ durationMs: schema.recordings.durationMs,
441
+ width: schema.recordings.width,
442
+ height: schema.recordings.height,
443
+ hasAudio: schema.recordings.hasAudio,
444
+ hasCamera: schema.recordings.hasCamera,
445
+ })
446
+ .from(schema.recordings)
447
+ .where(
448
+ and(
449
+ eq(schema.recordings.id, recordingId),
450
+ ownerEmailMatches(schema.recordings.ownerEmail, ownerEmail),
451
+ ),
452
+ );
453
+ if (committed?.status === "ready" && committed.videoUrl) {
454
+ console.warn(
455
+ "[clips] finalize reported an error after committing a ready recording; returning committed success.",
456
+ {
457
+ recordingId,
458
+ error: err instanceof Error ? err.message : String(err),
459
+ },
460
+ );
461
+ try {
462
+ await writeAppState(`recording-upload-${recordingId}`, {
463
+ recordingId,
464
+ status: "ready",
465
+ progress: 100,
466
+ videoUrl: committed.videoUrl,
467
+ finishedAt: new Date().toISOString(),
468
+ });
469
+ } catch (stateErr) {
470
+ console.warn("[clips] committed-ready state repair failed:", {
471
+ recordingId,
472
+ err:
473
+ stateErr instanceof Error ? stateErr.message : String(stateErr),
474
+ });
475
+ }
476
+ return {
477
+ ok: true,
478
+ finalized: true,
479
+ recoveredAfterFinalizeError: true,
480
+ id: committed.id,
481
+ status: "ready",
482
+ videoUrl: committed.videoUrl,
483
+ durationMs: committed.durationMs,
484
+ width: committed.width,
485
+ height: committed.height,
486
+ hasAudio: committed.hasAudio,
487
+ hasCamera: committed.hasCamera,
488
+ };
489
+ }
440
490
  await db
441
491
  .update(schema.recordings)
442
492
  .set({
@@ -0,0 +1,74 @@
1
+ import { runWithRequestContext } from "@agent-native/core/server";
2
+ import { and, eq } from "drizzle-orm";
3
+ import {
4
+ createError,
5
+ defineEventHandler,
6
+ getRouterParam,
7
+ setResponseStatus,
8
+ type H3Event,
9
+ } from "h3";
10
+
11
+ import { getDb, schema } from "../../../../db/index.js";
12
+ import { resolvePlayerVideoUrl } from "../../../../lib/player-video-url.js";
13
+ import {
14
+ getEventOwnerContext,
15
+ ownerEmailMatches,
16
+ } from "../../../../lib/recordings.js";
17
+
18
+ function appPath(path: string): string {
19
+ if (!path.startsWith("/")) return path;
20
+ const raw = process.env.VITE_APP_BASE_PATH || process.env.APP_BASE_PATH || "";
21
+ const base = raw.trim().replace(/^\/+/, "").replace(/\/+$/, "");
22
+ return base ? `/${base}${path}` : path;
23
+ }
24
+
25
+ export default defineEventHandler(async (event: H3Event) => {
26
+ const recordingId = getRouterParam(event, "recordingId");
27
+ if (!recordingId) {
28
+ throw createError({ statusCode: 400, message: "Missing recordingId" });
29
+ }
30
+
31
+ let ownerEmail: string;
32
+ let orgId: string | undefined;
33
+ try {
34
+ const context = await getEventOwnerContext(event);
35
+ ownerEmail = context.userEmail;
36
+ orgId = context.orgId;
37
+ } catch {
38
+ throw createError({ statusCode: 401, message: "Unauthorized" });
39
+ }
40
+
41
+ return runWithRequestContext({ userEmail: ownerEmail, orgId }, async () => {
42
+ const [recording] = await getDb()
43
+ .select()
44
+ .from(schema.recordings)
45
+ .where(
46
+ and(
47
+ eq(schema.recordings.id, recordingId),
48
+ ownerEmailMatches(schema.recordings.ownerEmail, ownerEmail),
49
+ ),
50
+ )
51
+ .limit(1);
52
+
53
+ if (!recording) {
54
+ setResponseStatus(event, 404);
55
+ return { error: "Not found" };
56
+ }
57
+
58
+ return {
59
+ recording: {
60
+ id: recording.id,
61
+ status: recording.status,
62
+ videoUrl: resolvePlayerVideoUrl(recording, { appPath }),
63
+ durationMs: recording.durationMs,
64
+ width: recording.width,
65
+ height: recording.height,
66
+ hasAudio: Boolean(recording.hasAudio),
67
+ hasCamera: Boolean(recording.hasCamera),
68
+ uploadProgress: recording.uploadProgress,
69
+ failureReason: recording.failureReason,
70
+ updatedAt: recording.updatedAt,
71
+ },
72
+ };
73
+ });
74
+ });
@@ -166,6 +166,24 @@ pnpm action index-design-system-with-builder --codeFiles '[{"filename":"globals.
166
166
 
167
167
  Uploads code/design files to Builder and starts design-system indexing. Do not create a local design system from uploaded code files unless the user explicitly asks for a manual local fallback.
168
168
 
169
+ For the active design's Tokens panel, use `import-design-tokens` when the user
170
+ wants to pull reusable CSS vars/tokens straight into the open design without
171
+ creating a full design system:
172
+
173
+ ```bash
174
+ pnpm action import-design-tokens \
175
+ --designId "design_123" \
176
+ --source files \
177
+ --files '[{"filename":"design.md","content":"Primary color: #2563eb"}]'
178
+ ```
179
+
180
+ Supported sources are `files`, `paste`, and `current-design`. The action parses
181
+ CSS variables, design.md-style labeled lines, Tailwind/theme JSON, colors,
182
+ spacing, radii, and fonts, then persists them through the design's
183
+ `tweakSelections` so the canvas updates like any other token edit. Treat manual
184
+ `apply-design-token-edit` calls as a last-resort one-off edit after import has
185
+ been tried or ruled out.
186
+
169
187
  ### Source: Documents (DOCX, PPTX, PDF)
170
188
 
171
189
  ```bash
@@ -204,6 +222,20 @@ indexing through the setup page upload route. Do not parse `.fig` files locally
204
222
  and do not call `create-design-system` from raw `.fig` output; Builder owns the
205
223
  indexed brand kit, generated docs, and usage guidance.
206
224
 
225
+ **When the user wants a Figma Assets-style native component drawer inside
226
+ Design**, do not use Figma or media assets. Use `list-design-native-assets` to
227
+ choose an editable primitive/component/layout, then
228
+ `insert-design-native-asset` to insert it into the active screen. These entries
229
+ are Design-native HTML stamped with component/layer metadata.
230
+
231
+ **When the user wants reusable Figma components/assets**, do not run
232
+ design-system indexing just to insert a component. Use
233
+ `list-figma-library-assets` with a Figma file URL/key, then
234
+ `insert-figma-library-asset` with the returned `renderUrl`, `fileKey`,
235
+ `nodeId`, `componentKey`, and `sourceUrl`. This inserts a rendered
236
+ component/component set with provenance. Styles and variables still belong in
237
+ the Builder-backed design-system path above.
238
+
207
239
  ### Source: Brand Analysis (combines website + notes)
208
240
 
209
241
  ```bash
@@ -32,6 +32,11 @@ patterns live in `.agents/skills/`.
32
32
  per direction; avoid lorem ipsum, generic SaaS filler, and decorative
33
33
  placeholders. Include expected responsive/accessibility states and visually
34
34
  inspect the result before calling it ready.
35
+ - For editable reusable building blocks inside Design, use
36
+ `list-design-native-assets` first, then `insert-design-native-asset` with the
37
+ chosen kind. These are Design-native HTML primitives/components, not external
38
+ media, so prefer them when the user asks for a Figma Assets-style library that
39
+ supports our own primitives.
35
40
  - For raster image generation, restyling, or editing existing screenshots/photos,
36
41
  use the available first-party Assets MCP tool such as `generate-asset` instead
37
42
  of placeholders or generic stock imagery. When the Assets picker returns a
@@ -43,6 +48,15 @@ patterns live in `.agents/skills/`.
43
48
  chat-attachment URL or call `upload-image` to create one before delegating. If
44
49
  no image/upload provider is configured, say that specific setup is needed and
45
50
  continue any non-image Design work separately.
51
+ - For reusable Figma library components, use `list-figma-library-assets` with a
52
+ Figma file URL or file key. It returns components/component sets with
53
+ thumbnails, rendered insert URLs, and Figma provenance. Insert with
54
+ `insert-figma-library-asset`, preserving `fileKey`, `nodeId`, `componentKey`,
55
+ `sourceUrl`, and the rendered URL. This path requires the saved
56
+ `FIGMA_ACCESS_TOKEN` secret; never ask the user to paste that token into chat
57
+ or pass it as an action parameter. Figma styles and variables are design-system
58
+ inputs, not draggable media assets; route full file/design-system extraction
59
+ through Builder-backed indexing.
46
60
  - Use Alpine.js and Tailwind CDN for interactive prototypes. Prefer Alpine
47
61
  directives over raw inline event handlers.
48
62
  - Navigate between prototype screens with Alpine state (`x-show`), a
@@ -72,6 +86,13 @@ patterns live in `.agents/skills/`.
72
86
  `inspectorTab: "extensions"` after installing it.
73
87
  - Follow linked design-system tokens and `customInstructions` whenever present;
74
88
  explicit user instructions in the current turn still win.
89
+ - When a user wants tokens from design.md, CSS, theme/tokens JSON, Tailwind
90
+ config, local files, or the current design, call `import-design-tokens` and
91
+ preserve its `tokens`, `filesAnalyzed`, and provenance in your answer. Manual
92
+ `apply-design-token-edit` / one-by-one token entry is the fallback for a small
93
+ one-off token, not the primary import workflow. For Figma variables/styles or
94
+ raw `.fig` files, keep using Builder-backed design-system indexing instead of
95
+ local `.fig` parsing.
75
96
  - Persist useful work early: create/update the design and files as soon as a
76
97
  coherent candidate exists, then iterate.
77
98
  - For non-trivial new design prompts, ask before generating: create/open the
@@ -104,6 +125,12 @@ patterns live in `.agents/skills/`.
104
125
  generated CSS selectors as a compatibility fallback only. For localhost React
105
126
  screens, resolve through build-time source/debug metadata (stable generated
106
127
  ids, component name, file, and line) before falling back to selectors.
128
+ - For inline/Alpine motion, use `get-motion-timeline` to inspect the active
129
+ file's saved or CSS-recovered timeline, then call `apply-motion-edit` with the
130
+ same `sourceRef`/`fileId` to update it. Motion edits persist as durable
131
+ managed keyframes in `<style data-agent-native-motion>` plus editable timeline
132
+ metadata; it is not a one-way export. Real CSS module or `motion/react`
133
+ write-back remains a localhost/fusion follow-up capability.
107
134
  - For multi-variant work, use `present-design-variants` so every candidate is
108
135
  saved as a normal overview-board screen and the user gets one inline chat
109
136
  button per screen name. After the user picks, delete the unchosen variant
@@ -396,7 +396,7 @@ Requirements:
396
396
  - diamond keyframes
397
397
  - play/scrub controls
398
398
  - auto-keyframe control
399
- - primary `Write to CSS` action
399
+ - autosave status for managed CSS persistence
400
400
  - compact Motion/Keyframe inspector section
401
401
  - no separate motion app/screen
402
402
 
@@ -347,7 +347,7 @@ The CSS is the runtime truth; the JSON `tracks` only aid editing.
347
347
 
348
348
  - **UI (matches the motion artboard):** a full-width, collapsible **Motion dock**
349
349
  at the bottom of `DesignEditor.tsx` (animated layer rows, property tracks, time
350
- ruler, diamond keyframes, play/scrub, auto-keyframe toggle, **Write to CSS**),
350
+ ruler, diamond keyframes, play/scrub, auto-keyframe toggle, autosave status),
351
351
  with the canvas still visible above; a compact **Motion section** in the
352
352
  inspector for the selected keyframe (numeric + easing + transform/opacity).
353
353
  - **Preview:** scrubbing sends preview-only bridge messages (`motion-preview` /
@@ -360,8 +360,8 @@ The CSS is the runtime truth; the JSON `tracks` only aid editing.
360
360
  **Real-app superset:** write keyframes into real CSS modules, optional
361
361
  `motion`-react round-trip, Dev Mode export.
362
362
 
363
- **Acceptance:** scrubbing never writes; **Write to CSS** is one atomic action with
364
- diff/rollback proof; multiple layers and tracks animate.
363
+ **Acceptance:** scrubbing never writes; timeline edits autosave through one atomic
364
+ action with diff/rollback proof; multiple layers and tracks animate.
365
365
 
366
366
  ### 6.4 States, responsive & captures
367
367
 
@@ -615,7 +615,7 @@ motion edits safely, use review outputs, and offer Builder migration CTAs.
615
615
  runtime + source-write + fallback + diff proof.
616
616
 
617
617
  **First user-visible milestone:** on an inline design, edit a token (live, persisted)
618
- and add a keyframe animation that **Write to CSS** commits with a visible diff —
618
+ and add a keyframe animation that autosaves to managed CSS with a visible diff —
619
619
  "visual edit becomes persisted code/CSS" without the whole real-app stack.
620
620
 
621
621
  ---
@@ -659,7 +659,7 @@ and add a keyframe animation that **Write to CSS** commits with a visible diff
659
659
  (Mobile) is untouched; the Desktop frame reflects the cascade; reset-to-base clears
660
660
  the override; the same flow works on an inline design and a real-app URL.
661
661
  - **Alpine smoke:** edit layout/styles; preview + persist a CSS-var token change;
662
- add simple keyframes and Write to CSS; run a basic a11y scan; see the real-app CTA
662
+ add simple keyframes that autosave to managed CSS; run a basic a11y scan; see the real-app CTA
663
663
  for full component controls.
664
664
 
665
665
  ---
@@ -25,7 +25,6 @@ import {
25
25
  agentLeaveDocument,
26
26
  agentUpdateSelection,
27
27
  applyText,
28
- getText,
29
28
  hasCollabState,
30
29
  seedFromText,
31
30
  } from "@agent-native/core/collab";
@@ -49,7 +48,10 @@ import type {
49
48
  ClassEditIntent,
50
49
  StyleEditIntent,
51
50
  } from "../shared/code-layer.js";
52
- import { componentNameFor } from "../shared/component-model.js";
51
+ import {
52
+ componentNameFor,
53
+ componentNodeIdMatches,
54
+ } from "../shared/component-model.js";
53
55
  import { hasCapability } from "../shared/design-source-capabilities.js";
54
56
  import { normalizeDesignSourceType } from "../shared/source-mode.js";
55
57
 
@@ -114,26 +116,11 @@ export function applyRootAttributeEdit(
114
116
  };
115
117
  }
116
118
 
117
- async function liveContent(
118
- fileId: string,
119
- storedContent: string,
120
- ): Promise<string> {
121
- try {
122
- if (await hasCollabState(fileId)) {
123
- const live = await getText(fileId, "content");
124
- if (typeof live === "string") return live;
125
- }
126
- } catch {
127
- // Collab reads are best-effort; SQL content is the fallback.
128
- }
129
- return storedContent;
130
- }
131
-
132
119
  async function persistEdit(file: {
133
120
  id: string;
134
121
  designId: string;
135
122
  content: string;
136
- }): Promise<void> {
123
+ }): Promise<string> {
137
124
  await assertAccess("design", file.designId, "editor");
138
125
  const db = getDb();
139
126
  const now = new Date().toISOString();
@@ -158,6 +145,8 @@ async function persistEdit(file: {
158
145
  } finally {
159
146
  agentLeaveDocument(file.id);
160
147
  }
148
+
149
+ return now;
161
150
  }
162
151
 
163
152
  // ─── Action ───────────────────────────────────────────────────────────────────
@@ -210,8 +199,24 @@ export default defineAction({
210
199
  }),
211
200
  ])
212
201
  .describe("The prop edit to apply"),
202
+ source: z
203
+ .object({
204
+ currentContent: z
205
+ .string()
206
+ .optional()
207
+ .describe(
208
+ "Latest editor HTML snapshot. Used to compose rapid sequential prop edits before collab persistence catches up.",
209
+ ),
210
+ revision: z
211
+ .string()
212
+ .optional()
213
+ .describe(
214
+ "design_files.updatedAt value the currentContent is based on.",
215
+ ),
216
+ })
217
+ .optional(),
213
218
  }),
214
- run: async ({ designId, nodeId, fileId, edit }) => {
219
+ run: async ({ designId, nodeId, fileId, edit, source }) => {
215
220
  const db = getDb();
216
221
 
217
222
  // ── Access check ────────────────────────────────────────────────────────
@@ -271,6 +276,7 @@ export default defineAction({
271
276
  designId: schema.designFiles.designId,
272
277
  filename: schema.designFiles.filename,
273
278
  content: schema.designFiles.content,
279
+ updatedAt: schema.designFiles.updatedAt,
274
280
  })
275
281
  .from(schema.designFiles)
276
282
  .innerJoin(
@@ -282,7 +288,31 @@ export default defineAction({
282
288
 
283
289
  if (!file) throw new Error("Design HTML file not found.");
284
290
 
285
- const html = await liveContent(file.id, file.content ?? "");
291
+ if (
292
+ source?.currentContent &&
293
+ source.revision &&
294
+ file.updatedAt &&
295
+ source.revision !== file.updatedAt
296
+ ) {
297
+ return {
298
+ designId,
299
+ nodeId,
300
+ persisted: false,
301
+ conflict: true,
302
+ fileId: file.id,
303
+ filename: file.filename,
304
+ error:
305
+ "This file changed since this component prop edit was prepared. Refresh the editor and try again.",
306
+ };
307
+ }
308
+
309
+ // Prefer explicit editor content after the caller's revision check, and use
310
+ // the saved SQL content as the fallback. Collab/Yjs reads can be stale
311
+ // across local dev worker processes and make prop controls lag behind.
312
+ const html =
313
+ typeof source?.currentContent === "string"
314
+ ? source.currentContent
315
+ : (file.content ?? "");
286
316
 
287
317
  // ── Resolve node ─────────────────────────────────────────────────────────
288
318
  const codeLayerSource: CodeLayerSource = {
@@ -296,7 +326,9 @@ export default defineAction({
296
326
  source: codeLayerSource,
297
327
  });
298
328
 
299
- const node = projection.nodes.find((n) => n.id === nodeId);
329
+ const node = projection.nodes.find((n) =>
330
+ componentNodeIdMatches(n, nodeId),
331
+ );
300
332
  if (!node) {
301
333
  throw new Error(
302
334
  `Node "${nodeId}" not found. Run get-code-layer-projection to list current ids.`,
@@ -398,8 +430,10 @@ export default defineAction({
398
430
  }
399
431
 
400
432
  // ── Persist ──────────────────────────────────────────────────────────────
401
- if (changed) {
402
- await persistEdit({
433
+ const shouldPersist = changed || patchedContent !== (file.content ?? "");
434
+
435
+ if (shouldPersist) {
436
+ const updatedAt = await persistEdit({
403
437
  id: file.id,
404
438
  designId: file.designId,
405
439
  content: patchedContent,
@@ -411,6 +445,22 @@ export default defineAction({
411
445
  editingFile: file.filename,
412
446
  designId: file.designId,
413
447
  });
448
+ return {
449
+ designId,
450
+ nodeId,
451
+ componentName,
452
+ sourceType,
453
+ editKind: edit.kind,
454
+ persisted: shouldPersist,
455
+ ctaRequired: false,
456
+ fileId: file.id,
457
+ filename: file.filename,
458
+ updatedAt,
459
+ content: patchedContent,
460
+ bytesBefore: html.length,
461
+ bytesAfter: patchedContent.length,
462
+ note: "Edit applied and persisted via the deterministic HTML-patch path.",
463
+ };
414
464
  }
415
465
 
416
466
  return {
@@ -419,13 +469,14 @@ export default defineAction({
419
469
  componentName,
420
470
  sourceType,
421
471
  editKind: edit.kind,
422
- persisted: changed,
472
+ persisted: shouldPersist,
423
473
  ctaRequired: false,
424
474
  fileId: file.id,
425
475
  filename: file.filename,
476
+ content: patchedContent,
426
477
  bytesBefore: html.length,
427
478
  bytesAfter: patchedContent.length,
428
- note: changed
479
+ note: shouldPersist
429
480
  ? "Edit applied and persisted via the deterministic HTML-patch path."
430
481
  : "No change applied — the edit produced the same result as the current content.",
431
482
  };
@@ -152,10 +152,23 @@ export default defineAction({
152
152
  if (route !== undefined) patch.route = route;
153
153
  if (fixtureData !== undefined) {
154
154
  // Sanitise replayed markup (stored-XSS guard) before persisting.
155
- patch.fixtureData =
156
- fixtureData !== null
157
- ? JSON.stringify(sanitizeStatePayload(fixtureData))
158
- : null;
155
+ if (fixtureData !== null) {
156
+ const fixtureDataJson = JSON.stringify(
157
+ sanitizeStatePayload(fixtureData),
158
+ );
159
+ if (
160
+ Buffer.byteLength(fixtureDataJson, "utf8") > CAPTURE_DATA_MAX_BYTES
161
+ ) {
162
+ throw new Error(
163
+ `fixtureData exceeds the ${Math.round(
164
+ CAPTURE_DATA_MAX_BYTES / 1024,
165
+ )}KB limit. Trim the payload before updating this fixture.`,
166
+ );
167
+ }
168
+ patch.fixtureData = fixtureDataJson;
169
+ } else {
170
+ patch.fixtureData = null;
171
+ }
159
172
  }
160
173
  if (captureData !== undefined) {
161
174
  if (captureData !== null) {