@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
@@ -477,6 +477,18 @@ function DocumentEditorBody({ documentId, document }: DocumentEditorBodyProps) {
477
477
  canEdit && !bodyHydrationPending && (isLocalFileDocument || !collabLoading);
478
478
  canEditRef.current = editorCanEdit;
479
479
 
480
+ // Viewers intentionally join awareness so they receive live cursors, but
481
+ // only an editor runs the app-state flush poller below. Publish that exact
482
+ // capability so server-side pull/push/conflict actions do not wait on a
483
+ // read-only tab that can never acknowledge their request.
484
+ useEffect(() => {
485
+ if (!awareness || !collabEnabled) return;
486
+ awareness.setLocalStateField("canFlushDocument", editorCanEdit);
487
+ return () => {
488
+ awareness.setLocalStateField("canFlushDocument", false);
489
+ };
490
+ }, [awareness, collabEnabled, editorCanEdit]);
491
+
480
492
  // Initialize from fetched document, reset on document switch
481
493
  useEffect(() => {
482
494
  if (!document) return;
@@ -813,6 +825,10 @@ function DocumentEditorBody({ documentId, document }: DocumentEditorBodyProps) {
813
825
  try {
814
826
  const next = await pushDocumentToNotion.mutateAsync({
815
827
  documentId,
828
+ // The exact editor value was persisted immediately above. Avoid
829
+ // a redundant live-editor flush handshake on every auto-sync
830
+ // save; manual pushes/conflict choices keep the safe default.
831
+ flushOpenEditor: false,
816
832
  });
817
833
  queryClient.setQueryData(
818
834
  documentSyncStatusQueryKey(documentId, { autoSync }),
@@ -1030,8 +1046,21 @@ function DocumentEditorBody({ documentId, document }: DocumentEditorBodyProps) {
1030
1046
  try {
1031
1047
  const res = await fetch(flushPath);
1032
1048
  if (res.ok) {
1033
- const pending = (await res.json()) as { id?: string } | null;
1049
+ const pending = (await res.json()) as {
1050
+ id?: string;
1051
+ ts?: number;
1052
+ requestId?: string;
1053
+ status?: "pending" | "success" | "error";
1054
+ error?: string;
1055
+ } | null;
1034
1056
  if (pending && active) {
1057
+ // A terminal acknowledgement waits for the requesting action to
1058
+ // read and clear it. Retrying here could hide a failed flush or
1059
+ // replace the explicit success signal before the server sees it.
1060
+ if (pending.status === "error" || pending.status === "success") {
1061
+ if (active) setTimeout(poll, 600);
1062
+ return;
1063
+ }
1035
1064
  const title = localTitleRef.current;
1036
1065
  const content = localContentRef.current;
1037
1066
  const updates: Record<string, string> = {};
@@ -1043,30 +1072,60 @@ function DocumentEditorBody({ documentId, document }: DocumentEditorBodyProps) {
1043
1072
  try {
1044
1073
  if (Object.keys(updates).length > 0) {
1045
1074
  const saved = await persistDocumentUpdates(updates);
1046
- // A CAS conflict here means a newer write landed between this
1047
- // editor's last reconcile and the flush; leave watermarks
1048
- // alone so the external-change effects reconcile this editor
1049
- // to the winning server content instead of us claiming the
1050
- // now-discarded flush content as saved.
1051
- if (!isDocumentUpdateConflict(saved)) {
1052
- const savedAt = saved?.updatedAt ?? new Date().toISOString();
1053
- adoptConfirmedSaveWatermarks({
1054
- saved,
1055
- savedAt,
1056
- title,
1057
- content,
1058
- updates,
1059
- lastSavedTitleRef,
1060
- lastSavedContentRef,
1061
- });
1075
+ if (isDocumentUpdateConflict(saved)) {
1076
+ // Do not acknowledge a CAS loss as a successful flush. The
1077
+ // requester must stop instead of pushing/replacing stale SQL.
1078
+ throw new Error(
1079
+ "The document changed while preparing it for sync.",
1080
+ );
1062
1081
  }
1082
+ const savedAt = saved?.updatedAt ?? new Date().toISOString();
1083
+ adoptConfirmedSaveWatermarks({
1084
+ saved,
1085
+ savedAt,
1086
+ title,
1087
+ content,
1088
+ updates,
1089
+ lastSavedTitleRef,
1090
+ lastSavedContentRef,
1091
+ });
1063
1092
  }
1064
- } finally {
1065
- // Acknowledge the flush even if nothing changed the SQL row is
1066
- // already current, and pull-document is waiting on this delete.
1093
+ // Explicitly acknowledge this exact request only after the live
1094
+ // editor state is confirmed in SQL (or nothing needed saving).
1095
+ // A delete is ambiguous with a transient app-state read failure.
1067
1096
  await fetch(flushPath, {
1068
- method: "DELETE",
1069
- headers: { "X-Agent-Native-CSRF": "1" },
1097
+ method: "PUT",
1098
+ headers: {
1099
+ "Content-Type": "application/json",
1100
+ "X-Agent-Native-CSRF": "1",
1101
+ },
1102
+ body: JSON.stringify({
1103
+ id: pending.id ?? documentId,
1104
+ ts: pending.ts ?? Date.now(),
1105
+ requestId: pending.requestId,
1106
+ status: "success",
1107
+ }),
1108
+ }).catch(() => {});
1109
+ } catch (error) {
1110
+ // Keep a durable negative acknowledgement so the requesting
1111
+ // Notion action can fail closed instead of timing out and using a
1112
+ // stale documents row. The server clears this after reading it.
1113
+ await fetch(flushPath, {
1114
+ method: "PUT",
1115
+ headers: {
1116
+ "Content-Type": "application/json",
1117
+ "X-Agent-Native-CSRF": "1",
1118
+ },
1119
+ body: JSON.stringify({
1120
+ id: pending.id ?? documentId,
1121
+ ts: pending.ts ?? Date.now(),
1122
+ requestId: pending.requestId,
1123
+ status: "error",
1124
+ error:
1125
+ error instanceof Error
1126
+ ? error.message
1127
+ : t("editor.liveDocumentSaveBeforeSyncFailed"),
1128
+ }),
1070
1129
  }).catch(() => {});
1071
1130
  }
1072
1131
  }
@@ -1082,7 +1141,13 @@ function DocumentEditorBody({ documentId, document }: DocumentEditorBodyProps) {
1082
1141
  active = false;
1083
1142
  clearTimeout(timer);
1084
1143
  };
1085
- }, [documentId, editorCanEdit, isLocalFileDocument, persistDocumentUpdates]);
1144
+ }, [
1145
+ documentId,
1146
+ editorCanEdit,
1147
+ isLocalFileDocument,
1148
+ persistDocumentUpdates,
1149
+ t,
1150
+ ]);
1086
1151
 
1087
1152
  const handleTitleChange = useCallback(
1088
1153
  (newTitle: string) => {
@@ -822,6 +822,28 @@ export function shouldSeedCollaborativeContent({
822
822
  return !!content.trim() && (fragmentLength === 0 || !semanticMarkdown);
823
823
  }
824
824
 
825
+ /**
826
+ * Parse authoritative Content NFM with Content's exact NFM parser before the
827
+ * shared reconcile computes its top-level surgical diff.
828
+ *
829
+ * Falling back to the shared CommonMark parser is lossy here: canonical NFM
830
+ * stores one Notion block per line without blank paragraph separators, while
831
+ * CommonMark merges those consecutive lines into one paragraph. That made
832
+ * external replacements such as Notion conflict resolution and version
833
+ * restores look correct in the non-collaborative history preview, then collapse
834
+ * into one wrapped paragraph when reconciled into the live Y.Doc.
835
+ */
836
+ export function parseNfmForCollabReconcile(
837
+ editor: CoreEditor,
838
+ value: string,
839
+ ): ProseMirrorNode | null {
840
+ try {
841
+ return editor.schema.nodeFromJSON(nfmToDoc(value) as any);
842
+ } catch {
843
+ return null;
844
+ }
845
+ }
846
+
825
847
  export function shouldApplyExternalContentSync({
826
848
  docChanged,
827
849
  content,
@@ -1669,10 +1691,14 @@ export function VisualEditor({
1669
1691
  // Clean up awareness on unmount
1670
1692
  useEffect(() => {
1671
1693
  return () => {
1672
- localAwareness?.setLocalState(null);
1694
+ // Only the fallback instance is owned by this editor. A provided
1695
+ // awareness belongs to the shared useCollaborativeDoc connection; clearing
1696
+ // it here races StrictMode/remounts and can erase the tab's durable
1697
+ // presence while the shared connection is still active.
1698
+ fallbackAwareness?.setLocalState(null);
1673
1699
  fallbackAwareness?.destroy();
1674
1700
  };
1675
- }, [fallbackAwareness, localAwareness]);
1701
+ }, [fallbackAwareness]);
1676
1702
 
1677
1703
  const extensions = useMemo(
1678
1704
  () =>
@@ -1941,6 +1967,10 @@ export function VisualEditor({
1941
1967
  e.commands.setContent(doc);
1942
1968
  },
1943
1969
  normalizeValue: canonicalizeNfm,
1970
+ // The shared fallback parser is CommonMark. Content stores canonical NFM,
1971
+ // whose adjacent lines are separate Notion blocks, so always provide the
1972
+ // exact NFM parser for the surgical reconcile path.
1973
+ parseValue: parseNfmForCollabReconcile,
1944
1974
  shouldSeed: ({ value, currentMarkdown, fragmentLength }) =>
1945
1975
  editable &&
1946
1976
  shouldSeedCollaborativeContent({
@@ -253,12 +253,12 @@ export function usePullDocumentFromNotion(documentId: string) {
253
253
 
254
254
  export function usePushDocumentToNotion(documentId: string) {
255
255
  const queryClient = useQueryClient();
256
- return useActionMutation<DocumentSyncStatus, { documentId: string }>(
257
- "push-notion-page",
258
- {
259
- onSuccess: () => invalidateDocumentQueries(queryClient, documentId),
260
- },
261
- );
256
+ return useActionMutation<
257
+ DocumentSyncStatus,
258
+ { documentId: string; flushOpenEditor?: boolean }
259
+ >("push-notion-page", {
260
+ onSuccess: () => invalidateDocumentQueries(queryClient, documentId),
261
+ });
262
262
  }
263
263
 
264
264
  export function useResolveDocumentSyncConflict(documentId: string) {
@@ -94,6 +94,7 @@ const messages = {
94
94
  couldNotReadLocalSourceFile: "無法讀取本機來源檔案",
95
95
  couldNotSaveLocalFile: "無法儲存本機檔案",
96
96
  collabConnectingReadOnly: "正在連接即時編輯器。顯示唯讀快照。",
97
+ liveDocumentSaveBeforeSyncFailed: "即時文件無法在同步前儲存。",
97
98
  documentTitle: "檔案標題",
98
99
  builderBodySyncing: "內容仍在從 Builder 同步",
99
100
  builderBodySyncingDescription:
@@ -2463,6 +2463,8 @@ const enUS = {
2463
2463
  couldNotSaveLocalFile: "Could not save local file",
2464
2464
  collabConnectingReadOnly:
2465
2465
  "Connecting live editor. Showing a read-only snapshot.",
2466
+ liveDocumentSaveBeforeSyncFailed:
2467
+ "The live document could not be saved before syncing.",
2466
2468
  documentTitle: "Document title",
2467
2469
  builderBodySyncing: "Content is still syncing from Builder",
2468
2470
  builderBodySyncingDescription:
@@ -4843,6 +4845,7 @@ const editorMessagesByLocale = {
4843
4845
  "zh-CN": {
4844
4846
  noDocumentSelected: "未选择文档",
4845
4847
  collabConnectingReadOnly: "正在连接实时编辑器。显示只读快照。",
4848
+ liveDocumentSaveBeforeSyncFailed: "实时文档无法在同步前保存。",
4846
4849
  builderBodySyncing: "内容仍在从 Builder 同步",
4847
4850
  builderBodySyncingDescription:
4848
4851
  "同步 Builder 正文完成前会暂停编辑,避免覆盖现有文章内容。",
@@ -5169,6 +5172,8 @@ const editorMessagesByLocale = {
5169
5172
  noDocumentSelected: "Ningún documento seleccionado",
5170
5173
  collabConnectingReadOnly:
5171
5174
  "Conectando el editor en vivo. Mostrando una instantánea de solo lectura.",
5175
+ liveDocumentSaveBeforeSyncFailed:
5176
+ "No se pudo guardar el documento activo antes de sincronizarlo.",
5172
5177
  builderBodySyncing: "El contenido aún se está sincronizando desde Builder",
5173
5178
  builderBodySyncingDescription:
5174
5179
  "La edición está en pausa hasta que el cuerpo de Builder termine de sincronizarse, para no sobrescribir el contenido existente del artículo.",
@@ -5506,6 +5511,8 @@ const editorMessagesByLocale = {
5506
5511
  noDocumentSelected: "Aucun document sélectionné",
5507
5512
  collabConnectingReadOnly:
5508
5513
  "Connexion de l'éditeur en direct. Affichage d'un instantané en lecture seule.",
5514
+ liveDocumentSaveBeforeSyncFailed:
5515
+ "Le document actif n’a pas pu être enregistré avant la synchronisation.",
5509
5516
  builderBodySyncing:
5510
5517
  "Le contenu est encore en cours de synchronisation depuis Builder",
5511
5518
  builderBodySyncingDescription:
@@ -5846,6 +5853,8 @@ const editorMessagesByLocale = {
5846
5853
  noDocumentSelected: "Kein Dokument ausgewählt",
5847
5854
  collabConnectingReadOnly:
5848
5855
  "Live-Editor wird verbunden. Schreibgeschützte Momentaufnahme wird angezeigt.",
5856
+ liveDocumentSaveBeforeSyncFailed:
5857
+ "Das aktive Dokument konnte vor der Synchronisierung nicht gespeichert werden.",
5849
5858
  builderBodySyncing: "Inhalte werden noch von Builder synchronisiert",
5850
5859
  builderBodySyncingDescription:
5851
5860
  "Die Bearbeitung ist pausiert, bis der Builder-Textkörper fertig synchronisiert ist, damit der bestehende Artikelinhalt nicht überschrieben wird.",
@@ -6190,6 +6199,8 @@ const editorMessagesByLocale = {
6190
6199
  noDocumentSelected: "ドキュメントが選択されていません",
6191
6200
  collabConnectingReadOnly:
6192
6201
  "ライブエディターに接続中。読み取り専用のスナップショットを表示しています。",
6202
+ liveDocumentSaveBeforeSyncFailed:
6203
+ "同期前にライブドキュメントを保存できませんでした。",
6193
6204
  builderBodySyncing: "コンテンツはまだ Builder から同期中です",
6194
6205
  builderBodySyncingDescription:
6195
6206
  "既存の記事内容を上書きしないよう、Builder 本文の同期が完了するまで編集は一時停止されます。",
@@ -6524,6 +6535,8 @@ const editorMessagesByLocale = {
6524
6535
  noDocumentSelected: "선택한 문서가 없습니다.",
6525
6536
  collabConnectingReadOnly:
6526
6537
  "라이브 편집기에 연결하는 중입니다. 읽기 전용 스냅샷을 표시합니다.",
6538
+ liveDocumentSaveBeforeSyncFailed:
6539
+ "동기화하기 전에 실시간 문서를 저장하지 못했습니다.",
6527
6540
  builderBodySyncing: "콘텐츠가 아직 Builder에서 동기화되는 중입니다",
6528
6541
  builderBodySyncingDescription:
6529
6542
  "기존 문서 내용을 덮어쓰지 않도록 Builder 본문 동기화가 완료될 때까지 편집이 일시 중지됩니다.",
@@ -6857,6 +6870,8 @@ const editorMessagesByLocale = {
6857
6870
  noDocumentSelected: "Nenhum documento selecionado",
6858
6871
  collabConnectingReadOnly:
6859
6872
  "Conectando o editor ao vivo. Exibindo um instantâneo somente leitura.",
6873
+ liveDocumentSaveBeforeSyncFailed:
6874
+ "Não foi possível salvar o documento ativo antes da sincronização.",
6860
6875
  builderBodySyncing: "O conteúdo ainda está sincronizando do Builder",
6861
6876
  builderBodySyncingDescription:
6862
6877
  "A edição fica pausada até o corpo do Builder terminar de sincronizar, para não sobrescrever o conteúdo existente do artigo.",
@@ -7196,6 +7211,8 @@ const editorMessagesByLocale = {
7196
7211
  noDocumentSelected: "कोई दस्तावेज़ चयनित नहीं",
7197
7212
  collabConnectingReadOnly:
7198
7213
  "लाइव संपादक कनेक्ट हो रहा है। केवल-पठन स्नैपशॉट दिखाया जा रहा है।",
7214
+ liveDocumentSaveBeforeSyncFailed:
7215
+ "सिंक करने से पहले लाइव दस्तावेज़ सहेजा नहीं जा सका।",
7199
7216
  builderBodySyncing: "सामग्री अभी भी Builder से सिंक हो रही है",
7200
7217
  builderBodySyncingDescription:
7201
7218
  "Builder का मुख्य भाग सिंक पूरा होने तक संपादन रोका गया है, ताकि मौजूदा लेख सामग्री अधिलेखित न हो।",
@@ -7525,6 +7542,7 @@ const editorMessagesByLocale = {
7525
7542
  noDocumentSelected: "لم يتم تحديد أي مستند",
7526
7543
  collabConnectingReadOnly:
7527
7544
  "جارٍ الاتصال بالمحرر المباشر. يتم عرض لقطة للقراءة فقط.",
7545
+ liveDocumentSaveBeforeSyncFailed: "تعذّر حفظ المستند المباشر قبل المزامنة.",
7528
7546
  builderBodySyncing: "لا يزال المحتوى قيد المزامنة من Builder",
7529
7547
  builderBodySyncingDescription:
7530
7548
  "يتم إيقاف التحرير مؤقتًا حتى تكتمل مزامنة نص Builder، حتى لا يتم استبدال محتوى المقالة الحالي.",
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-09
4
+ ---
5
+
6
+ Builder source refreshes now keep mapped fields such as topics and tags, preserve existing rows when a read unexpectedly returns empty, and load large databases more reliably.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-09
4
+ ---
5
+
6
+ Notion conflict choices and version restores preserve document paragraphs and protect live edits.
@@ -2,7 +2,6 @@
2
2
  // in pnpm's node_modules. Logic is correct; types just don't unify across instances.
3
3
  import crypto from "node:crypto";
4
4
 
5
- import { deleteCollabState, releaseDoc } from "@agent-native/core/collab";
6
5
  import { and, eq, inArray, isNull, lt, or } from "drizzle-orm";
7
6
  import type { InferSelectModel } from "drizzle-orm";
8
7
 
@@ -37,6 +36,56 @@ function nanoid(size = 12): string {
37
36
  return Array.from(bytes, (byte) => chars[byte % chars.length]).join("");
38
37
  }
39
38
 
39
+ async function replaceDocumentFromExternal(args: {
40
+ document: DocumentRow;
41
+ expectedUpdatedAt: string;
42
+ title: string;
43
+ content: string;
44
+ icon: string | null;
45
+ updatedAt: string;
46
+ }): Promise<boolean> {
47
+ const db = getDb();
48
+ return db.transaction(async (tx) => {
49
+ const applied = await tx
50
+ .update(schema.documents)
51
+ .set({
52
+ title: args.title,
53
+ content: args.content,
54
+ icon: args.icon,
55
+ updatedAt: args.updatedAt,
56
+ })
57
+ .where(
58
+ and(
59
+ eq(schema.documents.id, args.document.id),
60
+ eq(schema.documents.ownerEmail, args.document.ownerEmail),
61
+ eq(schema.documents.updatedAt, args.expectedUpdatedAt),
62
+ ),
63
+ )
64
+ .returning({ id: schema.documents.id });
65
+
66
+ if (!applied || applied.length === 0) return false;
67
+
68
+ if (
69
+ args.title !== args.document.title ||
70
+ args.content !== args.document.content
71
+ ) {
72
+ // Keep the recovery snapshot in the same transaction as the replacement:
73
+ // a lost CAS creates no phantom version, and a snapshot failure rolls the
74
+ // destructive replacement back instead of leaving it unrecoverable.
75
+ await tx.insert(schema.documentVersions).values({
76
+ id: nanoid(),
77
+ ownerEmail: args.document.ownerEmail,
78
+ documentId: args.document.id,
79
+ title: args.document.title,
80
+ content: args.document.content,
81
+ createdAt: nowIso(),
82
+ });
83
+ }
84
+
85
+ return true;
86
+ });
87
+ }
88
+
40
89
  /**
41
90
  * Hash of the canonical content. Two documents with the same hash are
42
91
  * byte-identical once canonicalized, so this is the authoritative "did the
@@ -827,8 +876,6 @@ async function pullDocumentFromNotionInner(
827
876
  remotePageDocumentIdByPageId?: RemotePageDocumentLookup;
828
877
  },
829
878
  ): Promise<DocumentSyncStatus> {
830
- const db = getDb();
831
- const document = await getDocument(documentId, owner);
832
879
  const link = await getSyncLink(documentId, owner);
833
880
  if (!link) throw new Error("Document is not linked to a Notion page.");
834
881
  const connection = await getNotionConnectionForOwner(owner);
@@ -935,28 +982,18 @@ async function pullDocumentFromNotionInner(
935
982
  // check will mistake the unchanged document for a fresh local edit.
936
983
  const updatedAt = contentChanged ? nowIso() : freshDocument.updatedAt;
937
984
  if (contentChanged) {
938
- // Compare-and-swap: only apply the pulled content if the row is still at
939
- // the snapshot we just re-read. If a concurrent save landed in between,
940
- // 0 rows match and we fall through to the conflict path instead of
941
- // clobbering the newer local write.
942
- const applied = await db
943
- .update(schema.documents)
944
- .set({
945
- title: newTitle,
946
- content: newContent,
947
- icon: newIcon,
948
- updatedAt,
949
- })
950
- .where(
951
- and(
952
- eq(schema.documents.id, documentId),
953
- eq(schema.documents.ownerEmail, owner),
954
- eq(schema.documents.updatedAt, freshDocument.updatedAt),
955
- ),
956
- )
957
- .returning({ id: schema.documents.id });
985
+ // Snapshot + compare-and-swap are one transaction: only the winning
986
+ // replacement gets a recovery version, and snapshot failure rolls it back.
987
+ const applied = await replaceDocumentFromExternal({
988
+ document: freshDocument,
989
+ expectedUpdatedAt: freshDocument.updatedAt,
990
+ title: newTitle,
991
+ content: newContent,
992
+ icon: newIcon,
993
+ updatedAt,
994
+ });
958
995
 
959
- if (!applied || applied.length === 0) {
996
+ if (!applied) {
960
997
  // A newer local save raced in after our re-read. Do not adopt the
961
998
  // pulled content or advance the hash baseline — surface a conflict so
962
999
  // the user resolves it explicitly instead of silently losing the edit.
@@ -986,16 +1023,10 @@ async function pullDocumentFromNotionInner(
986
1023
  });
987
1024
  }
988
1025
 
989
- // Reset the Yjs collaborative state so it no longer holds the pre-sync
990
- // content. Connected clients re-seed their Y.XmlFragment from the new
991
- // `documents.content` value via VisualEditor's content-sync effect, and
992
- // a fresh page load starts from an empty server state and seeds from SQL.
993
- try {
994
- await deleteCollabState(documentId);
995
- releaseDoc(documentId);
996
- } catch {
997
- // Non-fatal — the client-side sync will still reconcile via setContent.
998
- }
1026
+ // Keep the Y.Doc intact. The live editor's updatedAt-gated reconcile applies
1027
+ // this authoritative SQL snapshot as a minimal Yjs transaction. Deleting
1028
+ // collab state here races connected clients (which can re-persist the stale
1029
+ // pre-pull state) and briefly makes later flush handshakes miss the editor.
999
1030
  }
1000
1031
 
1001
1032
  await upsertSyncLink({
@@ -1193,7 +1224,6 @@ async function pushDocumentToNotionInner(
1193
1224
  // the rare case Notion normalizes a construct differently, immediately
1194
1225
  // converging instead of ping-ponging. Re-read the row first so a local save
1195
1226
  // that landed during the multi-round-trip push isn't clobbered below.
1196
- const db = getDb();
1197
1227
  const freshDocument = await getDocument(documentId, owner);
1198
1228
  const newContent = remote.content ?? document.content;
1199
1229
  const newTitle = remote.title || document.title;
@@ -1211,39 +1241,25 @@ async function pushDocumentToNotionInner(
1211
1241
  // exactly what we pushed, since contentChanged is false).
1212
1242
  let baselineContent = document.content;
1213
1243
  if (contentChanged) {
1214
- // Compare-and-swap against the pre-push snapshot: if a concurrent save
1215
- // changed the row since `document` was read, skip adopting Notion's
1216
- // normalized content so the newer local edit is never overwritten. The
1217
- // hash baseline is still advanced to the *pushed* content below so the
1218
- // concurrent edit continues to show as localChanged and gets re-pushed on
1219
- // the next sync cycle.
1220
- const applied = await db
1221
- .update(schema.documents)
1222
- .set({
1223
- title: newTitle,
1224
- content: newContent,
1225
- icon: newIcon,
1226
- updatedAt: pushedAt,
1227
- })
1228
- .where(
1229
- and(
1230
- eq(schema.documents.id, documentId),
1231
- eq(schema.documents.ownerEmail, owner),
1232
- eq(schema.documents.updatedAt, document.updatedAt),
1233
- ),
1234
- )
1235
- .returning({ id: schema.documents.id });
1244
+ // Snapshot + compare-and-swap are atomic. If a concurrent save changed the
1245
+ // row since `document` was read, skip both the provider readback adoption
1246
+ // and its recovery version; the newer local edit remains localChanged.
1247
+ const applied = await replaceDocumentFromExternal({
1248
+ document: freshDocument,
1249
+ expectedUpdatedAt: document.updatedAt,
1250
+ title: newTitle,
1251
+ content: newContent,
1252
+ icon: newIcon,
1253
+ updatedAt: pushedAt,
1254
+ });
1236
1255
 
1237
- if (applied && applied.length > 0) {
1256
+ if (applied) {
1238
1257
  // The CAS landed — the row now holds Notion's normalized readback, so
1239
1258
  // the baseline must match that, not the pre-push content we sent.
1240
1259
  baselineContent = newContent;
1241
- try {
1242
- await deleteCollabState(documentId);
1243
- releaseDoc(documentId);
1244
- } catch {
1245
- // Non-fatal — the client reconciles via setContent.
1246
- }
1260
+ // Preserve the live Y.Doc and let the updatedAt-gated editor reconcile
1261
+ // apply this provider-normalized snapshot. Clearing collab persistence
1262
+ // here can race a connected client's stale update and resurrect it.
1247
1263
  }
1248
1264
  // else: CAS raced and lost — the row holds the concurrent edit's content,
1249
1265
  // not `newContent` and not `document.content`. Keep baselineContent as
@@ -618,6 +618,7 @@ export interface ContentDatabaseSource {
618
618
  lastReadPartial?: boolean;
619
619
  lastReadHasMore?: boolean;
620
620
  lastReadNextOffset?: number;
621
+ lastReadSuspiciousEmpty?: boolean;
621
622
  sourceFetchState?: "idle" | "fetching" | "error";
622
623
  allowDraftWrites?: boolean;
623
624
  allowPublishWrites?: boolean;
@@ -173,7 +173,7 @@ defaults. The banned-defaults list above still applies, plus:
173
173
 
174
174
  ## Generation Workflow — the canonical 5-phase flow
175
175
 
176
- This flow mirrors Claude Design's UX: ask → show variants → user picks → refine. Don't skip phases for new designs.
176
+ This flow mirrors Claude Design's UX: clarify only what's unclear → show variants → user picks → refine. Don't collapse phases into one shot for new, open-ended designs.
177
177
 
178
178
  ### Phase 1 — Create the project + ask before generating
179
179
 
@@ -187,7 +187,28 @@ application state. External MCP hosts should surface the `create-design`
187
187
  returned "Open design" link, then use `present-design-variants` to open the
188
188
  visual picker.
189
189
 
190
- Then, for any non-trivial first prompt, call `show-design-questions` BEFORE generating. The editor renders a full-canvas overlay; answers come back as a chat message. Skip the questions only when the prompt is unambiguous ("re-skin this with my brand colors") or the user said "decide for me".
190
+ Then, for a brief or ambiguous first prompt to a **new** design, call
191
+ `show-design-questions` BEFORE generating. The editor renders a full-canvas
192
+ overlay; answers come back as a chat message. Size the question count to how
193
+ much is actually unresolved — most prompts warrant 2-4 questions, not the full
194
+ 1-8 range — and never ask about something the prompt already specified (a
195
+ stated color, audience, or layout is settled; don't re-ask it as a choice).
196
+
197
+ Skip the questions entirely when:
198
+ - the prompt is already specific enough to generate from (names the audience,
199
+ purpose, and a visual direction, e.g. "a dark, data-dense analytics
200
+ dashboard for ops engineers" or "re-skin this with my brand colors");
201
+ - it's a tweak/edit/refinement to an existing design rather than a new one —
202
+ go straight to `edit-design` (see Phase 3 and "Making edits" below);
203
+ - the user already answered a question set for this design and is now
204
+ iterating — don't re-ask settled ground on follow-up prompts for the same
205
+ design; or
206
+ - the user says "decide for me," "surprise me," "just build it," or similar.
207
+
208
+ Asking on every prompt is as much a failure mode as never asking: a detailed
209
+ prompt that already answers the obvious questions should generate
210
+ immediately, and a design already in flight should not be interrupted with a
211
+ second questionnaire.
191
212
 
192
213
  ```bash
193
214
  pnpm action show-design-questions \
@@ -196,6 +217,12 @@ pnpm action show-design-questions \
196
217
  --questions '[{"id":"form_factor","type":"text-options","question":"What form factor?","options":[{"label":"Desktop web app","value":"desktop"},{"label":"Mobile app","value":"mobile"},{"label":"Both / responsive","value":"responsive"},{"label":"Decide for me","value":"decide"}],"allowOther":true}]'
197
218
  ```
198
219
 
220
+ Favor choice-first questions (2-5 concrete options, `allowOther: true`, and a
221
+ "Decide for me" option when any answer is acceptable) over open-ended
222
+ `freeform` text, and avoid `multiSelect` unless the question genuinely allows
223
+ combining multiple answers — stacking multi-select questions multiplies
224
+ follow-up ambiguity instead of resolving it.
225
+
199
226
  **Carry the form-factor answer through to generation — do not just ask and discard it.** A "Desktop web app" answer means the generated screen's canvas frame must be desktop-sized (~1440×1024), not left at whatever a screen with no placement falls back to. Map the answer to real frame geometry: pass `deviceType` (`"mobile"` / `"tablet"` / `"desktop"`) per screen to `generate-screens`, explicit `width`/`height` per variant to `present-design-variants`, or an explicit `canvasFrames` entry to `generate-design` — see Phase 2 and Phase 3 below. For "Both / responsive," generate at desktop width and rely on the responsive breakpoint system (see `responsive-breakpoints` skill) rather than guessing a size.
200
227
 
201
228
  ### Phase 2 — Generate side-by-side variations (2-5, three by default)