@agent-native/core 0.92.10 → 0.92.12

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 (354) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +29 -0
  3. package/corpus/core/package.json +1 -1
  4. package/corpus/core/src/agent/production-agent.ts +214 -17
  5. package/corpus/core/src/agent/run-manager.ts +72 -14
  6. package/corpus/core/src/agent/run-store.ts +152 -27
  7. package/corpus/core/src/cli/design-connect.ts +433 -151
  8. package/corpus/core/src/cli/skills.ts +14 -0
  9. package/corpus/core/src/client/AssistantChat.tsx +25 -0
  10. package/corpus/core/src/client/MultiTabAssistantChat.tsx +40 -6
  11. package/corpus/core/src/client/agent-chat-adapter.ts +56 -12
  12. package/corpus/core/src/client/agent-chat.ts +165 -1
  13. package/corpus/core/src/client/chat/index.ts +9 -1
  14. package/corpus/core/src/client/extensions/EmbeddedExtension.tsx +38 -2
  15. package/corpus/core/src/client/index.ts +5 -0
  16. package/corpus/core/src/client/rich-markdown-editor/useCollabReconcile.ts +18 -7
  17. package/corpus/core/src/collab/awareness-store.ts +23 -12
  18. package/corpus/core/src/collab/client.ts +16 -5
  19. package/corpus/core/src/collab/index.ts +4 -0
  20. package/corpus/core/src/collab/recent-edits.ts +65 -2
  21. package/corpus/core/src/index.browser.ts +1 -0
  22. package/corpus/core/src/server/agent-chat-plugin.ts +98 -11
  23. package/corpus/core/src/server/builder-design-systems.ts +30 -4
  24. package/corpus/templates/clips/.agents/skills/ai-video-tools/SKILL.md +1 -1
  25. package/corpus/templates/clips/.agents/skills/video-sharing/SKILL.md +12 -6
  26. package/corpus/templates/clips/AGENTS.md +4 -0
  27. package/corpus/templates/clips/actions/create-recording.ts +2 -0
  28. package/corpus/templates/clips/actions/lib/create-recording-schema.ts +5 -3
  29. package/corpus/templates/clips/actions/request-transcript.ts +102 -33
  30. package/corpus/templates/clips/app/components/library/library-grid.tsx +1 -0
  31. package/corpus/templates/clips/app/components/player/share-dialog.tsx +59 -19
  32. package/corpus/templates/clips/app/components/player/transcript-panel.tsx +75 -9
  33. package/corpus/templates/clips/app/components/player/video-player.tsx +17 -12
  34. package/corpus/templates/clips/app/hooks/use-player-shortcuts.ts +1 -0
  35. package/corpus/templates/clips/app/i18n/ar-SA.ts +1 -0
  36. package/corpus/templates/clips/app/i18n/de-DE.ts +1 -0
  37. package/corpus/templates/clips/app/i18n/en-US.ts +1 -0
  38. package/corpus/templates/clips/app/i18n/es-ES.ts +1 -0
  39. package/corpus/templates/clips/app/i18n/fr-FR.ts +1 -0
  40. package/corpus/templates/clips/app/i18n/hi-IN.ts +1 -0
  41. package/corpus/templates/clips/app/i18n/ja-JP.ts +1 -0
  42. package/corpus/templates/clips/app/i18n/ko-KR.ts +1 -0
  43. package/corpus/templates/clips/app/i18n/pt-BR.ts +1 -0
  44. package/corpus/templates/clips/app/i18n/zh-CN.ts +1 -0
  45. package/corpus/templates/clips/app/i18n/zh-TW.ts +1 -0
  46. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +70 -37
  47. package/corpus/templates/clips/app/routes/record.tsx +17 -4
  48. package/corpus/templates/clips/app/routes/share.$shareId.tsx +2 -0
  49. package/corpus/templates/clips/changelog/2026-07-09-desktop-popover-sits-closer-to-the-menu-bar.md +6 -0
  50. package/corpus/templates/clips/changelog/2026-07-09-desktop-recording-optimization-progress-stays-visible-until-the-clip-opens.md +6 -0
  51. package/corpus/templates/clips/changelog/2026-07-09-fixed-large-video-file-uploads-failing-before-their-resumabl.md +6 -0
  52. package/corpus/templates/clips/changelog/2026-07-09-fixed-new-video-uploads-defaulting-to-private.md +6 -0
  53. package/corpus/templates/clips/changelog/2026-07-09-long-recordings-now-get-enough-time-to-download-their-media-.md +6 -0
  54. package/corpus/templates/clips/changelog/2026-07-09-meeting-notes-no-longer-lowers-your-microphone-volume-for-ot.md +6 -0
  55. package/corpus/templates/clips/changelog/2026-07-09-on-mobile-tapping-a-playing-clip-reveals-playback-controls-w.md +6 -0
  56. package/corpus/templates/clips/changelog/2026-07-09-regenerate-any-clip-transcript-from-its-saved-recording-or-r.md +6 -0
  57. package/corpus/templates/clips/changelog/2026-07-09-slack-previews-now-show-the-clip-length-alongside-its-descri.md +6 -0
  58. package/corpus/templates/clips/changelog/2026-07-10-fixed-copying-agent-responses-and-transcript-text-from-recor.md +6 -0
  59. package/corpus/templates/clips/desktop/src/app.tsx +12 -2
  60. package/corpus/templates/clips/desktop/src/hooks/useMeetingTranscription.ts +8 -1
  61. package/corpus/templates/clips/desktop/src/lib/recorder.ts +8 -5
  62. package/corpus/templates/clips/desktop/src/lib/transcription-engine.ts +5 -5
  63. package/corpus/templates/clips/desktop/src/lib/voice-dictation.ts +3 -0
  64. package/corpus/templates/clips/desktop/src/styles.css +6 -3
  65. package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +29 -11
  66. package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +1 -1
  67. package/corpus/templates/clips/desktop/src-tauri/src/system_audio.rs +103 -21
  68. package/corpus/templates/clips/desktop/src-tauri/src/whisper_speech.rs +117 -39
  69. package/corpus/templates/clips/server/lib/slack-unfurls.ts +20 -1
  70. package/corpus/templates/clips/server/lib/streaming-upload-mode.ts +10 -4
  71. package/corpus/templates/clips/shared/recording-core.ts +12 -0
  72. package/corpus/templates/content/AGENTS.md +7 -6
  73. package/corpus/templates/content/actions/_builder-cms-read-client.ts +82 -7
  74. package/corpus/templates/content/actions/_database-source-utils.ts +149 -30
  75. package/corpus/templates/content/actions/_document-flush.ts +132 -17
  76. package/corpus/templates/content/actions/_notion-action-utils.ts +15 -0
  77. package/corpus/templates/content/actions/add-content-database-source-field-property.ts +4 -7
  78. package/corpus/templates/content/actions/attach-content-database-source.ts +41 -17
  79. package/corpus/templates/content/actions/change-content-database-source-role.ts +46 -8
  80. package/corpus/templates/content/actions/create-and-link-notion-page.ts +2 -0
  81. package/corpus/templates/content/actions/link-notion-page.ts +2 -0
  82. package/corpus/templates/content/actions/pull-document.ts +5 -7
  83. package/corpus/templates/content/actions/pull-notion-page.ts +2 -0
  84. package/corpus/templates/content/actions/push-notion-page.ts +10 -0
  85. package/corpus/templates/content/actions/resolve-notion-sync-conflict.ts +2 -0
  86. package/corpus/templates/content/actions/suggest-source-join-key.ts +8 -1
  87. package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +88 -23
  88. package/corpus/templates/content/app/components/editor/VisualEditor.tsx +32 -2
  89. package/corpus/templates/content/app/hooks/use-notion.ts +6 -6
  90. package/corpus/templates/content/app/i18n/zh-TW.ts +1 -0
  91. package/corpus/templates/content/app/i18n-data.ts +18 -0
  92. package/corpus/templates/content/changelog/2026-07-09-builder-source-refreshes-now-keep-mapped-fields-such-as-topi.md +6 -0
  93. package/corpus/templates/content/changelog/2026-07-09-notion-conflict-choices-and-version-restores-preserve-docume.md +6 -0
  94. package/corpus/templates/content/server/lib/notion-sync.ts +80 -64
  95. package/corpus/templates/content/shared/api.ts +1 -0
  96. package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +29 -2
  97. package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +86 -0
  98. package/corpus/templates/design/.agents/skills/export-handoff/SKILL.md +57 -0
  99. package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +11 -0
  100. package/corpus/templates/design/AGENTS.md +45 -10
  101. package/corpus/templates/design/actions/add-localhost-screens.ts +66 -13
  102. package/corpus/templates/design/actions/apply-component-prop-edit.ts +45 -36
  103. package/corpus/templates/design/actions/apply-motion-edit.ts +25 -31
  104. package/corpus/templates/design/actions/apply-shader-fill.ts +29 -42
  105. package/corpus/templates/design/actions/detach-component-instance.ts +337 -0
  106. package/corpus/templates/design/actions/export-design-as-figma-svg.ts +219 -0
  107. package/corpus/templates/design/actions/generate-design.ts +79 -12
  108. package/corpus/templates/design/actions/generate-screens.ts +57 -12
  109. package/corpus/templates/design/actions/get-figma-styles.ts +115 -0
  110. package/corpus/templates/design/actions/go-to-main-component.ts +254 -0
  111. package/corpus/templates/design/actions/grant-localhost-write-consent.ts +4 -3
  112. package/corpus/templates/design/actions/import-design-source.ts +4 -31
  113. package/corpus/templates/design/actions/import-figma-clipboard.ts +134 -0
  114. package/corpus/templates/design/actions/import-figma-frame.ts +95 -0
  115. package/corpus/templates/design/actions/index-design-system-with-builder.ts +16 -1
  116. package/corpus/templates/design/actions/list-design-components.ts +104 -0
  117. package/corpus/templates/design/actions/list-figma-library-assets.ts +1 -24
  118. package/corpus/templates/design/actions/open-component-source.ts +2 -4
  119. package/corpus/templates/design/actions/present-design-variants.ts +412 -16
  120. package/corpus/templates/design/actions/preview-source-edit.ts +14 -1
  121. package/corpus/templates/design/actions/run-design-audit.ts +25 -3
  122. package/corpus/templates/design/actions/show-design-questions.ts +10 -0
  123. package/corpus/templates/design/actions/swap-component-instance.ts +544 -0
  124. package/corpus/templates/design/actions/take-design-screenshot.ts +45 -60
  125. package/corpus/templates/design/actions/write-local-file.ts +25 -1
  126. package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +152 -0
  127. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +1077 -58
  128. package/corpus/templates/design/app/components/design/DesignEditorSkeleton.tsx +5 -5
  129. package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +73 -21
  130. package/corpus/templates/design/app/components/design/EditPanel.tsx +16 -11
  131. package/corpus/templates/design/app/components/design/KeyboardShortcutsPanel.tsx +364 -0
  132. package/corpus/templates/design/app/components/design/LayersPanel.tsx +180 -53
  133. package/corpus/templates/design/app/components/design/LocalhostWriteConsentDialog.tsx +0 -2
  134. package/corpus/templates/design/app/components/design/MotionDock.tsx +144 -21
  135. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +1152 -167
  136. package/corpus/templates/design/app/components/design/QuestionFlow.tsx +27 -5
  137. package/corpus/templates/design/app/components/design/ReviewPanel.tsx +23 -2
  138. package/corpus/templates/design/app/components/design/TokensPanel.tsx +63 -49
  139. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +1293 -54
  140. package/corpus/templates/design/app/components/design/bridge/embedded-wheel.bridge.ts +7 -0
  141. package/corpus/templates/design/app/components/design/bridge/hit-test.bridge.ts +6 -1
  142. package/corpus/templates/design/app/components/design/bridge/motion-preview.bridge.ts +23 -6
  143. package/corpus/templates/design/app/components/design/bridge/sample.bridge.ts +7 -0
  144. package/corpus/templates/design/app/components/design/code-workbench/CodeWorkbench.tsx +5 -0
  145. package/corpus/templates/design/app/components/design/code-workbench/editor/MonacoHost.tsx +14 -2
  146. package/corpus/templates/design/app/components/design/code-workbench/explorer/FileTree.tsx +37 -2
  147. package/corpus/templates/design/app/components/design/code-workbench/format/format-on-open-guard.ts +25 -0
  148. package/corpus/templates/design/app/components/design/code-workbench/format/format-on-open.ts +19 -1
  149. package/corpus/templates/design/app/components/design/code-workbench/model-registry.ts +43 -5
  150. package/corpus/templates/design/app/components/design/code-workbench/search/SearchView.tsx +27 -2
  151. package/corpus/templates/design/app/components/design/code-workbench/search/search-engine.ts +10 -1
  152. package/corpus/templates/design/app/components/design/code-workbench/store.tsx +19 -2
  153. package/corpus/templates/design/app/components/design/design-canvas/annotation-snapshot.ts +265 -0
  154. package/corpus/templates/design/app/components/design/design-canvas/annotation-submit.ts +16 -5
  155. package/corpus/templates/design/app/components/design/design-canvas/coordinate-transforms.ts +16 -2
  156. package/corpus/templates/design/app/components/design/design-canvas/embedded-frame.ts +7 -2
  157. package/corpus/templates/design/app/components/design/design-canvas/iframe-events.ts +3 -1
  158. package/corpus/templates/design/app/components/design/edit-panel/code-inspect-helpers.tsx +78 -8
  159. package/corpus/templates/design/app/components/design/edit-panel/component-section.tsx +451 -73
  160. package/corpus/templates/design/app/components/design/edit-panel/document-colors.ts +12 -4
  161. package/corpus/templates/design/app/components/design/edit-panel/effects-properties.tsx +398 -272
  162. package/corpus/templates/design/app/components/design/edit-panel/element-classification.ts +41 -7
  163. package/corpus/templates/design/app/components/design/edit-panel/element-identity.ts +16 -3
  164. package/corpus/templates/design/app/components/design/edit-panel/field-primitives.tsx +24 -4
  165. package/corpus/templates/design/app/components/design/edit-panel/fill-gradient-helpers.ts +222 -4
  166. package/corpus/templates/design/app/components/design/edit-panel/fill-properties.tsx +171 -78
  167. package/corpus/templates/design/app/components/design/edit-panel/inspector-controls.tsx +45 -5
  168. package/corpus/templates/design/app/components/design/edit-panel/layout-properties.tsx +299 -28
  169. package/corpus/templates/design/app/components/design/edit-panel/panel-primitives.tsx +120 -150
  170. package/corpus/templates/design/app/components/design/edit-panel/position-helpers.ts +53 -1
  171. package/corpus/templates/design/app/components/design/edit-panel/position-layout-properties.tsx +284 -117
  172. package/corpus/templates/design/app/components/design/edit-panel/selection-helpers.ts +81 -0
  173. package/corpus/templates/design/app/components/design/edit-panel/stroke-properties.tsx +19 -14
  174. package/corpus/templates/design/app/components/design/edit-panel/style-options.ts +7 -0
  175. package/corpus/templates/design/app/components/design/edit-panel/typography-helpers.ts +155 -0
  176. package/corpus/templates/design/app/components/design/edit-panel/typography-properties.tsx +268 -46
  177. package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +350 -31
  178. package/corpus/templates/design/app/components/design/inspector/ConstraintsWidget.tsx +13 -4
  179. package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +233 -23
  180. package/corpus/templates/design/app/components/design/inspector/GradientEditor.tsx +72 -2
  181. package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +51 -11
  182. package/corpus/templates/design/app/components/design/inspector/ShaderFillsPanel.tsx +84 -11
  183. package/corpus/templates/design/app/components/design/inspector/index.ts +3 -0
  184. package/corpus/templates/design/app/components/design/keyboard-shortcuts.ts +552 -0
  185. package/corpus/templates/design/app/components/design/multi-screen/board-surface-html.ts +157 -0
  186. package/corpus/templates/design/app/components/design/multi-screen/frame-geometry.ts +53 -0
  187. package/corpus/templates/design/app/components/design/multi-screen/overview-layout.ts +184 -0
  188. package/corpus/templates/design/app/components/design/multi-screen/primitive-drop-target.ts +403 -26
  189. package/corpus/templates/design/app/components/design/multi-screen/types.ts +20 -1
  190. package/corpus/templates/design/app/components/design/types.ts +33 -0
  191. package/corpus/templates/design/app/components/editor/PromptDialog.tsx +59 -7
  192. package/corpus/templates/design/app/components/visual-editor/CanvasCommentPins.tsx +119 -17
  193. package/corpus/templates/design/app/components/visual-editor/DrawOverlay.tsx +275 -63
  194. package/corpus/templates/design/app/hooks/use-question-flow.ts +14 -3
  195. package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +67 -10
  196. package/corpus/templates/design/app/i18n/zh-TW.ts +91 -1
  197. package/corpus/templates/design/app/i18n-data.ts +1217 -10
  198. package/corpus/templates/design/app/i18n-keyboard-shortcuts.ts +577 -0
  199. package/corpus/templates/design/app/lib/agent-chat.ts +23 -0
  200. package/corpus/templates/design/app/lib/design-import.ts +24 -0
  201. package/corpus/templates/design/app/lib/figma-clipboard.ts +157 -0
  202. package/corpus/templates/design/app/lib/figma-svg-copy.ts +221 -0
  203. package/corpus/templates/design/app/pages/design-editor/code-layer-state.ts +143 -30
  204. package/corpus/templates/design/app/pages/design-editor/collab-sync.ts +15 -0
  205. package/corpus/templates/design/app/pages/design-editor/editor-session.ts +33 -0
  206. package/corpus/templates/design/app/pages/design-editor/editor-state.ts +38 -0
  207. package/corpus/templates/design/app/pages/design-editor/overview-annotation-context.ts +157 -0
  208. package/corpus/templates/design/app/pages/design-editor/overview-camera.ts +62 -6
  209. package/corpus/templates/design/app/pages/design-editor/pending-edits.ts +245 -0
  210. package/corpus/templates/design/app/pages/design-editor/react-semantic-handoff.ts +586 -0
  211. package/corpus/templates/design/app/pages/design-editor/screen-auto-layout.ts +169 -0
  212. package/corpus/templates/design/app/pages/design-editor/selection-state.ts +201 -1
  213. package/corpus/templates/design/changelog/2026-07-09-added-underline-strikethrough-and-text-case-uppercase-lowerc.md +6 -0
  214. package/corpus/templates/design/changelog/2026-07-09-annotate-now-draws-across-the-all-screens-canvas-without-swi.md +6 -0
  215. package/corpus/templates/design/changelog/2026-07-09-auto-layout-children-can-now-be-dragged-into-freeform-screen.md +6 -0
  216. package/corpus/templates/design/changelog/2026-07-09-board-shapes-now-stay-visible-and-nest-correctly-across-the-.md +6 -0
  217. package/corpus/templates/design/changelog/2026-07-09-component-source-navigation-now-opens-reliably-from-the-insp.md +6 -0
  218. package/corpus/templates/design/changelog/2026-07-09-cross-screen-local-react-layer-moves-now-hand-off-exact-sour.md +6 -0
  219. package/corpus/templates/design/changelog/2026-07-09-cross-screen-moves-undo-and-redo-now-keep-both-screens-stabl.md +6 -0
  220. package/corpus/templates/design/changelog/2026-07-09-detach-a-component-instance-into-plain-editable-eleme.md +6 -0
  221. package/corpus/templates/design/changelog/2026-07-09-dragging-layers-into-auto-layout-now-respects-ignore-auto-la.md +6 -0
  222. package/corpus/templates/design/changelog/2026-07-09-drawing-annotations-sent-to-the-agent-now-include-a-rendered.md +6 -0
  223. package/corpus/templates/design/changelog/2026-07-09-editor-sidebars-now-use-figma-matched-default-widths-for-a-r.md +6 -0
  224. package/corpus/templates/design/changelog/2026-07-09-empty-frames-and-rectangles-now-expose-full-auto-layout-cont.md +6 -0
  225. package/corpus/templates/design/changelog/2026-07-09-export-any-design-screen-as-a-real-vector-svg-for-figma-with.md +6 -0
  226. package/corpus/templates/design/changelog/2026-07-09-grid-auto-layout-now-uses-true-responsive-rows-and-columns-w.md +6 -0
  227. package/corpus/templates/design/changelog/2026-07-09-hidden-and-locked-screens-now-behave-consistently-between-la.md +6 -0
  228. package/corpus/templates/design/changelog/2026-07-09-image-fills-now-preserve-valid-urls-and-layered-fit-settings.md +6 -0
  229. package/corpus/templates/design/changelog/2026-07-09-import-figma-frames-as-pixel-accurate-design-screens-via-a-s.md +6 -0
  230. package/corpus/templates/design/changelog/2026-07-09-inspect-code-now-hides-internal-styling-metadata-and-wraps-l.md +6 -0
  231. package/corpus/templates/design/changelog/2026-07-09-inspector-blur-and-shadow-effects-now-preserve-other-effects.md +6 -0
  232. package/corpus/templates/design/changelog/2026-07-09-jump-to-a-components-other-instances-from-the-inspecto.md +6 -0
  233. package/corpus/templates/design/changelog/2026-07-09-keyboard-shortcuts-are-now-discoverable-in-a-figma-style-bot.md +6 -0
  234. package/corpus/templates/design/changelog/2026-07-09-layer-selection-now-keeps-cmd-or-ctrl-toggles-and-shift-rang.md +6 -0
  235. package/corpus/templates/design/changelog/2026-07-09-layers-dropped-into-auto-layout-now-participate-correctly-in.md +6 -0
  236. package/corpus/templates/design/changelog/2026-07-09-local-live-edit-screens-now-enter-and-leave-full-view-withou.md +6 -0
  237. package/corpus/templates/design/changelog/2026-07-09-local-preview-urls-now-live-in-the-inspector-instead-of-taki.md +6 -0
  238. package/corpus/templates/design/changelog/2026-07-09-local-react-layers-can-now-be-hidden-and-locked-from-layers-.md +6 -0
  239. package/corpus/templates/design/changelog/2026-07-09-local-react-layers-now-keep-exact-source-provenance-and-supp.md +6 -0
  240. package/corpus/templates/design/changelog/2026-07-09-local-react-visual-editing-now-loads-reliably-and-shows-live.md +6 -0
  241. package/corpus/templates/design/changelog/2026-07-09-paste-from-figma-cmd-v-now-imports-exact-editable-nodes-when.md +6 -0
  242. package/corpus/templates/design/changelog/2026-07-09-pressing-escape-after-selecting-a-screen-now-reveals-page-pr.md +6 -0
  243. package/corpus/templates/design/changelog/2026-07-09-right-click-overlapping-or-nested-objects-to-choose-the-exac.md +6 -0
  244. package/corpus/templates/design/changelog/2026-07-09-scale-edge-stretch-and-center-constraints-now-preserve-layer.md +6 -0
  245. package/corpus/templates/design/changelog/2026-07-09-screen-rows-can-now-be-reordered-in-layers-with-matching-can.md +6 -0
  246. package/corpus/templates/design/changelog/2026-07-09-screen-selection-stays-responsive-in-designs-with-hundreds-o.md +6 -0
  247. package/corpus/templates/design/changelog/2026-07-09-shift-a-now-enables-auto-layout-on-a-selected-screen-with-fl.md +6 -0
  248. package/corpus/templates/design/changelog/2026-07-09-swap-a-component-instance-for-another-from-the-libra.md +6 -0
  249. package/corpus/templates/design/changelog/2026-07-10-drawing-annotations-now-stay-available-to-retry-when-the.md +6 -0
  250. package/corpus/templates/design/e2e/global-setup.ts +3 -6
  251. package/corpus/templates/design/e2e/helpers.ts +27 -6
  252. package/corpus/templates/design/server/lib/design-to-figma-svg.ts +1412 -0
  253. package/corpus/templates/design/server/lib/figma-clipboard-match.ts +196 -0
  254. package/corpus/templates/design/server/lib/figma-node-import.ts +311 -0
  255. package/corpus/templates/design/server/lib/figma-node-to-html.ts +1334 -0
  256. package/corpus/templates/design/server/lib/figma-paste-fallback.ts +64 -0
  257. package/corpus/templates/design/server/lib/playwright-runtime.ts +78 -0
  258. package/corpus/templates/design/server/plugins/db.ts +41 -0
  259. package/corpus/templates/design/server/source-workspace.ts +64 -0
  260. package/corpus/templates/design/shared/board-file.ts +13 -2
  261. package/corpus/templates/design/shared/canvas-math.ts +231 -33
  262. package/corpus/templates/design/shared/code-layer.ts +122 -8
  263. package/corpus/templates/design/shared/component-library.ts +161 -0
  264. package/corpus/templates/design/shared/db-conflict.ts +27 -0
  265. package/corpus/templates/design/shared/design-source-capabilities.ts +2 -1
  266. package/corpus/templates/design/shared/figma-url.ts +149 -0
  267. package/corpus/templates/design/shared/pen-path.ts +16 -7
  268. package/corpus/templates/design/shared/source-mode.ts +1 -0
  269. package/corpus/templates/plan/app/components/plan/CanvasArea.tsx +7 -19
  270. package/corpus/templates/plan/changelog/2026-07-09-canvas-panning-now-keeps-zoom-fixed-even-at-the-edges.md +6 -0
  271. package/corpus/templates/slides/actions/index-design-system-with-builder.ts +16 -1
  272. package/corpus/templates/slides/app/context/DeckContext.tsx +311 -70
  273. package/corpus/templates/slides/changelog/2026-07-09-fixed-the-slide-rail-going-permanently-stale-during-a-long-a.md +6 -0
  274. package/dist/agent/production-agent.d.ts +82 -7
  275. package/dist/agent/production-agent.d.ts.map +1 -1
  276. package/dist/agent/production-agent.js +180 -17
  277. package/dist/agent/production-agent.js.map +1 -1
  278. package/dist/agent/run-manager.d.ts.map +1 -1
  279. package/dist/agent/run-manager.js +63 -14
  280. package/dist/agent/run-manager.js.map +1 -1
  281. package/dist/agent/run-store.d.ts +46 -0
  282. package/dist/agent/run-store.d.ts.map +1 -1
  283. package/dist/agent/run-store.js +129 -22
  284. package/dist/agent/run-store.js.map +1 -1
  285. package/dist/cli/design-connect.d.ts +6 -0
  286. package/dist/cli/design-connect.d.ts.map +1 -1
  287. package/dist/cli/design-connect.js +336 -139
  288. package/dist/cli/design-connect.js.map +1 -1
  289. package/dist/cli/skills.d.ts.map +1 -1
  290. package/dist/cli/skills.js +14 -0
  291. package/dist/cli/skills.js.map +1 -1
  292. package/dist/client/AssistantChat.d.ts +2 -0
  293. package/dist/client/AssistantChat.d.ts.map +1 -1
  294. package/dist/client/AssistantChat.js +19 -3
  295. package/dist/client/AssistantChat.js.map +1 -1
  296. package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
  297. package/dist/client/MultiTabAssistantChat.js +38 -18
  298. package/dist/client/MultiTabAssistantChat.js.map +1 -1
  299. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  300. package/dist/client/agent-chat-adapter.js +48 -12
  301. package/dist/client/agent-chat-adapter.js.map +1 -1
  302. package/dist/client/agent-chat.d.ts +60 -0
  303. package/dist/client/agent-chat.d.ts.map +1 -1
  304. package/dist/client/agent-chat.js +122 -1
  305. package/dist/client/agent-chat.js.map +1 -1
  306. package/dist/client/chat/index.d.ts +1 -1
  307. package/dist/client/chat/index.d.ts.map +1 -1
  308. package/dist/client/chat/index.js +1 -1
  309. package/dist/client/chat/index.js.map +1 -1
  310. package/dist/client/extensions/EmbeddedExtension.d.ts +12 -0
  311. package/dist/client/extensions/EmbeddedExtension.d.ts.map +1 -1
  312. package/dist/client/extensions/EmbeddedExtension.js +36 -2
  313. package/dist/client/extensions/EmbeddedExtension.js.map +1 -1
  314. package/dist/client/index.d.ts +1 -1
  315. package/dist/client/index.d.ts.map +1 -1
  316. package/dist/client/index.js +1 -1
  317. package/dist/client/index.js.map +1 -1
  318. package/dist/client/rich-markdown-editor/useCollabReconcile.d.ts.map +1 -1
  319. package/dist/client/rich-markdown-editor/useCollabReconcile.js +18 -6
  320. package/dist/client/rich-markdown-editor/useCollabReconcile.js.map +1 -1
  321. package/dist/collab/awareness-store.d.ts +5 -0
  322. package/dist/collab/awareness-store.d.ts.map +1 -1
  323. package/dist/collab/awareness-store.js +19 -12
  324. package/dist/collab/awareness-store.js.map +1 -1
  325. package/dist/collab/awareness.d.ts +2 -2
  326. package/dist/collab/awareness.d.ts.map +1 -1
  327. package/dist/collab/client.d.ts.map +1 -1
  328. package/dist/collab/client.js +13 -5
  329. package/dist/collab/client.js.map +1 -1
  330. package/dist/collab/index.d.ts +1 -0
  331. package/dist/collab/index.d.ts.map +1 -1
  332. package/dist/collab/index.js +1 -0
  333. package/dist/collab/index.js.map +1 -1
  334. package/dist/collab/recent-edits.d.ts +3 -1
  335. package/dist/collab/recent-edits.d.ts.map +1 -1
  336. package/dist/collab/recent-edits.js +57 -2
  337. package/dist/collab/recent-edits.js.map +1 -1
  338. package/dist/collab/struct-routes.d.ts +1 -1
  339. package/dist/index.browser.d.ts +1 -1
  340. package/dist/index.browser.d.ts.map +1 -1
  341. package/dist/index.browser.js +1 -1
  342. package/dist/index.browser.js.map +1 -1
  343. package/dist/observability/routes.d.ts +5 -5
  344. package/dist/progress/routes.d.ts +1 -1
  345. package/dist/resources/handlers.d.ts +3 -3
  346. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  347. package/dist/server/agent-chat-plugin.js +87 -10
  348. package/dist/server/agent-chat-plugin.js.map +1 -1
  349. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  350. package/dist/server/builder-design-systems.d.ts +15 -0
  351. package/dist/server/builder-design-systems.d.ts.map +1 -1
  352. package/dist/server/builder-design-systems.js +9 -4
  353. package/dist/server/builder-design-systems.js.map +1 -1
  354. package/package.json +1 -1
@@ -57,23 +57,59 @@ function jsonValuesEqual(left: unknown, right: unknown): boolean {
57
57
  return JSON.stringify(left) === JSON.stringify(right);
58
58
  }
59
59
 
60
+ // Same-process mutex guarding the generation-session read-modify-write below.
61
+ // generate-screens' own tool description explicitly recommends fanning out
62
+ // parallel generate-design calls per returned frame ("fan out calls to
63
+ // generate-design for each returned frame"). Without this lock, two
64
+ // concurrent calls for the same designId both read the same pre-update
65
+ // session, each only marks its own frame done, and whichever writeAppState
66
+ // lands second silently discards the first call's frame-done update —
67
+ // application state has no CAS/versioning primitive (unlike designs.data's
68
+ // mutateDesignData), so a plain read-then-write here is a classic lost-update
69
+ // race. This mirrors the same-process serialization
70
+ // server/lib/design-data-mutation.ts uses (withDesignDataLock) for the
71
+ // designs.data column. Cross-process races remain (no CAS primitive exists
72
+ // for application state), but same-process fan-out from one agent turn is
73
+ // the realistic, explicitly-encouraged case this closes.
74
+ const generationSessionLocks = new Map<string, Promise<unknown>>();
75
+
76
+ function withGenerationSessionLock<T>(
77
+ designId: string,
78
+ callback: () => Promise<T>,
79
+ ): Promise<T> {
80
+ const previous = generationSessionLocks.get(designId) ?? Promise.resolve();
81
+ const next = previous.then(callback, callback);
82
+ generationSessionLocks.set(designId, next);
83
+ const cleanup = () => {
84
+ if (generationSessionLocks.get(designId) === next) {
85
+ generationSessionLocks.delete(designId);
86
+ }
87
+ };
88
+ next.then(cleanup, cleanup);
89
+ return next;
90
+ }
91
+
60
92
  async function updateGenerationSessionForSavedFiles(
61
93
  designId: string,
62
94
  savedFilenames: string[],
63
95
  ) {
64
- const key = designGenerationSessionKey(designId);
65
- const rawSession = await readAppState(key).catch(() => null);
66
- if (!rawSession || typeof rawSession !== "object") return;
67
- const session = rawSession as unknown as DesignGenerationSession;
68
- if (session.designId !== designId || !Array.isArray(session.frames)) return;
69
-
70
- const nextSession = updateGenerationSessionWithSavedFiles(
71
- session,
72
- savedFilenames,
73
- );
74
- if (nextSession === session) return;
96
+ await withGenerationSessionLock(designId, async () => {
97
+ const key = designGenerationSessionKey(designId);
98
+ const rawSession = await readAppState(key).catch(() => null);
99
+ if (!rawSession || typeof rawSession !== "object") return;
100
+ const session = rawSession as unknown as DesignGenerationSession;
101
+ if (session.designId !== designId || !Array.isArray(session.frames)) {
102
+ return;
103
+ }
104
+
105
+ const nextSession = updateGenerationSessionWithSavedFiles(
106
+ session,
107
+ savedFilenames,
108
+ );
109
+ if (nextSession === session) return;
75
110
 
76
- await writeAppState(key, nextSession as unknown as Record<string, unknown>);
111
+ await writeAppState(key, nextSession as unknown as Record<string, unknown>);
112
+ });
77
113
  }
78
114
 
79
115
  const generateDesignAgentParameters = {
@@ -228,6 +264,37 @@ const generateDesignAction = defineAction({
228
264
  message: "canvasFrames entries require fileId or filename",
229
265
  }),
230
266
  )
267
+ // Reject two placements for the same target in one call. Without
268
+ // this, mergeCanvasFramePlacements folds both entries into a single
269
+ // canvasFrames[fileId] value (last one wins), but the mutateDesignData
270
+ // isApplied check below verifies EVERY placedFrames entry against
271
+ // that single folded value — so the earlier, now-overwritten entry
272
+ // always fails the equality check. Since the mutate callback is
273
+ // deterministic, every retry recomputes the identical mismatch, so
274
+ // the whole action always fails with a "concurrent write conflicts"
275
+ // error after burning through all retries, even though nothing else
276
+ // was actually writing to the design.
277
+ .superRefine((frames, ctx) => {
278
+ const seen = new Map<string, number>();
279
+ frames.forEach((frame, index) => {
280
+ const key = frame.fileId
281
+ ? `id:${frame.fileId}`
282
+ : `name:${frame.filename}`;
283
+ const firstIndex = seen.get(key);
284
+ if (firstIndex !== undefined) {
285
+ ctx.addIssue({
286
+ code: z.ZodIssueCode.custom,
287
+ path: [index],
288
+ message:
289
+ `canvasFrames entry ${index} duplicates the target already placed ` +
290
+ `at index ${firstIndex} (${frame.fileId ? `fileId ${frame.fileId}` : `filename ${frame.filename}`}). ` +
291
+ "Pass exactly one placement per screen.",
292
+ });
293
+ return;
294
+ }
295
+ seen.set(key, index);
296
+ });
297
+ })
231
298
  .optional(),
232
299
  )
233
300
  .optional()
@@ -2,10 +2,11 @@ import { defineAction, embedApp } from "@agent-native/core";
2
2
  import { writeAppState } from "@agent-native/core/application-state";
3
3
  import { buildDeepLink } from "@agent-native/core/server";
4
4
  import { assertAccess } from "@agent-native/core/sharing";
5
+ import { eq } from "drizzle-orm";
5
6
  import { nanoid } from "nanoid";
6
7
  import { z } from "zod";
7
8
 
8
- import "../server/db/index.js";
9
+ import { getDb, schema } from "../server/db/index.js";
9
10
  import {
10
11
  type AssignedCanvasRegion,
11
12
  DEFAULT_ASSIGNED_REGION_GAP,
@@ -186,6 +187,28 @@ export default defineAction({
186
187
  .min(1)
187
188
  .max(8)
188
189
  .superRefine((screens, ctx) => {
190
+ // Two screens sharing an explicit frameId would produce two
191
+ // DesignGenerationFrame entries with the same frameId (and thus
192
+ // the same derived agentId `agent-${frameId}`) once frames get
193
+ // built below, colliding agent presence/status tracking for both
194
+ // screens on the overview canvas.
195
+ const frameIdsSeen = new Map<string, number>();
196
+ screens.forEach((screen, index) => {
197
+ if (!screen.frameId) return;
198
+ const firstIndex = frameIdsSeen.get(screen.frameId);
199
+ if (firstIndex !== undefined) {
200
+ ctx.addIssue({
201
+ code: z.ZodIssueCode.custom,
202
+ path: [index, "frameId"],
203
+ message:
204
+ `duplicate frameId "${screen.frameId}" (already used at index ${firstIndex}); ` +
205
+ "each screen needs a distinct frameId",
206
+ });
207
+ return;
208
+ }
209
+ frameIdsSeen.set(screen.frameId, index);
210
+ });
211
+
189
212
  const requestAnchors = new Map<
190
213
  string,
191
214
  { index: number; role: DesignGenerationFrame["role"] }
@@ -259,21 +282,43 @@ export default defineAction({
259
282
  : DEFAULT_ASSIGNED_REGION_MAX_COLUMNS,
260
283
  });
261
284
 
262
- const seenFilenames = new Map<string, number>();
263
- // Dedupe any filename (explicit or auto-generated) so two screens can never
264
- // resolve to the same target file otherwise generate-design silently
265
- // overwrites the first screen's output with the second's.
285
+ // Seed the used-filename set with the design's EXISTING files, not just
286
+ // names requested in this call. Without this, a requested/auto-slugged
287
+ // target (e.g. a screen titled "Onboarding" slugging to "onboarding.html")
288
+ // can silently collide with an already-saved screen: generate-design's
289
+ // existing-file lookup is keyed by filename, so the later generate-design
290
+ // call for that "new" target would UPDATE (overwrite) the pre-existing
291
+ // file instead of creating the new screen the agent intended.
292
+ const db = getDb();
293
+ const existingFiles = await db
294
+ .select({ filename: schema.designFiles.filename })
295
+ .from(schema.designFiles)
296
+ .where(eq(schema.designFiles.designId, designId));
297
+ const usedFilenames = new Set(
298
+ existingFiles
299
+ .map((file) => file.filename)
300
+ .filter((filename): filename is string => Boolean(filename)),
301
+ );
302
+ // Dedupe any filename (explicit or auto-generated) so two screens can
303
+ // never resolve to the same target file, and so no target collides with
304
+ // an existing screen — otherwise generate-design silently overwrites the
305
+ // other screen's content.
266
306
  const dedupeFilename = (base: string): string => {
267
- if (!seenFilenames.has(base)) {
268
- seenFilenames.set(base, 1);
307
+ if (!usedFilenames.has(base)) {
308
+ usedFilenames.add(base);
269
309
  return base;
270
310
  }
271
- const count = seenFilenames.get(base)! + 1;
272
- seenFilenames.set(base, count);
273
311
  const dot = base.lastIndexOf(".");
274
- return dot > 0
275
- ? `${base.slice(0, dot)}-${count}${base.slice(dot)}`
276
- : `${base}-${count}`;
312
+ const stem = dot > 0 ? base.slice(0, dot) : base;
313
+ const ext = dot > 0 ? base.slice(dot) : "";
314
+ let count = 2;
315
+ let candidate = `${stem}-${count}${ext}`;
316
+ while (usedFilenames.has(candidate)) {
317
+ count += 1;
318
+ candidate = `${stem}-${count}${ext}`;
319
+ }
320
+ usedFilenames.add(candidate);
321
+ return candidate;
277
322
  };
278
323
  const requestedTargets = screens.map((screen, index) => {
279
324
  if (screen.filename) return dedupeFilename(screen.filename);
@@ -0,0 +1,115 @@
1
+ import { defineAction } from "@agent-native/core";
2
+ import { z } from "zod";
3
+
4
+ import { executeProviderApiRequest } from "../server/lib/provider-api.js";
5
+ import { parseFigmaFileKey } from "../shared/figma-url.js";
6
+
7
+ const schemaInput = z
8
+ .object({
9
+ fileUrl: z
10
+ .string()
11
+ .trim()
12
+ .optional()
13
+ .describe(
14
+ "Figma file URL, for example https://www.figma.com/design/<fileKey>/...",
15
+ ),
16
+ fileKey: z
17
+ .string()
18
+ .trim()
19
+ .optional()
20
+ .describe("Figma file key. Used when fileUrl is omitted."),
21
+ })
22
+ .refine((value) => value.fileUrl || value.fileKey, {
23
+ message: "Pass fileUrl or fileKey.",
24
+ path: ["fileUrl"],
25
+ });
26
+
27
+ type FigmaProviderEnvelope = {
28
+ response?: {
29
+ ok?: boolean;
30
+ status?: number;
31
+ statusText?: string;
32
+ json?: unknown;
33
+ text?: string;
34
+ };
35
+ };
36
+
37
+ type FigmaStyleRecord = {
38
+ key?: unknown;
39
+ node_id?: unknown;
40
+ style_type?: unknown;
41
+ name?: unknown;
42
+ description?: unknown;
43
+ };
44
+
45
+ function stringValue(value: unknown): string | null {
46
+ return typeof value === "string" && value.trim() ? value.trim() : null;
47
+ }
48
+
49
+ function providerJson(envelope: unknown, label: string): unknown {
50
+ const response = (envelope as FigmaProviderEnvelope | null)?.response;
51
+ if (!response) throw new Error(`Figma ${label} response was empty.`);
52
+ if (response.ok === false) {
53
+ const detail =
54
+ stringValue(response.text) ||
55
+ response.statusText ||
56
+ `HTTP ${response.status ?? "error"}`;
57
+ throw new Error(`Figma ${label} request failed: ${detail}`);
58
+ }
59
+ return response.json;
60
+ }
61
+
62
+ export default defineAction({
63
+ description:
64
+ "Summarize a Figma file's published styles (fill/text/effect/grid) by name, description, and node id. This is the file's Styles panel, NOT the Enterprise Variables API — full design-token extraction (including variables/collections) routes through index-design-system-with-builder or import-file --format fig. Requires the saved FIGMA_ACCESS_TOKEN secret.",
65
+ schema: schemaInput,
66
+ http: { method: "GET" },
67
+ readOnly: true,
68
+ publicAgent: { expose: true, readOnly: true, requiresAuth: true },
69
+ run: async (args) => {
70
+ const fileKey =
71
+ parseFigmaFileKey(args.fileKey) ?? parseFigmaFileKey(args.fileUrl);
72
+ if (!fileKey) {
73
+ throw new Error("Could not find a Figma file key in the provided URL.");
74
+ }
75
+
76
+ const envelope = await executeProviderApiRequest({
77
+ provider: "figma",
78
+ method: "GET",
79
+ path: `/files/${fileKey}/styles`,
80
+ maxBytes: 2 * 1024 * 1024,
81
+ });
82
+ const json = providerJson(envelope, "styles") as {
83
+ meta?: { styles?: unknown };
84
+ };
85
+ const records = Array.isArray(json.meta?.styles)
86
+ ? (json.meta!.styles as FigmaStyleRecord[])
87
+ : [];
88
+
89
+ const styles = records
90
+ .filter((record) => record && typeof record === "object")
91
+ .map((record) => ({
92
+ key: stringValue(record.key),
93
+ nodeId: stringValue(record.node_id),
94
+ styleType: stringValue(record.style_type),
95
+ name: stringValue(record.name) ?? "Untitled style",
96
+ description: stringValue(record.description),
97
+ }));
98
+
99
+ const byType = styles.reduce<Record<string, number>>((acc, style) => {
100
+ const type = style.styleType ?? "UNKNOWN";
101
+ acc[type] = (acc[type] ?? 0) + 1;
102
+ return acc;
103
+ }, {});
104
+
105
+ return {
106
+ source: "figma",
107
+ fileKey,
108
+ total: styles.length,
109
+ byType,
110
+ styles,
111
+ guidance:
112
+ "These are the file's published FILL/TEXT/EFFECT/GRID styles (name, description, node id), not the Enterprise Variables API. For full design-system token extraction (variables, collections, modes) use index-design-system-with-builder or import-file --format fig instead of hand-mapping these style records.",
113
+ };
114
+ },
115
+ });
@@ -0,0 +1,254 @@
1
+ /**
2
+ * go-to-main-component — Figma's "Go to main component".
3
+ *
4
+ * DESIGN NOTE — mapping onto this codebase's data model: there is no
5
+ * persisted "main component" concept anywhere here. A component is just a
6
+ * name (`data-agent-native-component="Name"`) that happens to be stamped on
7
+ * more than one element; every instance is an independently-duplicated copy
8
+ * of HTML (see `shared/component-model.ts`, `component_index` schema — it
9
+ * stores props/variants/runtime-selectors metadata, never a canonical
10
+ * definition). A true "main component" would require a data-model addition
11
+ * (e.g. an `isMain`/definition pointer on `component_index`) — out of scope
12
+ * here; flagged for a follow-up if promoting a specific instance as
13
+ * authoritative becomes a real need.
14
+ *
15
+ * Closest tractable equivalent: treat the EARLIEST instance of the same
16
+ * component name — scanning every design file in `createdAt` order, document
17
+ * order within each file — as the "main" analogue, mirroring the common case
18
+ * where a component's original occurrence predates its copies. If the given
19
+ * instance already IS that earliest one, this returns `isMain: true` instead
20
+ * of navigating (nothing to jump to). Otherwise it writes a `navigate`
21
+ * app-state command (same mechanism as `navigate` / `open-component-source`)
22
+ * so the editor selects the target instance, including switching design
23
+ * files when the main instance lives on a different screen.
24
+ *
25
+ * Navigation-only + inline/Alpine designs only (real-app sources return a
26
+ * CTA). Because this writes the transient `navigate` application-state
27
+ * command, it is exposed as the default POST mutation rather than a GET.
28
+ */
29
+
30
+ import { defineAction } from "@agent-native/core/action";
31
+ import { writeAppState } from "@agent-native/core/application-state";
32
+ import { getText, hasCollabState } from "@agent-native/core/collab";
33
+ import { accessFilter, resolveAccess } from "@agent-native/core/sharing";
34
+ import { and, eq } from "drizzle-orm";
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 { buildCodeLayerProjection } from "../shared/code-layer.js";
40
+ import type { CodeLayerSource } from "../shared/code-layer.js";
41
+ import {
42
+ entriesForComponent,
43
+ scanComponentLibrary,
44
+ } from "../shared/component-library.js";
45
+ import {
46
+ componentNameFor,
47
+ componentNodeIdMatches,
48
+ } from "../shared/component-model.js";
49
+ import { designSourceTypeFromData } from "../shared/source-mode.js";
50
+
51
+ async function liveContent(
52
+ fileId: string,
53
+ storedContent: string,
54
+ ): Promise<string> {
55
+ try {
56
+ if (await hasCollabState(fileId)) {
57
+ const live = await getText(fileId, "content");
58
+ if (typeof live === "string") return live;
59
+ }
60
+ } catch {
61
+ // Collab reads are best-effort; SQL content is the fallback.
62
+ }
63
+ return storedContent;
64
+ }
65
+
66
+ export default defineAction({
67
+ description:
68
+ "Resolve the 'main' instance of a component (Figma's Go to main " +
69
+ "component). This codebase has no separate component-definition markup " +
70
+ "— components are structurally duplicated HTML matched by name — so the " +
71
+ "earliest instance found across the design's files stands in for the " +
72
+ "main component. Returns isMain=true when the given instance already IS " +
73
+ "that earliest one; otherwise navigates the editor to it (which may " +
74
+ "switch design files).",
75
+ schema: z.object({
76
+ designId: z.string().describe("Design project ID"),
77
+ nodeId: z
78
+ .string()
79
+ .describe("data-agent-native-node-id of the component instance root"),
80
+ fileId: z
81
+ .string()
82
+ .optional()
83
+ .describe(
84
+ "Design file id the instance currently lives in; defaults to index.html",
85
+ ),
86
+ }),
87
+ run: async ({ designId, nodeId, fileId }) => {
88
+ const access = await resolveAccess("design", designId);
89
+ if (!access) throw new Error("Design not found");
90
+
91
+ const rawData = (access.resource as { data?: unknown }).data;
92
+ const sourceType = designSourceTypeFromData(rawData);
93
+
94
+ if (sourceType !== "inline") {
95
+ return {
96
+ designId,
97
+ nodeId,
98
+ sourceType,
99
+ ctaRequired: true,
100
+ ctaMessage:
101
+ "Go to main component requires a connected Builder app for " +
102
+ "real-app sources. Not yet available.",
103
+ isMain: false,
104
+ navigated: false,
105
+ };
106
+ }
107
+
108
+ const db = getDb();
109
+
110
+ // ── Resolve the current node + component name ──────────────────────────
111
+ const conditions = [
112
+ accessFilter(schema.designs, schema.designShares),
113
+ eq(schema.designFiles.designId, designId),
114
+ fileId
115
+ ? eq(schema.designFiles.id, fileId)
116
+ : eq(schema.designFiles.filename, "index.html"),
117
+ ];
118
+
119
+ const [file] = await db
120
+ .select({
121
+ id: schema.designFiles.id,
122
+ designId: schema.designFiles.designId,
123
+ filename: schema.designFiles.filename,
124
+ content: schema.designFiles.content,
125
+ })
126
+ .from(schema.designFiles)
127
+ .innerJoin(
128
+ schema.designs,
129
+ eq(schema.designFiles.designId, schema.designs.id),
130
+ )
131
+ .where(and(...conditions))
132
+ .limit(1);
133
+
134
+ if (!file) throw new Error("Design HTML file not found.");
135
+
136
+ const currentHtml = await liveContent(file.id, file.content ?? "");
137
+ const codeLayerSource: CodeLayerSource = {
138
+ kind: "design-file",
139
+ designId: file.designId,
140
+ fileId: file.id,
141
+ filename: file.filename,
142
+ };
143
+ const currentProjection = buildCodeLayerProjection(currentHtml, {
144
+ source: codeLayerSource,
145
+ });
146
+
147
+ const node = currentProjection.nodes.find((n) =>
148
+ componentNodeIdMatches(n, nodeId),
149
+ );
150
+ if (!node) {
151
+ throw new Error(
152
+ `Node "${nodeId}" not found. Run get-code-layer-projection to list current ids.`,
153
+ );
154
+ }
155
+
156
+ const componentName = componentNameFor(node);
157
+ if (!componentName) {
158
+ throw new Error(
159
+ `Node "${nodeId}" is not a component root (no data-agent-native-component attribute).`,
160
+ );
161
+ }
162
+
163
+ // ── Scan every design file for instances of this component ─────────────
164
+ const allFiles = await db
165
+ .select({
166
+ id: schema.designFiles.id,
167
+ designId: schema.designFiles.designId,
168
+ filename: schema.designFiles.filename,
169
+ content: schema.designFiles.content,
170
+ })
171
+ .from(schema.designFiles)
172
+ .innerJoin(
173
+ schema.designs,
174
+ eq(schema.designFiles.designId, schema.designs.id),
175
+ )
176
+ .where(
177
+ and(
178
+ accessFilter(schema.designs, schema.designShares),
179
+ eq(schema.designFiles.designId, designId),
180
+ eq(schema.designFiles.fileType, "html"),
181
+ ),
182
+ )
183
+ .orderBy(schema.designFiles.createdAt);
184
+
185
+ // Use the freshest content we already read for the current file so the
186
+ // instance we just resolved (possibly from a live collab doc) matches up
187
+ // with `node.id` exactly.
188
+ const filesForScan = allFiles.map((row) =>
189
+ row.id === file.id ? { ...row, content: currentHtml } : row,
190
+ );
191
+
192
+ const entries = scanComponentLibrary(filesForScan);
193
+ const matches = entriesForComponent(entries, componentName);
194
+
195
+ if (matches.length === 0) {
196
+ // Shouldn't happen (the current node itself matches), but guard anyway.
197
+ throw new Error(
198
+ `No instances of component "${componentName}" found across the design's files.`,
199
+ );
200
+ }
201
+
202
+ const main = matches[0];
203
+ // Compare against the caller's own stable `nodeId` param (a durable
204
+ // data-agent-native-node-id), not `node.id` (an ephemeral id scoped to
205
+ // this projection call) — `scanComponentLibrary` resolves the same
206
+ // durable id for every entry, so this is an apples-to-apples comparison.
207
+ const isMain = main.fileId === file.id && main.nodeId === nodeId;
208
+
209
+ if (isMain) {
210
+ return {
211
+ designId,
212
+ nodeId,
213
+ componentName,
214
+ sourceType,
215
+ ctaRequired: false,
216
+ isMain: true,
217
+ instanceCount: matches.length,
218
+ navigated: false,
219
+ note:
220
+ matches.length === 1
221
+ ? `"${componentName}" has only one instance — this is it.`
222
+ : `This is the earliest instance of "${componentName}" across the design.`,
223
+ };
224
+ }
225
+
226
+ await writeAppState("navigate", {
227
+ view: "editor",
228
+ designId,
229
+ editorView: "single",
230
+ fileId: main.fileId,
231
+ filename: main.filename,
232
+ selectedNodeId: main.nodeId,
233
+ inspectorTab: "design",
234
+ inspectorSection: "component",
235
+ });
236
+
237
+ return {
238
+ designId,
239
+ nodeId,
240
+ componentName,
241
+ sourceType,
242
+ ctaRequired: false,
243
+ isMain: false,
244
+ instanceCount: matches.length,
245
+ main: {
246
+ fileId: main.fileId,
247
+ filename: main.filename,
248
+ nodeId: main.nodeId,
249
+ selector: main.selector,
250
+ },
251
+ navigated: true,
252
+ };
253
+ },
254
+ });
@@ -19,7 +19,7 @@ export default defineAction({
19
19
  "for a specific design + localhost connection. The grant scopes writes to the " +
20
20
  "connection's rootPath and expires after 8 hours. Requires editor access on the design. " +
21
21
  "The LocalhostWriteConsentDialog calls this after the user clicks 'Allow writes'.",
22
- // This action mints/returns the real bridgeToken that unlocks the local
22
+ // This action persists the real bridgeToken that unlocks the local
23
23
  // bridge's unrestricted /read-file, /write-file, and /apply-edit. It must
24
24
  // only ever be triggered by a human clicking "Allow writes" in
25
25
  // LocalhostWriteConsentDialog — never by the agent itself, or an agent
@@ -27,7 +27,9 @@ export default defineAction({
27
27
  // human consent model entirely. `agentTool: false` hides it from every
28
28
  // agent tool surface (in-app assistant, MCP, A2A) while keeping it
29
29
  // callable from the frontend via `callAction` / `useActionMutation` and
30
- // the raw `/_agent-native/actions/grant-localhost-write-consent` route.
30
+ // the raw `/_agent-native/actions/grant-localhost-write-consent` route. The
31
+ // token intentionally stays server-side: browser callers only need grant
32
+ // metadata because write-local-file adds bridge authentication itself.
31
33
  agentTool: false,
32
34
  schema: z.object({
33
35
  designId: z.string().describe("Design ID."),
@@ -124,7 +126,6 @@ export default defineAction({
124
126
 
125
127
  return {
126
128
  grantId: existing?.id ?? grantId,
127
- bridgeToken,
128
129
  rootPath,
129
130
  grantedUntil,
130
131
  };
@@ -2,12 +2,12 @@ import { defineAction } from "@agent-native/core";
2
2
  import { assertAccess } from "@agent-native/core/sharing";
3
3
  import { z } from "zod";
4
4
 
5
+ import { saveFigmaPasteHtmlFallback } from "../server/lib/figma-paste-fallback.js";
5
6
  import {
6
7
  normalizeImportedHtmlDocument,
7
8
  resolveImportDesignId,
8
9
  saveImportedDesignFiles,
9
10
  } from "../server/lib/import-design-files.js";
10
- import { parseVisibleClipboardHtml } from "../server/lib/visible-clipboard-html.js";
11
11
 
12
12
  const MAX_HTML_IMPORT_BYTES = 2 * 1024 * 1024;
13
13
 
@@ -62,38 +62,11 @@ export default defineAction({
62
62
  };
63
63
  }
64
64
 
65
- const parsed = parseVisibleClipboardHtml(content);
66
- if (!parsed.fallbackHtml) {
67
- throw new Error(
68
- "No visible HTML was found in the clipboard. Copy a frame, then paste into the canvas.",
69
- );
70
- }
71
- const saved = await saveImportedDesignFiles({
65
+ return saveFigmaPasteHtmlFallback({
72
66
  designId: resolvedDesignId,
73
- sourceType: "figma-paste-html",
74
- files: [
75
- {
76
- filename: baseFilename(originalName, "figma-paste"),
77
- fileType: "html",
78
- content: normalizeImportedHtmlDocument(
79
- parsed.fallbackHtml,
80
- "visible clipboard HTML",
81
- ),
82
- source: {
83
- sourceType: "figma-paste-html",
84
- },
85
- },
86
- ],
67
+ clipboardHtml: content,
68
+ originalName,
87
69
  });
88
- return {
89
- ...saved,
90
- stats: {
91
- sourceKind: "figma-paste",
92
- format: "html",
93
- frameCount: saved.files.length,
94
- imageCount: 0,
95
- },
96
- };
97
70
  },
98
71
  link: ({ result }) => {
99
72
  if (!result || typeof result !== "object") return null;