@agent-native/core 0.92.6 → 0.92.8

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 (297) 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 +2 -2
  42. package/corpus/core/src/cli/sync-builder-starter-manifest.ts +1 -1
  43. package/corpus/core/src/client/AgentPanel.tsx +8 -1
  44. package/corpus/core/src/client/agent-chat-adapter.ts +12 -7
  45. package/corpus/core/src/client/composer/TiptapComposer.tsx +1 -1
  46. package/corpus/core/src/client/index.ts +4 -0
  47. package/corpus/core/src/client/org/TeamPage.tsx +41 -26
  48. package/corpus/core/src/client/use-action.ts +113 -1
  49. package/corpus/core/src/db/client.ts +12 -2
  50. package/corpus/core/src/db/create-get-db.ts +4 -1
  51. package/corpus/core/src/scripts/agent-engines/manage-agent-engine.ts +1 -1
  52. package/corpus/core/src/scripts/agent-engines/set-agent-engine.ts +1 -1
  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.js +2 -2
  214. package/dist/cli/recap.js.map +1 -1
  215. package/dist/cli/sync-builder-starter-manifest.js +1 -1
  216. package/dist/cli/sync-builder-starter-manifest.js.map +1 -1
  217. package/dist/client/AgentPanel.d.ts.map +1 -1
  218. package/dist/client/AgentPanel.js +6 -1
  219. package/dist/client/AgentPanel.js.map +1 -1
  220. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  221. package/dist/client/agent-chat-adapter.js +10 -5
  222. package/dist/client/agent-chat-adapter.js.map +1 -1
  223. package/dist/client/composer/TiptapComposer.js +1 -1
  224. package/dist/client/composer/TiptapComposer.js.map +1 -1
  225. package/dist/client/index.d.ts +1 -1
  226. package/dist/client/index.d.ts.map +1 -1
  227. package/dist/client/index.js +1 -1
  228. package/dist/client/index.js.map +1 -1
  229. package/dist/client/org/TeamPage.d.ts.map +1 -1
  230. package/dist/client/org/TeamPage.js +6 -5
  231. package/dist/client/org/TeamPage.js.map +1 -1
  232. package/dist/client/use-action.d.ts +32 -0
  233. package/dist/client/use-action.d.ts.map +1 -1
  234. package/dist/client/use-action.js +68 -1
  235. package/dist/client/use-action.js.map +1 -1
  236. package/dist/collab/awareness.d.ts +2 -2
  237. package/dist/collab/awareness.d.ts.map +1 -1
  238. package/dist/collab/routes.d.ts +1 -1
  239. package/dist/db/client.d.ts +3 -0
  240. package/dist/db/client.d.ts.map +1 -1
  241. package/dist/db/client.js +7 -2
  242. package/dist/db/client.js.map +1 -1
  243. package/dist/db/create-get-db.d.ts.map +1 -1
  244. package/dist/db/create-get-db.js +2 -2
  245. package/dist/db/create-get-db.js.map +1 -1
  246. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  247. package/dist/notifications/routes.d.ts +1 -1
  248. package/dist/observability/routes.d.ts +5 -5
  249. package/dist/progress/routes.d.ts +1 -1
  250. package/dist/resources/handlers.d.ts +1 -1
  251. package/dist/scripts/agent-engines/manage-agent-engine.js +1 -1
  252. package/dist/scripts/agent-engines/manage-agent-engine.js.map +1 -1
  253. package/dist/scripts/agent-engines/set-agent-engine.js +1 -1
  254. package/dist/scripts/agent-engines/set-agent-engine.js.map +1 -1
  255. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  256. package/dist/server/transcribe-voice.js +1 -1
  257. package/dist/server/transcribe-voice.js.map +1 -1
  258. package/dist/usage/store.d.ts.map +1 -1
  259. package/dist/usage/store.js +13 -4
  260. package/dist/usage/store.js.map +1 -1
  261. package/dist/vite/client.d.ts.map +1 -1
  262. package/dist/vite/client.js +36 -1
  263. package/dist/vite/client.js.map +1 -1
  264. package/docs/content/a2a-protocol.mdx +12 -0
  265. package/docs/content/locales/ar-SA/a2a-protocol.mdx +12 -0
  266. package/docs/content/locales/ar-SA/pr-visual-recap.mdx +3 -3
  267. package/docs/content/locales/ar-SA/pure-agent-apps.mdx +4 -0
  268. package/docs/content/locales/de-DE/a2a-protocol.mdx +12 -0
  269. package/docs/content/locales/de-DE/pr-visual-recap.mdx +3 -3
  270. package/docs/content/locales/de-DE/pure-agent-apps.mdx +4 -0
  271. package/docs/content/locales/es-ES/a2a-protocol.mdx +12 -0
  272. package/docs/content/locales/es-ES/pr-visual-recap.mdx +3 -3
  273. package/docs/content/locales/es-ES/pure-agent-apps.mdx +4 -0
  274. package/docs/content/locales/fr-FR/a2a-protocol.mdx +12 -0
  275. package/docs/content/locales/fr-FR/pr-visual-recap.mdx +3 -3
  276. package/docs/content/locales/fr-FR/pure-agent-apps.mdx +4 -0
  277. package/docs/content/locales/hi-IN/a2a-protocol.mdx +12 -0
  278. package/docs/content/locales/hi-IN/pr-visual-recap.mdx +3 -3
  279. package/docs/content/locales/hi-IN/pure-agent-apps.mdx +4 -0
  280. package/docs/content/locales/ja-JP/a2a-protocol.mdx +12 -0
  281. package/docs/content/locales/ja-JP/pr-visual-recap.mdx +3 -3
  282. package/docs/content/locales/ja-JP/pure-agent-apps.mdx +4 -0
  283. package/docs/content/locales/ko-KR/a2a-protocol.mdx +12 -0
  284. package/docs/content/locales/ko-KR/pr-visual-recap.mdx +3 -3
  285. package/docs/content/locales/ko-KR/pure-agent-apps.mdx +4 -0
  286. package/docs/content/locales/pt-BR/a2a-protocol.mdx +12 -0
  287. package/docs/content/locales/pt-BR/pr-visual-recap.mdx +3 -3
  288. package/docs/content/locales/pt-BR/pure-agent-apps.mdx +4 -0
  289. package/docs/content/locales/zh-CN/a2a-protocol.mdx +12 -0
  290. package/docs/content/locales/zh-CN/pr-visual-recap.mdx +3 -3
  291. package/docs/content/locales/zh-CN/pure-agent-apps.mdx +4 -0
  292. package/docs/content/locales/zh-TW/a2a-protocol.mdx +12 -0
  293. package/docs/content/locales/zh-TW/pr-visual-recap.mdx +3 -3
  294. package/docs/content/locales/zh-TW/pure-agent-apps.mdx +4 -0
  295. package/docs/content/pr-visual-recap.mdx +3 -3
  296. package/docs/content/pure-agent-apps.mdx +22 -0
  297. package/package.json +2 -2
@@ -12,6 +12,12 @@ export const designs = table("designs", {
12
12
  title: text("title").notNull(),
13
13
  description: text("description"),
14
14
  data: text("data").notNull(),
15
+ // Monotonic per-client sequence numbers for path-addressed data writes.
16
+ // Kept outside `data` so editor/export payloads stay free of transport
17
+ // bookkeeping while late keepalive requests can be rejected atomically.
18
+ dataOperationRevisions: text("data_operation_revisions")
19
+ .notNull()
20
+ .default("{}"),
15
21
  projectType: text("project_type").notNull().default("prototype"),
16
22
  designSystemId: text("design_system_id"),
17
23
  createdAt: text("created_at").default(now()),
@@ -43,6 +49,12 @@ export const designFiles = table("design_files", {
43
49
  designId: text("design_id").notNull(),
44
50
  filename: text("filename").notNull(),
45
51
  content: text("content").notNull(),
52
+ // Last accepted browser content-save operation. This transport metadata
53
+ // stays beside (rather than inside) the document so a late unload
54
+ // keepalive can be rejected without parsing or rewriting user content.
55
+ contentOperationSource: text("content_operation_source"),
56
+ contentOperationRevision: integer("content_operation_revision"),
57
+ contentOperationResultHash: text("content_operation_result_hash"),
46
58
  fileType: text("file_type").notNull().default("html"),
47
59
  createdAt: text("created_at").default(now()),
48
60
  updatedAt: text("updated_at").default(now()),
@@ -75,6 +87,10 @@ export const designLocalhostConnections = table(
75
87
  .notNull()
76
88
  .default("connected"),
77
89
  lastSeenAt: text("last_seen_at"),
90
+ /** Read-only credential used by browser preview/bridge-registration calls.
91
+ * It is one-way derived from the filesystem token when not supplied by a
92
+ * newer bridge, so leaking it cannot grant source-file access. */
93
+ previewToken: text("preview_token"),
78
94
  bridgeToken: text("bridge_token"),
79
95
  ownerEmail: text("owner_email").notNull(),
80
96
  orgId: text("org_id"),
@@ -0,0 +1,70 @@
1
+ const EDITOR_ROLES = new Set(["owner", "admin", "editor"]);
2
+
3
+ /**
4
+ * Public visibility is read-only. Ownership and explicit share grants are
5
+ * resolved separately by the framework, so they can still upgrade the caller
6
+ * to owner/admin/editor without making an anonymous public link writable.
7
+ */
8
+ export function publicDesignAccessRole(): "viewer" {
9
+ return "viewer";
10
+ }
11
+
12
+ function removeLocalhostCredentials(
13
+ value: unknown,
14
+ ancestors: WeakSet<object>,
15
+ allowPreviewToken: boolean,
16
+ ): unknown {
17
+ if (!value || typeof value !== "object") return value;
18
+ if (ancestors.has(value)) {
19
+ throw new Error("Design data contains a circular reference");
20
+ }
21
+
22
+ ancestors.add(value);
23
+ try {
24
+ if (Array.isArray(value)) {
25
+ return value.map((child) =>
26
+ removeLocalhostCredentials(child, ancestors, allowPreviewToken),
27
+ );
28
+ }
29
+
30
+ return Object.fromEntries(
31
+ Object.entries(value as Record<string, unknown>)
32
+ .filter(
33
+ ([key]) =>
34
+ key !== "bridgeToken" &&
35
+ (allowPreviewToken || key !== "previewToken"),
36
+ )
37
+ .map(([key, child]) => [
38
+ key,
39
+ removeLocalhostCredentials(child, ancestors, allowPreviewToken),
40
+ ]),
41
+ );
42
+ } finally {
43
+ ancestors.delete(value);
44
+ }
45
+ }
46
+
47
+ /**
48
+ * Filter persisted design data before sending it to a caller.
49
+ *
50
+ * Filesystem bridge tokens unlock privileged loopback endpoints and are never
51
+ * returned through design data, including to owners/editors. The separate
52
+ * read-only preview token is available only to callers with an editor role;
53
+ * anonymous/public viewers fail closed rather than gaining access to a local
54
+ * development server. Invalid persisted JSON also fails closed to null.
55
+ */
56
+ export function designDataForAccessRole(data: unknown, role: unknown): unknown {
57
+ const allowPreviewToken = typeof role === "string" && EDITOR_ROLES.has(role);
58
+
59
+ try {
60
+ if (typeof data === "string") {
61
+ const parsed = JSON.parse(data) as unknown;
62
+ return JSON.stringify(
63
+ removeLocalhostCredentials(parsed, new WeakSet(), allowPreviewToken),
64
+ );
65
+ }
66
+ return removeLocalhostCredentials(data, new WeakSet(), allowPreviewToken);
67
+ } catch {
68
+ return null;
69
+ }
70
+ }
@@ -0,0 +1,249 @@
1
+ import { assertAccess } from "@agent-native/core/sharing";
2
+ import { and, eq, isNull } from "drizzle-orm";
3
+
4
+ import { getDb, schema } from "../db/index.js";
5
+
6
+ const DEFAULT_MAX_ATTEMPTS = 8;
7
+ const CONFLICT_BACKOFF_MS = 8;
8
+ const designDataLocks = new Map<string, Promise<unknown>>();
9
+
10
+ export type DesignDataRecord = Record<string, unknown>;
11
+
12
+ export class DesignDataMutationConflictError extends Error {
13
+ constructor(designId: string, attempts: number) {
14
+ super(
15
+ `Failed to update design "${designId}" after ${attempts} concurrent write conflicts. Please retry.`,
16
+ );
17
+ this.name = "DesignDataMutationConflictError";
18
+ }
19
+ }
20
+
21
+ export class InvalidDesignDataError extends Error {
22
+ constructor(designId: string) {
23
+ super(
24
+ `Design "${designId}" has invalid data JSON. Refusing to overwrite it; repair or restore the design data before retrying.`,
25
+ );
26
+ this.name = "InvalidDesignDataError";
27
+ }
28
+ }
29
+
30
+ function parseDesignData(
31
+ designId: string,
32
+ serialized: string | null,
33
+ ): DesignDataRecord {
34
+ // A small number of legacy rows predate the current NOT NULL schema. Treat
35
+ // SQL NULL as the old empty-data sentinel, while still refusing malformed
36
+ // non-null JSON so a corrupt blob is never silently discarded.
37
+ if (serialized === null) return {};
38
+ try {
39
+ const parsed: unknown = JSON.parse(serialized);
40
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
41
+ return parsed as DesignDataRecord;
42
+ }
43
+ } catch {
44
+ // The dedicated error below explains why the write is refused.
45
+ }
46
+ throw new InvalidDesignDataError(designId);
47
+ }
48
+
49
+ function serializeDesignData(designId: string, data: DesignDataRecord): string {
50
+ if (!data || typeof data !== "object" || Array.isArray(data)) {
51
+ throw new InvalidDesignDataError(designId);
52
+ }
53
+ try {
54
+ const serialized = JSON.stringify(data);
55
+ if (typeof serialized === "string") return serialized;
56
+ } catch {
57
+ // The dedicated error below keeps circular/non-serializable transforms
58
+ // from turning into a partial or ambiguous write.
59
+ }
60
+ throw new InvalidDesignDataError(designId);
61
+ }
62
+
63
+ function nextUpdatedAt(current: string | null, now: Date): string {
64
+ const currentMs = current ? Date.parse(current) : Number.NaN;
65
+ const nextMs = Number.isFinite(currentMs)
66
+ ? Math.max(now.getTime(), currentMs + 1)
67
+ : now.getTime();
68
+ return new Date(nextMs).toISOString();
69
+ }
70
+
71
+ function conflictDelay(attempt: number): Promise<void> {
72
+ return new Promise((resolve) => {
73
+ setTimeout(resolve, Math.min(64, CONFLICT_BACKOFF_MS * (attempt + 1)));
74
+ });
75
+ }
76
+
77
+ function isRetryableTransactionConflict(error: unknown): boolean {
78
+ const code =
79
+ error && typeof error === "object" && "code" in error
80
+ ? String((error as { code?: unknown }).code ?? "")
81
+ : "";
82
+ return (
83
+ code === "SQLITE_BUSY" ||
84
+ code === "SQLITE_LOCKED" ||
85
+ code === "40001" || // Postgres serialization failure
86
+ code === "40P01" // Postgres deadlock detected
87
+ );
88
+ }
89
+
90
+ function withDesignDataLock<T>(
91
+ designId: string,
92
+ callback: () => Promise<T>,
93
+ ): Promise<T> {
94
+ const previous = designDataLocks.get(designId) ?? Promise.resolve();
95
+ const next = previous.then(callback, callback);
96
+ designDataLocks.set(designId, next);
97
+ const cleanup = () => {
98
+ if (designDataLocks.get(designId) === next) {
99
+ designDataLocks.delete(designId);
100
+ }
101
+ };
102
+ next.then(cleanup, cleanup);
103
+ return next;
104
+ }
105
+
106
+ interface MutateDesignDataOptions {
107
+ designId: string;
108
+ mutate: (
109
+ current: DesignDataRecord,
110
+ context: { updatedAt: string },
111
+ ) => DesignDataRecord;
112
+ /**
113
+ * Proves the caller's intent is present in the committed row. This is
114
+ * deliberately intent-based rather than whole-object equality: a sibling
115
+ * writer may safely add unrelated keys immediately after our commit.
116
+ */
117
+ isApplied: (persisted: DesignDataRecord) => boolean;
118
+ maxAttempts?: number;
119
+ now?: () => Date;
120
+ }
121
+
122
+ /**
123
+ * Atomically mutate the designs.data JSON record without losing sibling keys.
124
+ *
125
+ * The conditional UPDATE is portable Drizzle query-builder SQL (no RETURNING,
126
+ * dialect-specific JSON operator, or driver result-shape assumption). The
127
+ * read, compare-and-swap, and confirmation read live in one transaction. A
128
+ * post-commit read then proves the requested intent survived before success is
129
+ * reported. Explicit property deletion performed by `mutate` is preserved
130
+ * because the complete transformed object is the CAS candidate.
131
+ *
132
+ * Isolation assumptions: local better-sqlite3 transactions use the framework's
133
+ * BEGIN IMMEDIATE + top-level queue, so no sibling writer can enter between
134
+ * the read and CAS. Postgres may let a sibling commit after the read, but its
135
+ * conditional UPDATE is re-evaluated after the row-lock wait; the confirmation
136
+ * read detects a lost CAS and triggers a retry. Other supported drivers get the
137
+ * same guarded predicate plus confirmation/post-commit verification rather
138
+ * than relying on a driver-specific affected-row result.
139
+ */
140
+ async function mutateDesignDataUnlocked({
141
+ designId,
142
+ mutate,
143
+ isApplied,
144
+ maxAttempts = DEFAULT_MAX_ATTEMPTS,
145
+ now = () => new Date(),
146
+ }: MutateDesignDataOptions): Promise<{
147
+ data: DesignDataRecord;
148
+ updatedAt: string;
149
+ }> {
150
+ // Re-assert at the shared write boundary. Callers also check before doing
151
+ // parse/index work so unauthorized requests fail early, but this helper must
152
+ // remain independently scoped if a new action adopts it later.
153
+ await assertAccess("design", designId, "editor");
154
+ const db = getDb();
155
+
156
+ for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
157
+ let committed: { data: DesignDataRecord; updatedAt: string } | undefined;
158
+
159
+ try {
160
+ committed = await db.transaction(async (tx) => {
161
+ const [currentRow] = await tx
162
+ .select({
163
+ data: schema.designs.data,
164
+ updatedAt: schema.designs.updatedAt,
165
+ })
166
+ .from(schema.designs)
167
+ .where(eq(schema.designs.id, designId));
168
+
169
+ if (!currentRow) {
170
+ throw new Error(`Design "${designId}" not found.`);
171
+ }
172
+
173
+ const updatedAt = nextUpdatedAt(currentRow.updatedAt, now());
174
+ const currentData = parseDesignData(designId, currentRow.data);
175
+ const nextData = mutate(currentData, { updatedAt });
176
+ const nextSerialized = serializeDesignData(designId, nextData);
177
+ const revisionConditions = [
178
+ eq(schema.designs.id, designId),
179
+ currentRow.data === null
180
+ ? isNull(schema.designs.data)
181
+ : eq(schema.designs.data, currentRow.data),
182
+ ];
183
+ revisionConditions.push(
184
+ currentRow.updatedAt === null
185
+ ? isNull(schema.designs.updatedAt)
186
+ : eq(schema.designs.updatedAt, currentRow.updatedAt),
187
+ );
188
+
189
+ await tx
190
+ .update(schema.designs)
191
+ .set({ data: nextSerialized, updatedAt })
192
+ .where(and(...revisionConditions));
193
+
194
+ // Avoid driver-specific rowsAffected/rowCount/RETURNING contracts.
195
+ // Inside the same transaction the row remains locked after a winning
196
+ // UPDATE, so exact equality proves this CAS attempt wrote its candidate.
197
+ const [confirmed] = await tx
198
+ .select({ data: schema.designs.data })
199
+ .from(schema.designs)
200
+ .where(eq(schema.designs.id, designId));
201
+
202
+ if (!confirmed) {
203
+ throw new Error(`Design "${designId}" not found after update.`);
204
+ }
205
+ if (confirmed.data !== nextSerialized) {
206
+ throw new DesignDataMutationConflictError(designId, attempt + 1);
207
+ }
208
+
209
+ return { data: nextData, updatedAt };
210
+ });
211
+ } catch (error) {
212
+ if (
213
+ !(error instanceof DesignDataMutationConflictError) &&
214
+ !isRetryableTransactionConflict(error)
215
+ ) {
216
+ throw error;
217
+ }
218
+ }
219
+
220
+ if (committed) {
221
+ const [persistedRow] = await db
222
+ .select({ data: schema.designs.data })
223
+ .from(schema.designs)
224
+ .where(eq(schema.designs.id, designId));
225
+ if (!persistedRow) {
226
+ throw new Error(`Design "${designId}" not found after commit.`);
227
+ }
228
+ const persistedData = parseDesignData(designId, persistedRow.data);
229
+ if (isApplied(persistedData)) {
230
+ return { data: persistedData, updatedAt: committed.updatedAt };
231
+ }
232
+ }
233
+
234
+ if (attempt < maxAttempts - 1) await conflictDelay(attempt);
235
+ }
236
+
237
+ throw new DesignDataMutationConflictError(designId, maxAttempts);
238
+ }
239
+
240
+ export function mutateDesignData(
241
+ options: MutateDesignDataOptions,
242
+ ): Promise<{ data: DesignDataRecord; updatedAt: string }> {
243
+ // Serialize same-process calls before entering a backend transaction. This
244
+ // avoids overlapping libSQL/SQLite transactions on one client; the CAS
245
+ // remains necessary for multi-instance and cross-process writers.
246
+ return withDesignDataLock(options.designId, () =>
247
+ mutateDesignDataUnlocked(options),
248
+ );
249
+ }
@@ -29,8 +29,8 @@ import {
29
29
  parseCanvasFrameGeometryById,
30
30
  type CanvasFramePlacement,
31
31
  } from "../../shared/canvas-frames.js";
32
- import { parseDesignDataBlob } from "../../shared/full-app.js";
33
32
  import { getDb, schema } from "../db/index.js";
33
+ import { mutateDesignData } from "./design-data-mutation.js";
34
34
 
35
35
  /** Default iframe viewport, mirroring add-localhost-screens' defaults. */
36
36
  export const DEFAULT_FUSION_SCREEN_WIDTH = 1280;
@@ -117,18 +117,6 @@ export async function upsertFusionScreens(args: {
117
117
  }
118
118
 
119
119
  const db = getDb();
120
- const [design] = await db
121
- .select({ data: schema.designs.data })
122
- .from(schema.designs)
123
- .where(eq(schema.designs.id, designId))
124
- .limit(1);
125
- const prevData = parseDesignDataBlob(design?.data);
126
- const existingCanvasFrames = parseCanvasFrameGeometryById(
127
- prevData.canvasFrames,
128
- );
129
- const existingMetadata = isRecord(prevData.screenMetadata)
130
- ? { ...(prevData.screenMetadata as Record<string, unknown>) }
131
- : {};
132
120
  const existingFiles = await db
133
121
  .select()
134
122
  .from(schema.designFiles)
@@ -140,22 +128,6 @@ export async function upsertFusionScreens(args: {
140
128
  const now = new Date().toISOString();
141
129
 
142
130
  const results: FusionScreenResult[] = [];
143
- const placements: CanvasFramePlacement[] = [];
144
-
145
- // Only place screens that don't already have canvas geometry so re-syncs
146
- // never reset positions the user has arranged. New screens start after the
147
- // right-most existing frame.
148
- const existingGeometries = Object.values(existingCanvasFrames);
149
- let nextX = startX;
150
- if (existingGeometries.length > 0) {
151
- nextX =
152
- Math.max(
153
- startX,
154
- ...existingGeometries.map(
155
- (frame) => (frame.x ?? 0) + (frame.width ?? width),
156
- ),
157
- ) + gap;
158
- }
159
131
 
160
132
  for (let index = 0; index < paths.length; index += 1) {
161
133
  const path = paths[index]!;
@@ -190,57 +162,98 @@ export async function upsertFusionScreens(args: {
190
162
  }
191
163
 
192
164
  results.push({ fileId, filename, path, url, title, width, height });
193
- if (!existingCanvasFrames[fileId]) {
194
- placements.push({
195
- fileId,
196
- filename,
197
- x: nextX,
198
- y: startY,
199
- width,
200
- height,
201
- z: placements.length,
202
- });
203
- nextX += width + gap;
204
- }
205
165
  }
206
166
 
207
- const mergedFrames = mergeCanvasFramePlacements({
208
- existing: prevData.canvasFrames,
209
- placements,
210
- resolveFileId: (placement) => placement.fileId,
211
- });
167
+ let placedFrames: Array<{
168
+ fileId: string;
169
+ filename?: string;
170
+ frame: CanvasFramePlacement;
171
+ }> = [];
172
+ await mutateDesignData({
173
+ designId,
174
+ mutate: (current, { updatedAt }) => {
175
+ const currentFrames = parseCanvasFrameGeometryById(current.canvasFrames);
176
+ const existingGeometries = Object.values(currentFrames);
177
+ let nextX = startX;
178
+ if (existingGeometries.length > 0) {
179
+ nextX =
180
+ Math.max(
181
+ startX,
182
+ ...existingGeometries.map(
183
+ (frame) => (frame.x ?? 0) + (frame.width ?? width),
184
+ ),
185
+ ) + gap;
186
+ }
212
187
 
213
- for (const screen of results) {
214
- // Preserve user-adjusted title/dimensions on refresh; only the URL keys
215
- // must always track the current preview URL.
216
- const prevMeta = isRecord(existingMetadata[screen.fileId])
217
- ? (existingMetadata[screen.fileId] as Record<string, unknown>)
218
- : {};
219
- existingMetadata[screen.fileId] = {
220
- ...prevMeta,
221
- sourceType: "fusion",
222
- previewState: "live",
223
- title: prevMeta.title ?? screen.title,
224
- width: prevMeta.width ?? screen.width,
225
- height: prevMeta.height ?? screen.height,
226
- url: screen.url,
227
- previewUrl: screen.url,
228
- path: screen.path,
229
- };
230
- }
188
+ const placements: CanvasFramePlacement[] = [];
189
+ for (const screen of results) {
190
+ if (currentFrames[screen.fileId]) continue;
191
+ placements.push({
192
+ fileId: screen.fileId,
193
+ filename: screen.filename,
194
+ x: nextX,
195
+ y: startY,
196
+ width,
197
+ height,
198
+ z: placements.length,
199
+ });
200
+ nextX += width + gap;
201
+ }
202
+ const mergedFrames = mergeCanvasFramePlacements({
203
+ existing: current.canvasFrames,
204
+ placements,
205
+ resolveFileId: (placement) => placement.fileId,
206
+ });
207
+ placedFrames = mergedFrames.placedFrames;
231
208
 
232
- await db
233
- .update(schema.designs)
234
- .set({
235
- data: JSON.stringify({
236
- ...prevData,
209
+ const metadata = isRecord(current.screenMetadata)
210
+ ? { ...current.screenMetadata }
211
+ : {};
212
+ for (const screen of results) {
213
+ // Preserve user-adjusted title/dimensions on refresh; only URL-backed
214
+ // source fields track the current container preview.
215
+ const candidate = metadata[screen.fileId];
216
+ const previous: Record<string, unknown> = isRecord(candidate)
217
+ ? candidate
218
+ : {};
219
+ metadata[screen.fileId] = {
220
+ ...previous,
221
+ sourceType: "fusion",
222
+ previewState: "live",
223
+ title: previous.title ?? screen.title,
224
+ width: previous.width ?? screen.width,
225
+ height: previous.height ?? screen.height,
226
+ url: screen.url,
227
+ previewUrl: screen.url,
228
+ path: screen.path,
229
+ };
230
+ }
231
+
232
+ return {
233
+ ...current,
237
234
  canvasFrames: mergedFrames.canvasFrames,
238
- screenMetadata: existingMetadata,
239
- updatedAt: now,
240
- }),
241
- updatedAt: now,
242
- })
243
- .where(eq(schema.designs.id, designId));
235
+ screenMetadata: metadata,
236
+ updatedAt,
237
+ };
238
+ },
239
+ isApplied: (current) => {
240
+ const frames = parseCanvasFrameGeometryById(current.canvasFrames);
241
+ const metadata = isRecord(current.screenMetadata)
242
+ ? current.screenMetadata
243
+ : {};
244
+ return results.every((screen) => {
245
+ const entry = metadata[screen.fileId];
246
+ return (
247
+ Boolean(frames[screen.fileId]) &&
248
+ isRecord(entry) &&
249
+ entry.sourceType === "fusion" &&
250
+ entry.url === screen.url &&
251
+ entry.previewUrl === screen.url &&
252
+ entry.path === screen.path
253
+ );
254
+ });
255
+ },
256
+ });
244
257
 
245
- return { screens: results, placedFrames: mergedFrames.placedFrames };
258
+ return { screens: results, placedFrames };
246
259
  }