@agent-native/core 0.92.7 → 0.92.9

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 (295) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +18 -0
  3. package/corpus/core/docs/content/a2a-protocol.mdx +12 -0
  4. package/corpus/core/docs/content/locales/ar-SA/a2a-protocol.mdx +12 -0
  5. package/corpus/core/docs/content/locales/ar-SA/pr-visual-recap.mdx +3 -3
  6. package/corpus/core/docs/content/locales/ar-SA/pure-agent-apps.mdx +4 -0
  7. package/corpus/core/docs/content/locales/de-DE/a2a-protocol.mdx +12 -0
  8. package/corpus/core/docs/content/locales/de-DE/pr-visual-recap.mdx +3 -3
  9. package/corpus/core/docs/content/locales/de-DE/pure-agent-apps.mdx +4 -0
  10. package/corpus/core/docs/content/locales/es-ES/a2a-protocol.mdx +12 -0
  11. package/corpus/core/docs/content/locales/es-ES/pr-visual-recap.mdx +3 -3
  12. package/corpus/core/docs/content/locales/es-ES/pure-agent-apps.mdx +4 -0
  13. package/corpus/core/docs/content/locales/fr-FR/a2a-protocol.mdx +12 -0
  14. package/corpus/core/docs/content/locales/fr-FR/pr-visual-recap.mdx +3 -3
  15. package/corpus/core/docs/content/locales/fr-FR/pure-agent-apps.mdx +4 -0
  16. package/corpus/core/docs/content/locales/hi-IN/a2a-protocol.mdx +12 -0
  17. package/corpus/core/docs/content/locales/hi-IN/pr-visual-recap.mdx +3 -3
  18. package/corpus/core/docs/content/locales/hi-IN/pure-agent-apps.mdx +4 -0
  19. package/corpus/core/docs/content/locales/ja-JP/a2a-protocol.mdx +12 -0
  20. package/corpus/core/docs/content/locales/ja-JP/pr-visual-recap.mdx +3 -3
  21. package/corpus/core/docs/content/locales/ja-JP/pure-agent-apps.mdx +4 -0
  22. package/corpus/core/docs/content/locales/ko-KR/a2a-protocol.mdx +12 -0
  23. package/corpus/core/docs/content/locales/ko-KR/pr-visual-recap.mdx +3 -3
  24. package/corpus/core/docs/content/locales/ko-KR/pure-agent-apps.mdx +4 -0
  25. package/corpus/core/docs/content/locales/pt-BR/a2a-protocol.mdx +12 -0
  26. package/corpus/core/docs/content/locales/pt-BR/pr-visual-recap.mdx +3 -3
  27. package/corpus/core/docs/content/locales/pt-BR/pure-agent-apps.mdx +4 -0
  28. package/corpus/core/docs/content/locales/zh-CN/a2a-protocol.mdx +12 -0
  29. package/corpus/core/docs/content/locales/zh-CN/pr-visual-recap.mdx +3 -3
  30. package/corpus/core/docs/content/locales/zh-CN/pure-agent-apps.mdx +4 -0
  31. package/corpus/core/docs/content/locales/zh-TW/a2a-protocol.mdx +12 -0
  32. package/corpus/core/docs/content/locales/zh-TW/pr-visual-recap.mdx +3 -3
  33. package/corpus/core/docs/content/locales/zh-TW/pure-agent-apps.mdx +4 -0
  34. package/corpus/core/docs/content/pr-visual-recap.mdx +3 -3
  35. package/corpus/core/docs/content/pure-agent-apps.mdx +22 -0
  36. package/corpus/core/package.json +1 -1
  37. package/corpus/core/src/agent/engine/builtin.ts +1 -1
  38. package/corpus/core/src/agent/model-config.ts +30 -30
  39. package/corpus/core/src/agent/production-agent.ts +129 -7
  40. package/corpus/core/src/cli/design-connect.ts +438 -83
  41. package/corpus/core/src/cli/recap.ts +17 -3
  42. package/corpus/core/src/client/AgentPanel.tsx +8 -1
  43. package/corpus/core/src/client/agent-chat-adapter.ts +12 -7
  44. package/corpus/core/src/client/composer/TiptapComposer.tsx +1 -1
  45. package/corpus/core/src/client/index.ts +4 -0
  46. package/corpus/core/src/client/org/TeamPage.tsx +41 -26
  47. package/corpus/core/src/client/use-action.ts +113 -1
  48. package/corpus/core/src/db/client.ts +12 -2
  49. package/corpus/core/src/db/create-get-db.ts +4 -1
  50. package/corpus/core/src/scripts/agent-engines/manage-agent-engine.ts +1 -1
  51. package/corpus/core/src/scripts/agent-engines/set-agent-engine.ts +1 -1
  52. package/corpus/core/src/server/ssr-handler.ts +7 -0
  53. package/corpus/core/src/server/transcribe-voice.ts +1 -1
  54. package/corpus/core/src/usage/store.ts +13 -4
  55. package/corpus/core/src/vite/client.ts +38 -1
  56. package/corpus/templates/analytics/AGENTS.md +3 -0
  57. package/corpus/templates/analytics/actions/get-data-program.ts +78 -0
  58. package/corpus/templates/analytics/actions/list-data-programs.ts +49 -0
  59. package/corpus/templates/analytics/app/global.css +11 -0
  60. package/corpus/templates/analytics/app/hooks/use-dashboard-chat-context.ts +163 -17
  61. package/corpus/templates/analytics/app/i18n/zh-TW.ts +2 -0
  62. package/corpus/templates/analytics/app/i18n-data.ts +20 -0
  63. package/corpus/templates/analytics/app/pages/adhoc/explorer-dashboard/ChartCard.tsx +96 -3
  64. package/corpus/templates/analytics/app/pages/adhoc/explorer-dashboard/index.tsx +3 -1
  65. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +61 -4
  66. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +40 -2
  67. package/corpus/templates/analytics/changelog/2026-07-09-analytics-no-longer-forgets-successful-data-queries-when-a-l.md +6 -0
  68. package/corpus/templates/analytics/changelog/2026-07-09-dashboard-charts-keep-consistent-spacing-when-they-stack-ver.md +6 -0
  69. package/corpus/templates/analytics/changelog/2026-07-09-dashboard-charts-tables-and-extensions-can-be-selected-and-d.md +6 -0
  70. package/corpus/templates/analytics/changelog/2026-07-09-long-running-analytics-requests-now-recover-automatically-wh.md +6 -0
  71. package/corpus/templates/analytics/server/db/index.ts +6 -0
  72. package/corpus/templates/clips/desktop/src/hooks/useMeetingTranscription.ts +1 -7
  73. package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +102 -15
  74. package/corpus/templates/content/app/hooks/use-content-database.ts +52 -0
  75. package/corpus/templates/content/app/hooks/use-document-properties.ts +50 -6
  76. package/corpus/templates/content/app/i18n/zh-TW.ts +2 -0
  77. package/corpus/templates/content/app/i18n-data.ts +28 -0
  78. package/corpus/templates/content/changelog/2026-07-09-notion-conflict-warnings-no-longer-flash-during-normal-synce.md +6 -0
  79. package/corpus/templates/content/changelog/2026-07-09-property-menus-and-delete-confirmations-now-open-visibly-and.md +6 -0
  80. package/corpus/templates/content/server/lib/notion-sync.ts +130 -28
  81. package/corpus/templates/design/actions/add-breakpoint.ts +48 -68
  82. package/corpus/templates/design/actions/add-localhost-screens.ts +496 -61
  83. package/corpus/templates/design/actions/apply-component-prop-edit.ts +2 -18
  84. package/corpus/templates/design/actions/apply-design-token-edit.ts +51 -82
  85. package/corpus/templates/design/actions/apply-tweaks.ts +63 -41
  86. package/corpus/templates/design/actions/capture-design-state.ts +2 -2
  87. package/corpus/templates/design/actions/connect-localhost.ts +58 -12
  88. package/corpus/templates/design/actions/create-component.ts +2 -18
  89. package/corpus/templates/design/actions/create-design-branch.ts +38 -18
  90. package/corpus/templates/design/actions/create-fusion-app.ts +15 -11
  91. package/corpus/templates/design/actions/delete-file.ts +19 -34
  92. package/corpus/templates/design/actions/deploy-design-preview.ts +59 -27
  93. package/corpus/templates/design/actions/deploy-fusion-app.ts +22 -15
  94. package/corpus/templates/design/actions/export-pdf.ts +2 -1
  95. package/corpus/templates/design/actions/export-zip.ts +9 -3
  96. package/corpus/templates/design/actions/generate-design.ts +107 -70
  97. package/corpus/templates/design/actions/get-component-details.ts +2 -18
  98. package/corpus/templates/design/actions/get-design-branch-diff.ts +2 -3
  99. package/corpus/templates/design/actions/get-design-surface-index.ts +2 -18
  100. package/corpus/templates/design/actions/get-design.ts +2 -1
  101. package/corpus/templates/design/actions/grant-localhost-write-consent.ts +7 -1
  102. package/corpus/templates/design/actions/import-design-tokens.ts +114 -76
  103. package/corpus/templates/design/actions/index-components.ts +2 -18
  104. package/corpus/templates/design/actions/list-design-source-capabilities.ts +23 -28
  105. package/corpus/templates/design/actions/list-local-files.ts +9 -2
  106. package/corpus/templates/design/actions/migrate-board-objects-to-file.ts +82 -98
  107. package/corpus/templates/design/actions/open-component-source.ts +2 -18
  108. package/corpus/templates/design/actions/open-visual-edit.ts +19 -1
  109. package/corpus/templates/design/actions/present-design-variants.ts +70 -70
  110. package/corpus/templates/design/actions/preview-component-prop-edit.ts +4 -14
  111. package/corpus/templates/design/actions/read-local-file.ts +9 -2
  112. package/corpus/templates/design/actions/remove-breakpoint.ts +40 -61
  113. package/corpus/templates/design/actions/rename-screen.ts +425 -0
  114. package/corpus/templates/design/actions/request-localhost-write-consent.ts +12 -2
  115. package/corpus/templates/design/actions/revoke-localhost-write-consent.ts +9 -2
  116. package/corpus/templates/design/actions/sync-fusion-app.ts +56 -39
  117. package/corpus/templates/design/actions/update-design.ts +376 -61
  118. package/corpus/templates/design/actions/update-file.ts +455 -188
  119. package/corpus/templates/design/actions/write-local-file.ts +10 -2
  120. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +685 -608
  121. package/corpus/templates/design/app/components/design/LayersPanel.tsx +43 -15
  122. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +576 -2015
  123. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +62 -14
  124. package/corpus/templates/design/app/components/design/bridge/embedded-wheel.bridge.ts +263 -25
  125. package/corpus/templates/design/app/components/design/design-canvas/annotation-submit.ts +34 -0
  126. package/corpus/templates/design/app/components/design/design-canvas/coordinate-transforms.ts +40 -0
  127. package/corpus/templates/design/app/components/design/design-canvas/creation.ts +47 -0
  128. package/corpus/templates/design/app/components/design/design-canvas/element-payload.ts +27 -0
  129. package/corpus/templates/design/app/components/design/design-canvas/embedded-frame.ts +62 -0
  130. package/corpus/templates/design/app/components/design/design-canvas/external-preview.ts +137 -0
  131. package/corpus/templates/design/app/components/design/design-canvas/file-drop.ts +10 -0
  132. package/corpus/templates/design/app/components/design/design-canvas/hit-test.ts +23 -0
  133. package/corpus/templates/design/app/components/design/design-canvas/iframe-events.ts +23 -0
  134. package/corpus/templates/design/app/components/design/design-canvas/iframe-pan.ts +177 -0
  135. package/corpus/templates/design/app/components/design/design-canvas/motion-types.ts +7 -0
  136. package/corpus/templates/design/app/components/design/design-canvas/pending-text-edit.ts +62 -0
  137. package/corpus/templates/design/app/components/design/index.ts +2 -2
  138. package/corpus/templates/design/app/components/design/multi-screen/canvas-tools.ts +100 -0
  139. package/corpus/templates/design/app/components/design/multi-screen/coordinate-transforms.ts +81 -0
  140. package/corpus/templates/design/app/components/design/multi-screen/cross-screen-drop.ts +23 -13
  141. package/corpus/templates/design/app/components/design/multi-screen/culling.ts +238 -19
  142. package/corpus/templates/design/app/components/design/multi-screen/draft-primitives.ts +424 -0
  143. package/corpus/templates/design/app/components/design/multi-screen/frame-geometry.ts +341 -0
  144. package/corpus/templates/design/app/components/design/multi-screen/iframe-targeting.ts +52 -0
  145. package/corpus/templates/design/app/components/design/multi-screen/overview-layout.ts +62 -0
  146. package/corpus/templates/design/app/components/design/multi-screen/primitive-drop-target.ts +283 -0
  147. package/corpus/templates/design/app/components/design/multi-screen/screen-content-cache.ts +272 -0
  148. package/corpus/templates/design/app/components/design/multi-screen/types.ts +5 -1
  149. package/corpus/templates/design/app/components/design/multi-screen/wheel-gesture-state.ts +12 -0
  150. package/corpus/templates/design/app/components/visual-editor/DrawOverlay.tsx +325 -84
  151. package/corpus/templates/design/app/hooks/use-navigation-state.ts +15 -1
  152. package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +119 -47
  153. package/corpus/templates/design/app/i18n/zh-TW.ts +9 -0
  154. package/corpus/templates/design/app/i18n-data.ts +223 -0
  155. package/corpus/templates/design/app/lib/design-save-outbox.ts +379 -0
  156. package/corpus/templates/design/app/lib/png-clipboard.ts +113 -0
  157. package/corpus/templates/design/app/pages/design-editor/canvas-primitives.ts +229 -0
  158. package/corpus/templates/design/app/pages/design-editor/code-layer-state.ts +918 -0
  159. package/corpus/templates/design/app/pages/design-editor/collab-sync.ts +43 -0
  160. package/corpus/templates/design/app/pages/design-editor/cross-screen-text-color.ts +336 -0
  161. package/corpus/templates/design/app/pages/design-editor/data-operations.ts +270 -0
  162. package/corpus/templates/design/app/pages/design-editor/dom-guards.ts +24 -0
  163. package/corpus/templates/design/app/pages/design-editor/dom-utils.ts +11 -0
  164. package/corpus/templates/design/app/pages/design-editor/editor-session.ts +20 -0
  165. package/corpus/templates/design/app/pages/design-editor/editor-state.ts +408 -0
  166. package/corpus/templates/design/app/pages/design-editor/export-capture.ts +115 -0
  167. package/corpus/templates/design/app/pages/design-editor/geometry-persistence.ts +93 -0
  168. package/corpus/templates/design/app/pages/design-editor/history.ts +169 -0
  169. package/corpus/templates/design/app/pages/design-editor/layout-operations.ts +185 -0
  170. package/corpus/templates/design/app/pages/design-editor/motion-state.ts +309 -0
  171. package/corpus/templates/design/app/pages/design-editor/overview-camera.ts +438 -0
  172. package/corpus/templates/design/app/pages/design-editor/pending-edits.ts +510 -0
  173. package/corpus/templates/design/app/pages/design-editor/portable-style.ts +115 -0
  174. package/corpus/templates/design/app/pages/design-editor/selection-state.ts +337 -0
  175. package/corpus/templates/design/app/pages/design-editor/style-utils.ts +5 -0
  176. package/corpus/templates/design/app/pages/design-editor/tool-state.ts +92 -0
  177. package/corpus/templates/design/app/pages/design-editor/tweak-save.ts +87 -0
  178. package/corpus/templates/design/app/pages/design-editor/types.ts +36 -0
  179. package/corpus/templates/design/app/routes/design.$id.tsx +11 -1
  180. package/corpus/templates/design/changelog/2026-07-09-design-edits-canvas-positions-tools-and-tab-context-now-pers.md +6 -0
  181. package/corpus/templates/design/changelog/2026-07-09-figma-style-shortcuts-drawing-annotations-and-cross-screen-d.md +6 -0
  182. package/corpus/templates/design/changelog/2026-07-09-hand-and-scale-tools-now-keep-their-active-toolbar-identity-.md +6 -0
  183. package/corpus/templates/design/changelog/2026-07-09-large-multi-screen-designs-now-stay-responsive-during-long-c.md +6 -0
  184. package/corpus/templates/design/changelog/2026-07-09-layer-drags-now-land-exactly-where-the-insertion-indicator-s.md +6 -0
  185. package/corpus/templates/design/server/db/index.ts +2 -52
  186. package/corpus/templates/design/server/db/schema.ts +16 -0
  187. package/corpus/templates/design/server/lib/design-data-access.ts +70 -0
  188. package/corpus/templates/design/server/lib/design-data-mutation.ts +249 -0
  189. package/corpus/templates/design/server/lib/fusion-screens.ts +89 -76
  190. package/corpus/templates/design/server/lib/import-design-files.ts +76 -35
  191. package/corpus/templates/design/server/lib/verify-write-grant.ts +7 -2
  192. package/corpus/templates/design/server/plugins/db.ts +19 -0
  193. package/corpus/templates/design/server/source-workspace.ts +12 -13
  194. package/corpus/templates/design/shared/resolve-tweaks.ts +15 -0
  195. package/corpus/templates/design/shared/screen-rename.ts +42 -0
  196. package/corpus/templates/design/shared/source-mode.ts +84 -4
  197. package/dist/agent/engine/builder-engine.d.ts +1 -1
  198. package/dist/agent/engine/builder-engine.d.ts.map +1 -1
  199. package/dist/agent/engine/builtin.js +1 -1
  200. package/dist/agent/engine/builtin.js.map +1 -1
  201. package/dist/agent/model-config.d.ts +9 -9
  202. package/dist/agent/model-config.d.ts.map +1 -1
  203. package/dist/agent/model-config.js +30 -30
  204. package/dist/agent/model-config.js.map +1 -1
  205. package/dist/agent/production-agent.d.ts +60 -1
  206. package/dist/agent/production-agent.d.ts.map +1 -1
  207. package/dist/agent/production-agent.js +106 -7
  208. package/dist/agent/production-agent.js.map +1 -1
  209. package/dist/cli/design-connect.d.ts +30 -1
  210. package/dist/cli/design-connect.d.ts.map +1 -1
  211. package/dist/cli/design-connect.js +359 -75
  212. package/dist/cli/design-connect.js.map +1 -1
  213. package/dist/cli/recap.d.ts.map +1 -1
  214. package/dist/cli/recap.js +13 -3
  215. package/dist/cli/recap.js.map +1 -1
  216. package/dist/client/AgentPanel.d.ts.map +1 -1
  217. package/dist/client/AgentPanel.js +6 -1
  218. package/dist/client/AgentPanel.js.map +1 -1
  219. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  220. package/dist/client/agent-chat-adapter.js +10 -5
  221. package/dist/client/agent-chat-adapter.js.map +1 -1
  222. package/dist/client/composer/TiptapComposer.js +1 -1
  223. package/dist/client/composer/TiptapComposer.js.map +1 -1
  224. package/dist/client/index.d.ts +1 -1
  225. package/dist/client/index.d.ts.map +1 -1
  226. package/dist/client/index.js +1 -1
  227. package/dist/client/index.js.map +1 -1
  228. package/dist/client/org/TeamPage.d.ts.map +1 -1
  229. package/dist/client/org/TeamPage.js +6 -5
  230. package/dist/client/org/TeamPage.js.map +1 -1
  231. package/dist/client/use-action.d.ts +32 -0
  232. package/dist/client/use-action.d.ts.map +1 -1
  233. package/dist/client/use-action.js +68 -1
  234. package/dist/client/use-action.js.map +1 -1
  235. package/dist/collab/struct-routes.d.ts +1 -1
  236. package/dist/db/client.d.ts +3 -0
  237. package/dist/db/client.d.ts.map +1 -1
  238. package/dist/db/client.js +7 -2
  239. package/dist/db/client.js.map +1 -1
  240. package/dist/db/create-get-db.d.ts.map +1 -1
  241. package/dist/db/create-get-db.js +2 -2
  242. package/dist/db/create-get-db.js.map +1 -1
  243. package/dist/notifications/routes.d.ts +3 -3
  244. package/dist/observability/routes.d.ts +1 -1
  245. package/dist/resources/handlers.d.ts +3 -3
  246. package/dist/scripts/agent-engines/manage-agent-engine.js +1 -1
  247. package/dist/scripts/agent-engines/manage-agent-engine.js.map +1 -1
  248. package/dist/scripts/agent-engines/set-agent-engine.js +1 -1
  249. package/dist/scripts/agent-engines/set-agent-engine.js.map +1 -1
  250. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  251. package/dist/server/ssr-handler.d.ts.map +1 -1
  252. package/dist/server/ssr-handler.js +7 -0
  253. package/dist/server/ssr-handler.js.map +1 -1
  254. package/dist/server/transcribe-voice.js +1 -1
  255. package/dist/server/transcribe-voice.js.map +1 -1
  256. package/dist/usage/store.d.ts.map +1 -1
  257. package/dist/usage/store.js +13 -4
  258. package/dist/usage/store.js.map +1 -1
  259. package/dist/vite/client.d.ts.map +1 -1
  260. package/dist/vite/client.js +36 -1
  261. package/dist/vite/client.js.map +1 -1
  262. package/docs/content/a2a-protocol.mdx +12 -0
  263. package/docs/content/locales/ar-SA/a2a-protocol.mdx +12 -0
  264. package/docs/content/locales/ar-SA/pr-visual-recap.mdx +3 -3
  265. package/docs/content/locales/ar-SA/pure-agent-apps.mdx +4 -0
  266. package/docs/content/locales/de-DE/a2a-protocol.mdx +12 -0
  267. package/docs/content/locales/de-DE/pr-visual-recap.mdx +3 -3
  268. package/docs/content/locales/de-DE/pure-agent-apps.mdx +4 -0
  269. package/docs/content/locales/es-ES/a2a-protocol.mdx +12 -0
  270. package/docs/content/locales/es-ES/pr-visual-recap.mdx +3 -3
  271. package/docs/content/locales/es-ES/pure-agent-apps.mdx +4 -0
  272. package/docs/content/locales/fr-FR/a2a-protocol.mdx +12 -0
  273. package/docs/content/locales/fr-FR/pr-visual-recap.mdx +3 -3
  274. package/docs/content/locales/fr-FR/pure-agent-apps.mdx +4 -0
  275. package/docs/content/locales/hi-IN/a2a-protocol.mdx +12 -0
  276. package/docs/content/locales/hi-IN/pr-visual-recap.mdx +3 -3
  277. package/docs/content/locales/hi-IN/pure-agent-apps.mdx +4 -0
  278. package/docs/content/locales/ja-JP/a2a-protocol.mdx +12 -0
  279. package/docs/content/locales/ja-JP/pr-visual-recap.mdx +3 -3
  280. package/docs/content/locales/ja-JP/pure-agent-apps.mdx +4 -0
  281. package/docs/content/locales/ko-KR/a2a-protocol.mdx +12 -0
  282. package/docs/content/locales/ko-KR/pr-visual-recap.mdx +3 -3
  283. package/docs/content/locales/ko-KR/pure-agent-apps.mdx +4 -0
  284. package/docs/content/locales/pt-BR/a2a-protocol.mdx +12 -0
  285. package/docs/content/locales/pt-BR/pr-visual-recap.mdx +3 -3
  286. package/docs/content/locales/pt-BR/pure-agent-apps.mdx +4 -0
  287. package/docs/content/locales/zh-CN/a2a-protocol.mdx +12 -0
  288. package/docs/content/locales/zh-CN/pr-visual-recap.mdx +3 -3
  289. package/docs/content/locales/zh-CN/pure-agent-apps.mdx +4 -0
  290. package/docs/content/locales/zh-TW/a2a-protocol.mdx +12 -0
  291. package/docs/content/locales/zh-TW/pr-visual-recap.mdx +3 -3
  292. package/docs/content/locales/zh-TW/pure-agent-apps.mdx +4 -0
  293. package/docs/content/pr-visual-recap.mdx +3 -3
  294. package/docs/content/pure-agent-apps.mdx +22 -0
  295. package/package.json +2 -2
@@ -4,7 +4,7 @@ import {
4
4
  getRequestUserEmail,
5
5
  } from "@agent-native/core/server/request-context";
6
6
  import { assertAccess } from "@agent-native/core/sharing";
7
- import { and, eq } from "drizzle-orm";
7
+ import { and, eq, isNull } from "drizzle-orm";
8
8
  import { nanoid } from "nanoid";
9
9
  import { z } from "zod";
10
10
 
@@ -53,6 +53,9 @@ export default defineAction({
53
53
  and(
54
54
  eq(schema.designLocalhostConnections.id, connectionId),
55
55
  eq(schema.designLocalhostConnections.ownerEmail, ownerEmail),
56
+ orgId
57
+ ? eq(schema.designLocalhostConnections.orgId, orgId)
58
+ : isNull(schema.designLocalhostConnections.orgId),
56
59
  ),
57
60
  )
58
61
  .limit(1);
@@ -93,6 +96,9 @@ export default defineAction({
93
96
  eq(schema.designLocalhostWriteGrants.designId, designId),
94
97
  eq(schema.designLocalhostWriteGrants.connectionId, connectionId),
95
98
  eq(schema.designLocalhostWriteGrants.ownerEmail, ownerEmail),
99
+ orgId
100
+ ? eq(schema.designLocalhostWriteGrants.orgId, orgId)
101
+ : isNull(schema.designLocalhostWriteGrants.orgId),
96
102
  ),
97
103
  )
98
104
  .limit(1);
@@ -11,12 +11,17 @@ import {
11
11
  extractDocumentColors,
12
12
  extractDocumentFonts,
13
13
  } from "@agent-native/core/server/design-token-utils";
14
- import { assertAccess, resolveAccess } from "@agent-native/core/sharing";
14
+ import { assertAccess } from "@agent-native/core/sharing";
15
15
  import { eq } from "drizzle-orm";
16
+ import { nanoid } from "nanoid";
16
17
  import { z } from "zod";
17
18
 
18
19
  import { getDb, schema } from "../server/db/index.js";
19
20
  import "../server/db/index.js"; // ensure registerShareableResource runs
21
+ import {
22
+ mutateDesignData,
23
+ type DesignDataRecord,
24
+ } from "../server/lib/design-data-mutation.js";
20
25
  import {
21
26
  isSafeCssTokenValue,
22
27
  isSafeCssVarName,
@@ -39,6 +44,31 @@ interface ImportedDesignToken {
39
44
  source: string;
40
45
  }
41
46
 
47
+ type TweakDef = Parameters<typeof resolveTweaksToCssVars>[0][number];
48
+ type TweakSelections = Record<string, string | number | boolean>;
49
+
50
+ function readTweaks(data: DesignDataRecord): TweakDef[] {
51
+ return Array.isArray(data.tweaks) ? (data.tweaks as TweakDef[]) : [];
52
+ }
53
+
54
+ function readSelections(data: DesignDataRecord): TweakSelections {
55
+ return data.tweakSelections &&
56
+ typeof data.tweakSelections === "object" &&
57
+ !Array.isArray(data.tweakSelections)
58
+ ? (data.tweakSelections as TweakSelections)
59
+ : {};
60
+ }
61
+
62
+ function selectionKeyForCssVar(tweaks: TweakDef[], cssVar: string): string {
63
+ return tweaks.find((tweak) => tweak.cssVar === cssVar)?.id ?? cssVar;
64
+ }
65
+
66
+ function tokenSourceLabel(token: ImportedDesignToken, sourceLabel: string) {
67
+ return token.source === "CSS variables" || token.source === "Colors"
68
+ ? sourceLabel
69
+ : token.source;
70
+ }
71
+
42
72
  const tokenFileSchema = z.object({
43
73
  filename: z.string().trim().min(1).describe("File name or relative path"),
44
74
  content: z.string().describe("Raw text content"),
@@ -421,85 +451,93 @@ export default defineAction({
421
451
  };
422
452
  }
423
453
 
424
- const access = await resolveAccess("design", designId);
425
- if (!access) throw new Error("Design not found");
426
-
427
- let prevData: Record<string, unknown> = {};
428
- if (access.resource.data) {
429
- try {
430
- const parsed = JSON.parse(access.resource.data);
431
- if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
432
- prevData = parsed;
454
+ const importId = nanoid();
455
+ const { data: persistedData } = await mutateDesignData({
456
+ designId,
457
+ mutate: (prevData, { updatedAt }) => {
458
+ const tweaks = readTweaks(prevData);
459
+ const nextSelections = { ...readSelections(prevData) };
460
+ for (const token of safeTokens) {
461
+ nextSelections[selectionKeyForCssVar(tweaks, token.cssVar)] =
462
+ token.value;
433
463
  }
434
- } catch {
435
- // Stale/invalid JSON — preserve the write with a fresh object.
436
- }
437
- }
438
464
 
439
- type TweakDef = Parameters<typeof resolveTweaksToCssVars>[0][number];
440
- const tweaks: TweakDef[] = Array.isArray(prevData.tweaks)
441
- ? (prevData.tweaks as TweakDef[])
442
- : [];
443
- const cssVarToTweakId = new Map<string, string>();
444
- for (const tweak of tweaks) {
445
- if (tweak.cssVar) cssVarToTweakId.set(tweak.cssVar, tweak.id);
446
- }
447
-
448
- const prevSelections: Record<string, string | number | boolean> =
449
- prevData.tweakSelections &&
450
- typeof prevData.tweakSelections === "object" &&
451
- !Array.isArray(prevData.tweakSelections)
452
- ? (prevData.tweakSelections as Record<
453
- string,
454
- string | number | boolean
455
- >)
456
- : {};
457
- const nextSelections = { ...prevSelections };
458
-
459
- for (const token of safeTokens) {
460
- const tweakId = cssVarToTweakId.get(token.cssVar);
461
- nextSelections[tweakId ?? token.cssVar] = token.value;
462
- }
463
-
464
- const previousSources =
465
- prevData.tokenImportSources &&
466
- typeof prevData.tokenImportSources === "object" &&
467
- !Array.isArray(prevData.tokenImportSources)
468
- ? (prevData.tokenImportSources as Record<string, string>)
469
- : {};
470
- const tokenImportSources = { ...previousSources };
471
- for (const token of safeTokens) {
472
- tokenImportSources[token.cssVar] =
473
- token.source === "CSS variables" || token.source === "Colors"
474
- ? sourceLabel
475
- : token.source;
476
- }
465
+ const previousSources =
466
+ prevData.tokenImportSources &&
467
+ typeof prevData.tokenImportSources === "object" &&
468
+ !Array.isArray(prevData.tokenImportSources)
469
+ ? (prevData.tokenImportSources as Record<string, string>)
470
+ : {};
471
+ const tokenImportSources = { ...previousSources };
472
+ for (const token of safeTokens) {
473
+ tokenImportSources[token.cssVar] = tokenSourceLabel(
474
+ token,
475
+ sourceLabel,
476
+ );
477
+ }
477
478
 
478
- const now = new Date().toISOString();
479
- const previousImports = Array.isArray(prevData.tokenImports)
480
- ? (prevData.tokenImports as unknown[])
481
- : [];
482
- const nextData: Record<string, unknown> = {
483
- ...prevData,
484
- tweakSelections: nextSelections,
485
- tokenImportSources,
486
- tokenImports: [
487
- ...previousImports.slice(-9),
488
- {
489
- source,
490
- sourceLabel,
491
- tokenCount: safeTokens.length,
492
- filesAnalyzed,
493
- importedAt: now,
494
- },
495
- ],
496
- tweaksAppliedAt: now,
497
- };
479
+ const previousImports = Array.isArray(prevData.tokenImports)
480
+ ? (prevData.tokenImports as unknown[])
481
+ : [];
482
+ const alreadyAppended = previousImports.some(
483
+ (entry) =>
484
+ entry !== null &&
485
+ typeof entry === "object" &&
486
+ (entry as { id?: unknown }).id === importId,
487
+ );
488
+ const tokenImports = alreadyAppended
489
+ ? previousImports
490
+ : [
491
+ ...previousImports.slice(-9),
492
+ {
493
+ id: importId,
494
+ source,
495
+ sourceLabel,
496
+ tokenCount: safeTokens.length,
497
+ filesAnalyzed,
498
+ importedAt: updatedAt,
499
+ },
500
+ ];
501
+
502
+ return {
503
+ ...prevData,
504
+ tweakSelections: nextSelections,
505
+ tokenImportSources,
506
+ tokenImports,
507
+ tweaksAppliedAt: updatedAt,
508
+ };
509
+ },
510
+ isApplied: (data) => {
511
+ const tweaks = readTweaks(data);
512
+ const selections = readSelections(data);
513
+ const sources =
514
+ data.tokenImportSources &&
515
+ typeof data.tokenImportSources === "object" &&
516
+ !Array.isArray(data.tokenImportSources)
517
+ ? (data.tokenImportSources as Record<string, string>)
518
+ : {};
519
+ const imports = Array.isArray(data.tokenImports)
520
+ ? data.tokenImports
521
+ : [];
522
+ return (
523
+ safeTokens.every(
524
+ (token) =>
525
+ selections[selectionKeyForCssVar(tweaks, token.cssVar)] ===
526
+ token.value &&
527
+ sources[token.cssVar] === tokenSourceLabel(token, sourceLabel),
528
+ ) &&
529
+ imports.some(
530
+ (entry) =>
531
+ entry !== null &&
532
+ typeof entry === "object" &&
533
+ (entry as { id?: unknown }).id === importId,
534
+ )
535
+ );
536
+ },
537
+ });
498
538
 
499
- await db
500
- .update(schema.designs)
501
- .set({ data: JSON.stringify(nextData), updatedAt: now })
502
- .where(eq(schema.designs.id, designId));
539
+ const tweaks = readTweaks(persistedData);
540
+ const nextSelections = readSelections(persistedData);
503
541
 
504
542
  return {
505
543
  designId,
@@ -34,7 +34,7 @@ import {
34
34
  type ComponentInstance,
35
35
  } from "../shared/component-model.js";
36
36
  import { hasCapability } from "../shared/design-source-capabilities.js";
37
- import { normalizeDesignSourceType } from "../shared/source-mode.js";
37
+ import { designSourceTypeFromData } from "../shared/source-mode.js";
38
38
 
39
39
  // ─── Helpers ──────────────────────────────────────────────────────────────────
40
40
 
@@ -89,24 +89,8 @@ export default defineAction({
89
89
  const access = await assertAccess("design", designId, "editor");
90
90
 
91
91
  // ── Source type + capability check ──────────────────────────────────────
92
- let rawSourceType: unknown = "inline";
93
92
  const rawData = (access.resource as { data?: unknown }).data;
94
- if (typeof rawData === "string") {
95
- try {
96
- const parsed: unknown = JSON.parse(rawData);
97
- if (
98
- parsed !== null &&
99
- typeof parsed === "object" &&
100
- "sourceType" in (parsed as object)
101
- ) {
102
- rawSourceType = (parsed as { sourceType: unknown }).sourceType;
103
- }
104
- } catch {
105
- // Stale JSON — default to inline.
106
- }
107
- }
108
-
109
- const sourceType = normalizeDesignSourceType(rawSourceType) ?? "inline";
93
+ const sourceType = designSourceTypeFromData(rawData);
110
94
  const caps = resolveSourceCapabilities(sourceType);
111
95
 
112
96
  // Real-app sources gate on `indexComponents`. For inline designs this
@@ -1,13 +1,20 @@
1
1
  import { defineAction } from "@agent-native/core";
2
+ import {
3
+ getRequestOrgId,
4
+ getRequestUserEmail,
5
+ } from "@agent-native/core/server/request-context";
2
6
  import { resolveAccess } from "@agent-native/core/sharing";
3
- import { eq } from "drizzle-orm";
7
+ import { and, eq, isNull } from "drizzle-orm";
4
8
  import { z } from "zod";
5
9
 
6
10
  import { getDb, schema } from "../server/db/index.js";
7
11
  import { resolveSourceCapabilities } from "../shared/capability-resolver.js";
8
12
  import { DESIGN_CAPABILITY_NAMES } from "../shared/design-source-capabilities.js";
9
13
  import "../server/db/index.js"; // ensure registerShareableResource runs
10
- import { normalizeDesignSourceType } from "../shared/source-mode.js";
14
+ import {
15
+ designConnectionIdFromData,
16
+ designSourceTypeFromData,
17
+ } from "../shared/source-mode.js";
11
18
 
12
19
  export default defineAction({
13
20
  description:
@@ -36,25 +43,8 @@ export default defineAction({
36
43
  // Resolve source type from the design's data blob. The `sourceType`
37
44
  // field lives inside `designs.data` as a JSON key (set by connect-localhost
38
45
  // and the fusion upgrade flow), falling back to "inline" when absent.
39
- let rawSourceType: unknown = "inline";
40
46
  const rawData = (access.resource as { data?: unknown }).data;
41
- if (typeof rawData === "string") {
42
- try {
43
- const parsed: unknown = JSON.parse(rawData);
44
- if (
45
- parsed !== null &&
46
- typeof parsed === "object" &&
47
- !Array.isArray(parsed) &&
48
- "sourceType" in (parsed as object)
49
- ) {
50
- rawSourceType = (parsed as { sourceType: unknown }).sourceType;
51
- }
52
- } catch {
53
- // Stale/invalid JSON — default to "inline".
54
- }
55
- }
56
-
57
- const sourceType = normalizeDesignSourceType(rawSourceType) ?? "inline";
47
+ const sourceType = designSourceTypeFromData(rawData);
58
48
  const capabilities = resolveSourceCapabilities(sourceType);
59
49
 
60
50
  // Collect active localhost capabilities from the design's stored connection,
@@ -65,20 +55,25 @@ export default defineAction({
65
55
  try {
66
56
  const rawDesignData =
67
57
  typeof rawData === "string" ? JSON.parse(rawData) : {};
68
- const connectionId =
69
- typeof rawDesignData === "object" &&
70
- rawDesignData !== null &&
71
- "connectionId" in rawDesignData
72
- ? String(rawDesignData.connectionId)
73
- : undefined;
58
+ const connectionId = designConnectionIdFromData(rawDesignData);
74
59
 
75
- if (connectionId) {
60
+ const ownerEmail = getRequestUserEmail();
61
+ const orgId = getRequestOrgId() ?? null;
62
+ if (connectionId && ownerEmail) {
76
63
  const [conn] = await db
77
64
  .select({
78
65
  capabilities: schema.designLocalhostConnections.capabilities,
79
66
  })
80
67
  .from(schema.designLocalhostConnections)
81
- .where(eq(schema.designLocalhostConnections.id, connectionId))
68
+ .where(
69
+ and(
70
+ eq(schema.designLocalhostConnections.id, connectionId),
71
+ eq(schema.designLocalhostConnections.ownerEmail, ownerEmail),
72
+ orgId
73
+ ? eq(schema.designLocalhostConnections.orgId, orgId)
74
+ : isNull(schema.designLocalhostConnections.orgId),
75
+ ),
76
+ )
82
77
  .limit(1);
83
78
 
84
79
  if (conn?.capabilities) {
@@ -13,9 +13,12 @@
13
13
  */
14
14
 
15
15
  import { defineAction } from "@agent-native/core";
16
- import { getRequestUserEmail } from "@agent-native/core/server/request-context";
16
+ import {
17
+ getRequestOrgId,
18
+ getRequestUserEmail,
19
+ } from "@agent-native/core/server/request-context";
17
20
  import { assertAccess } from "@agent-native/core/sharing";
18
- import { and, eq } from "drizzle-orm";
21
+ import { and, eq, isNull } from "drizzle-orm";
19
22
  import { z } from "zod";
20
23
 
21
24
  import { getDb, schema } from "../server/db/index.js";
@@ -76,6 +79,7 @@ export default defineAction({
76
79
 
77
80
  const ownerEmail = getRequestUserEmail();
78
81
  if (!ownerEmail) throw new Error("no authenticated user");
82
+ const orgId = getRequestOrgId() ?? null;
79
83
 
80
84
  const db = getDb();
81
85
  const [connection] = await db
@@ -88,6 +92,9 @@ export default defineAction({
88
92
  and(
89
93
  eq(schema.designLocalhostConnections.id, connectionId),
90
94
  eq(schema.designLocalhostConnections.ownerEmail, ownerEmail),
95
+ orgId
96
+ ? eq(schema.designLocalhostConnections.orgId, orgId)
97
+ : isNull(schema.designLocalhostConnections.orgId),
91
98
  ),
92
99
  )
93
100
  .limit(1);
@@ -13,8 +13,8 @@
13
13
  * coordinates exactly (no clamping).
14
14
  * 3. Inserts the fragments as direct <body> children of a new __board__.html
15
15
  * design file (or merges into an existing one if somehow already present).
16
- * 4. Stores the new file's id into designs.data.boardFileId in a transaction.
17
- * 5. Nulls out designs.data.boardObjects so the old model no longer conflicts.
16
+ * 4. Reserves one stable file id in designs.data, then upserts the board file.
17
+ * 5. Finalizes boardFileId and nulls boardObjects through a retryable mutation.
18
18
  *
19
19
  * ## Contract
20
20
  *
@@ -37,6 +37,7 @@ import { nanoid } from "nanoid";
37
37
  import { z } from "zod";
38
38
 
39
39
  import { getDb, schema } from "../server/db/index.js";
40
+ import { mutateDesignData } from "../server/lib/design-data-mutation.js";
40
41
  import {
41
42
  BOARD_FILENAME,
42
43
  backfillBoardPrimitiveMarkers,
@@ -64,29 +65,39 @@ export default defineAction({
64
65
  const db = getDb();
65
66
 
66
67
  // ── 1. Idempotency guard ──────────────────────────────────────────────
67
- // Read the design data JSON to check whether boardFileId is already set.
68
- // guard:allow-unscoped assertAccess "editor" above scopes this read to
69
- // the requesting user's accessible designs.
70
- const [existing] = await db
71
- .select({ data: schema.designs.data })
72
- .from(schema.designs)
73
- .where(eq(schema.designs.id, designId))
68
+ // Reserve one stable file id without clearing boardObjects. If the process
69
+ // stops between phases, the next call resumes the same reservation.
70
+ const [preexistingBoardFile] = await db
71
+ .select({ id: schema.designFiles.id })
72
+ .from(schema.designFiles)
73
+ .where(
74
+ and(
75
+ eq(schema.designFiles.designId, designId),
76
+ eq(schema.designFiles.filename, BOARD_FILENAME),
77
+ ),
78
+ )
74
79
  .limit(1);
75
-
76
- if (!existing) {
77
- throw new Error(`Design "${designId}" not found.`);
78
- }
79
-
80
- let parsed: Record<string, unknown>;
81
- try {
82
- const raw = JSON.parse(existing.data);
83
- parsed =
84
- raw && typeof raw === "object" && !Array.isArray(raw)
85
- ? (raw as Record<string, unknown>)
86
- : {};
87
- } catch {
88
- parsed = {};
89
- }
80
+ const proposedBoardFileId = preexistingBoardFile?.id ?? nanoid();
81
+ const reservation = await mutateDesignData({
82
+ designId,
83
+ mutate: (current) => {
84
+ if (
85
+ (typeof current.boardFileId === "string" && current.boardFileId) ||
86
+ (typeof current.boardFileMigrationId === "string" &&
87
+ current.boardFileMigrationId)
88
+ ) {
89
+ return current;
90
+ }
91
+ return { ...current, boardFileMigrationId: proposedBoardFileId };
92
+ },
93
+ isApplied: (current) =>
94
+ Boolean(
95
+ (typeof current.boardFileId === "string" && current.boardFileId) ||
96
+ (typeof current.boardFileMigrationId === "string" &&
97
+ current.boardFileMigrationId),
98
+ ),
99
+ });
100
+ const parsed = reservation.data;
90
101
 
91
102
  if (
92
103
  typeof parsed["boardFileId"] === "string" &&
@@ -176,68 +187,32 @@ export default defineAction({
176
187
 
177
188
  const now = new Date().toISOString();
178
189
 
179
- // ── 4. Upsert the board file and update designs.data atomically ───────
180
- // Run in a transaction so concurrent opens cannot create two board files.
181
- let boardFileId = "";
182
-
183
- await db.transaction(async (tx) => {
184
- // Re-read designs.data inside the transaction to guard against a
185
- // concurrent migration that raced us.
186
- const [fresh] = await tx
187
- .select({ data: schema.designs.data })
188
- .from(schema.designs)
189
- .where(eq(schema.designs.id, designId))
190
- .limit(1);
191
-
192
- if (!fresh) {
193
- throw new Error(`Design "${designId}" not found.`);
194
- }
190
+ // ── 4. Upsert the board file, then finalize the reservation ────────
191
+ const reservationId =
192
+ typeof parsed.boardFileMigrationId === "string" &&
193
+ parsed.boardFileMigrationId
194
+ ? parsed.boardFileMigrationId
195
+ : proposedBoardFileId;
196
+ const [existingBoardFile] = await db
197
+ .select({ id: schema.designFiles.id })
198
+ .from(schema.designFiles)
199
+ .where(
200
+ and(
201
+ eq(schema.designFiles.designId, designId),
202
+ eq(schema.designFiles.filename, BOARD_FILENAME),
203
+ ),
204
+ )
205
+ .limit(1);
206
+ const boardFileId = existingBoardFile?.id ?? reservationId;
195
207
 
196
- let freshParsed: Record<string, unknown>;
208
+ if (existingBoardFile) {
209
+ await db
210
+ .update(schema.designFiles)
211
+ .set({ content: boardHtml, updatedAt: now })
212
+ .where(eq(schema.designFiles.id, boardFileId));
213
+ } else {
197
214
  try {
198
- const raw = JSON.parse(fresh.data);
199
- freshParsed =
200
- raw && typeof raw === "object" && !Array.isArray(raw)
201
- ? (raw as Record<string, unknown>)
202
- : {};
203
- } catch {
204
- freshParsed = {};
205
- }
206
-
207
- // Double-check inside the transaction.
208
- if (
209
- typeof freshParsed["boardFileId"] === "string" &&
210
- freshParsed["boardFileId"].length > 0
211
- ) {
212
- boardFileId = freshParsed["boardFileId"] as string;
213
- return; // Another concurrent call already finished — skip.
214
- }
215
-
216
- // Check if a __board__.html file was somehow already created (shouldn't
217
- // happen in normal flow, but be defensive).
218
- const [existingBoardFile] = await tx
219
- .select({ id: schema.designFiles.id })
220
- .from(schema.designFiles)
221
- .where(
222
- and(
223
- eq(schema.designFiles.designId, designId),
224
- eq(schema.designFiles.filename, BOARD_FILENAME),
225
- ),
226
- )
227
- .limit(1);
228
-
229
- if (existingBoardFile) {
230
- // Board file exists but boardFileId was not set — link it and update
231
- // its content.
232
- boardFileId = existingBoardFile.id;
233
- await tx
234
- .update(schema.designFiles)
235
- .set({ content: boardHtml, updatedAt: now })
236
- .where(eq(schema.designFiles.id, boardFileId));
237
- } else {
238
- // Create the board file.
239
- boardFileId = nanoid();
240
- await tx.insert(schema.designFiles).values({
215
+ await db.insert(schema.designFiles).values({
241
216
  id: boardFileId,
242
217
  designId,
243
218
  filename: BOARD_FILENAME,
@@ -246,22 +221,31 @@ export default defineAction({
246
221
  createdAt: now,
247
222
  updatedAt: now,
248
223
  });
224
+ } catch (error) {
225
+ const [concurrentBoardFile] = await db
226
+ .select({ id: schema.designFiles.id })
227
+ .from(schema.designFiles)
228
+ .where(eq(schema.designFiles.id, boardFileId))
229
+ .limit(1);
230
+ if (!concurrentBoardFile) throw error;
249
231
  }
232
+ }
250
233
 
251
- // Write boardFileId into designs.data; null out boardObjects.
252
- const nextData: Record<string, unknown> = {
253
- ...freshParsed,
254
- boardFileId,
255
- boardObjects: null,
256
- };
257
-
258
- await tx
259
- .update(schema.designs)
260
- .set({
261
- data: JSON.stringify(nextData),
262
- updatedAt: now,
263
- })
264
- .where(eq(schema.designs.id, designId));
234
+ await mutateDesignData({
235
+ designId,
236
+ mutate: (current) => {
237
+ if (typeof current.boardFileId === "string" && current.boardFileId) {
238
+ return current;
239
+ }
240
+ const next: Record<string, unknown> = {
241
+ ...current,
242
+ boardFileId,
243
+ boardObjects: null,
244
+ };
245
+ delete next.boardFileMigrationId;
246
+ return next;
247
+ },
248
+ isApplied: (current) => current.boardFileId === boardFileId,
265
249
  });
266
250
 
267
251
  // ── 5. Seed collab state for the new board file ───────────────────────
@@ -41,7 +41,7 @@ import {
41
41
  componentNodeIdMatches,
42
42
  } from "../shared/component-model.js";
43
43
  import { hasCapability } from "../shared/design-source-capabilities.js";
44
- import { normalizeDesignSourceType } from "../shared/source-mode.js";
44
+ import { designSourceTypeFromData } from "../shared/source-mode.js";
45
45
 
46
46
  // ─── Helpers ──────────────────────────────────────────────────────────────────
47
47
 
@@ -90,24 +90,8 @@ export default defineAction({
90
90
  if (!access) throw new Error("Design not found");
91
91
 
92
92
  // ── Source type + capabilities ───────────────────────────────────────────
93
- let rawSourceType: unknown = "inline";
94
93
  const rawData = (access.resource as { data?: unknown }).data;
95
- if (typeof rawData === "string") {
96
- try {
97
- const parsed: unknown = JSON.parse(rawData);
98
- if (
99
- parsed !== null &&
100
- typeof parsed === "object" &&
101
- "sourceType" in (parsed as object)
102
- ) {
103
- rawSourceType = (parsed as { sourceType: unknown }).sourceType;
104
- }
105
- } catch {
106
- // Default to inline.
107
- }
108
- }
109
-
110
- const sourceType = normalizeDesignSourceType(rawSourceType) ?? "inline";
94
+ const sourceType = designSourceTypeFromData(rawData);
111
95
  const caps = resolveSourceCapabilities(sourceType);
112
96
  const canResolveToFile = hasCapability(caps, "resolveNodeToFile");
113
97