@agent-native/core 0.92.9 → 0.92.11

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 (345) 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 +181 -15
  5. package/corpus/core/src/agent/run-manager.ts +31 -4
  6. package/corpus/core/src/agent/run-store.ts +80 -0
  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.ts +165 -1
  12. package/corpus/core/src/client/chat/index.ts +9 -1
  13. package/corpus/core/src/client/extensions/EmbeddedExtension.tsx +38 -2
  14. package/corpus/core/src/client/index.ts +5 -0
  15. package/corpus/core/src/client/rich-markdown-editor/useCollabReconcile.ts +18 -7
  16. package/corpus/core/src/collab/awareness-store.ts +23 -12
  17. package/corpus/core/src/collab/client.ts +16 -5
  18. package/corpus/core/src/collab/index.ts +4 -0
  19. package/corpus/core/src/collab/recent-edits.ts +65 -2
  20. package/corpus/core/src/deploy/build.ts +133 -5
  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 +48 -4
  33. package/corpus/templates/clips/app/components/player/video-player.tsx +17 -12
  34. package/corpus/templates/clips/app/i18n/ar-SA.ts +1 -0
  35. package/corpus/templates/clips/app/i18n/de-DE.ts +1 -0
  36. package/corpus/templates/clips/app/i18n/en-US.ts +1 -0
  37. package/corpus/templates/clips/app/i18n/es-ES.ts +1 -0
  38. package/corpus/templates/clips/app/i18n/fr-FR.ts +1 -0
  39. package/corpus/templates/clips/app/i18n/hi-IN.ts +1 -0
  40. package/corpus/templates/clips/app/i18n/ja-JP.ts +1 -0
  41. package/corpus/templates/clips/app/i18n/ko-KR.ts +1 -0
  42. package/corpus/templates/clips/app/i18n/pt-BR.ts +1 -0
  43. package/corpus/templates/clips/app/i18n/zh-CN.ts +1 -0
  44. package/corpus/templates/clips/app/i18n/zh-TW.ts +1 -0
  45. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +70 -37
  46. package/corpus/templates/clips/app/routes/record.tsx +17 -4
  47. package/corpus/templates/clips/app/routes/share.$shareId.tsx +2 -0
  48. package/corpus/templates/clips/changelog/2026-07-09-desktop-popover-sits-closer-to-the-menu-bar.md +6 -0
  49. package/corpus/templates/clips/changelog/2026-07-09-desktop-recording-optimization-progress-stays-visible-until-the-clip-opens.md +6 -0
  50. package/corpus/templates/clips/changelog/2026-07-09-fixed-large-video-file-uploads-failing-before-their-resumabl.md +6 -0
  51. package/corpus/templates/clips/changelog/2026-07-09-fixed-new-video-uploads-defaulting-to-private.md +6 -0
  52. package/corpus/templates/clips/changelog/2026-07-09-long-recordings-now-get-enough-time-to-download-their-media-.md +6 -0
  53. package/corpus/templates/clips/changelog/2026-07-09-meeting-notes-no-longer-lowers-your-microphone-volume-for-ot.md +6 -0
  54. package/corpus/templates/clips/changelog/2026-07-09-on-mobile-tapping-a-playing-clip-reveals-playback-controls-w.md +6 -0
  55. package/corpus/templates/clips/changelog/2026-07-09-regenerate-any-clip-transcript-from-its-saved-recording-or-r.md +6 -0
  56. package/corpus/templates/clips/changelog/2026-07-09-slack-previews-now-show-the-clip-length-alongside-its-descri.md +6 -0
  57. package/corpus/templates/clips/desktop/src/app.tsx +12 -2
  58. package/corpus/templates/clips/desktop/src/hooks/useMeetingTranscription.ts +8 -1
  59. package/corpus/templates/clips/desktop/src/lib/recorder.ts +8 -5
  60. package/corpus/templates/clips/desktop/src/lib/transcription-engine.ts +5 -5
  61. package/corpus/templates/clips/desktop/src/lib/voice-dictation.ts +3 -0
  62. package/corpus/templates/clips/desktop/src/styles.css +6 -3
  63. package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +29 -11
  64. package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +1 -1
  65. package/corpus/templates/clips/desktop/src-tauri/src/system_audio.rs +103 -21
  66. package/corpus/templates/clips/desktop/src-tauri/src/whisper_speech.rs +117 -39
  67. package/corpus/templates/clips/server/lib/slack-unfurls.ts +20 -1
  68. package/corpus/templates/clips/server/lib/streaming-upload-mode.ts +10 -4
  69. package/corpus/templates/clips/shared/recording-core.ts +12 -0
  70. package/corpus/templates/content/AGENTS.md +7 -6
  71. package/corpus/templates/content/actions/_document-flush.ts +132 -17
  72. package/corpus/templates/content/actions/_notion-action-utils.ts +15 -0
  73. package/corpus/templates/content/actions/create-and-link-notion-page.ts +2 -0
  74. package/corpus/templates/content/actions/link-notion-page.ts +2 -0
  75. package/corpus/templates/content/actions/pull-document.ts +5 -7
  76. package/corpus/templates/content/actions/pull-notion-page.ts +2 -0
  77. package/corpus/templates/content/actions/push-notion-page.ts +10 -0
  78. package/corpus/templates/content/actions/resolve-notion-sync-conflict.ts +2 -0
  79. package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +88 -23
  80. package/corpus/templates/content/app/components/editor/VisualEditor.tsx +32 -2
  81. package/corpus/templates/content/app/hooks/use-notion.ts +6 -6
  82. package/corpus/templates/content/app/i18n/zh-TW.ts +1 -0
  83. package/corpus/templates/content/app/i18n-data.ts +18 -0
  84. package/corpus/templates/content/changelog/2026-07-09-notion-conflict-choices-and-version-restores-preserve-docume.md +6 -0
  85. package/corpus/templates/content/server/lib/notion-sync.ts +80 -64
  86. package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +29 -2
  87. package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +86 -0
  88. package/corpus/templates/design/.agents/skills/export-handoff/SKILL.md +57 -0
  89. package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +11 -0
  90. package/corpus/templates/design/AGENTS.md +45 -10
  91. package/corpus/templates/design/actions/add-localhost-screens.ts +66 -13
  92. package/corpus/templates/design/actions/apply-component-prop-edit.ts +45 -36
  93. package/corpus/templates/design/actions/apply-motion-edit.ts +25 -31
  94. package/corpus/templates/design/actions/apply-shader-fill.ts +29 -42
  95. package/corpus/templates/design/actions/detach-component-instance.ts +337 -0
  96. package/corpus/templates/design/actions/export-design-as-figma-svg.ts +219 -0
  97. package/corpus/templates/design/actions/generate-design.ts +79 -12
  98. package/corpus/templates/design/actions/generate-screens.ts +57 -12
  99. package/corpus/templates/design/actions/get-figma-styles.ts +115 -0
  100. package/corpus/templates/design/actions/go-to-main-component.ts +254 -0
  101. package/corpus/templates/design/actions/grant-localhost-write-consent.ts +4 -3
  102. package/corpus/templates/design/actions/import-design-source.ts +4 -31
  103. package/corpus/templates/design/actions/import-figma-clipboard.ts +134 -0
  104. package/corpus/templates/design/actions/import-figma-frame.ts +95 -0
  105. package/corpus/templates/design/actions/index-design-system-with-builder.ts +16 -1
  106. package/corpus/templates/design/actions/list-design-components.ts +104 -0
  107. package/corpus/templates/design/actions/list-figma-library-assets.ts +1 -24
  108. package/corpus/templates/design/actions/open-component-source.ts +2 -4
  109. package/corpus/templates/design/actions/present-design-variants.ts +412 -16
  110. package/corpus/templates/design/actions/preview-source-edit.ts +14 -1
  111. package/corpus/templates/design/actions/run-design-audit.ts +25 -3
  112. package/corpus/templates/design/actions/show-design-questions.ts +10 -0
  113. package/corpus/templates/design/actions/swap-component-instance.ts +544 -0
  114. package/corpus/templates/design/actions/take-design-screenshot.ts +45 -60
  115. package/corpus/templates/design/actions/write-local-file.ts +25 -1
  116. package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +152 -0
  117. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +1077 -58
  118. package/corpus/templates/design/app/components/design/DesignEditorSkeleton.tsx +5 -5
  119. package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +73 -21
  120. package/corpus/templates/design/app/components/design/EditPanel.tsx +16 -11
  121. package/corpus/templates/design/app/components/design/KeyboardShortcutsPanel.tsx +364 -0
  122. package/corpus/templates/design/app/components/design/LayersPanel.tsx +180 -53
  123. package/corpus/templates/design/app/components/design/LocalhostWriteConsentDialog.tsx +0 -2
  124. package/corpus/templates/design/app/components/design/MotionDock.tsx +144 -21
  125. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +1152 -167
  126. package/corpus/templates/design/app/components/design/QuestionFlow.tsx +27 -5
  127. package/corpus/templates/design/app/components/design/ReviewPanel.tsx +23 -2
  128. package/corpus/templates/design/app/components/design/TokensPanel.tsx +63 -49
  129. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +1293 -54
  130. package/corpus/templates/design/app/components/design/bridge/embedded-wheel.bridge.ts +7 -0
  131. package/corpus/templates/design/app/components/design/bridge/hit-test.bridge.ts +6 -1
  132. package/corpus/templates/design/app/components/design/bridge/motion-preview.bridge.ts +23 -6
  133. package/corpus/templates/design/app/components/design/bridge/sample.bridge.ts +7 -0
  134. package/corpus/templates/design/app/components/design/code-workbench/CodeWorkbench.tsx +5 -0
  135. package/corpus/templates/design/app/components/design/code-workbench/editor/MonacoHost.tsx +14 -2
  136. package/corpus/templates/design/app/components/design/code-workbench/explorer/FileTree.tsx +37 -2
  137. package/corpus/templates/design/app/components/design/code-workbench/format/format-on-open-guard.ts +25 -0
  138. package/corpus/templates/design/app/components/design/code-workbench/format/format-on-open.ts +19 -1
  139. package/corpus/templates/design/app/components/design/code-workbench/model-registry.ts +43 -5
  140. package/corpus/templates/design/app/components/design/code-workbench/search/SearchView.tsx +27 -2
  141. package/corpus/templates/design/app/components/design/code-workbench/search/search-engine.ts +10 -1
  142. package/corpus/templates/design/app/components/design/code-workbench/store.tsx +19 -2
  143. package/corpus/templates/design/app/components/design/design-canvas/annotation-snapshot.ts +265 -0
  144. package/corpus/templates/design/app/components/design/design-canvas/annotation-submit.ts +16 -5
  145. package/corpus/templates/design/app/components/design/design-canvas/coordinate-transforms.ts +16 -2
  146. package/corpus/templates/design/app/components/design/design-canvas/embedded-frame.ts +7 -2
  147. package/corpus/templates/design/app/components/design/design-canvas/iframe-events.ts +3 -1
  148. package/corpus/templates/design/app/components/design/edit-panel/code-inspect-helpers.tsx +78 -8
  149. package/corpus/templates/design/app/components/design/edit-panel/component-section.tsx +451 -73
  150. package/corpus/templates/design/app/components/design/edit-panel/document-colors.ts +12 -4
  151. package/corpus/templates/design/app/components/design/edit-panel/effects-properties.tsx +398 -272
  152. package/corpus/templates/design/app/components/design/edit-panel/element-classification.ts +41 -7
  153. package/corpus/templates/design/app/components/design/edit-panel/element-identity.ts +16 -3
  154. package/corpus/templates/design/app/components/design/edit-panel/field-primitives.tsx +24 -4
  155. package/corpus/templates/design/app/components/design/edit-panel/fill-gradient-helpers.ts +222 -4
  156. package/corpus/templates/design/app/components/design/edit-panel/fill-properties.tsx +171 -78
  157. package/corpus/templates/design/app/components/design/edit-panel/inspector-controls.tsx +45 -5
  158. package/corpus/templates/design/app/components/design/edit-panel/layout-properties.tsx +299 -28
  159. package/corpus/templates/design/app/components/design/edit-panel/panel-primitives.tsx +120 -150
  160. package/corpus/templates/design/app/components/design/edit-panel/position-helpers.ts +53 -1
  161. package/corpus/templates/design/app/components/design/edit-panel/position-layout-properties.tsx +284 -117
  162. package/corpus/templates/design/app/components/design/edit-panel/selection-helpers.ts +81 -0
  163. package/corpus/templates/design/app/components/design/edit-panel/stroke-properties.tsx +19 -14
  164. package/corpus/templates/design/app/components/design/edit-panel/style-options.ts +7 -0
  165. package/corpus/templates/design/app/components/design/edit-panel/typography-helpers.ts +155 -0
  166. package/corpus/templates/design/app/components/design/edit-panel/typography-properties.tsx +268 -46
  167. package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +350 -31
  168. package/corpus/templates/design/app/components/design/inspector/ConstraintsWidget.tsx +13 -4
  169. package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +233 -23
  170. package/corpus/templates/design/app/components/design/inspector/GradientEditor.tsx +72 -2
  171. package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +51 -11
  172. package/corpus/templates/design/app/components/design/inspector/ShaderFillsPanel.tsx +84 -11
  173. package/corpus/templates/design/app/components/design/inspector/index.ts +3 -0
  174. package/corpus/templates/design/app/components/design/keyboard-shortcuts.ts +552 -0
  175. package/corpus/templates/design/app/components/design/multi-screen/board-surface-html.ts +157 -0
  176. package/corpus/templates/design/app/components/design/multi-screen/frame-geometry.ts +53 -0
  177. package/corpus/templates/design/app/components/design/multi-screen/overview-layout.ts +184 -0
  178. package/corpus/templates/design/app/components/design/multi-screen/primitive-drop-target.ts +403 -26
  179. package/corpus/templates/design/app/components/design/multi-screen/types.ts +20 -1
  180. package/corpus/templates/design/app/components/design/types.ts +33 -0
  181. package/corpus/templates/design/app/components/editor/PromptDialog.tsx +59 -7
  182. package/corpus/templates/design/app/components/visual-editor/CanvasCommentPins.tsx +119 -17
  183. package/corpus/templates/design/app/components/visual-editor/DrawOverlay.tsx +275 -63
  184. package/corpus/templates/design/app/hooks/use-question-flow.ts +14 -3
  185. package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +67 -10
  186. package/corpus/templates/design/app/i18n/zh-TW.ts +91 -1
  187. package/corpus/templates/design/app/i18n-data.ts +1217 -10
  188. package/corpus/templates/design/app/i18n-keyboard-shortcuts.ts +577 -0
  189. package/corpus/templates/design/app/lib/agent-chat.ts +23 -0
  190. package/corpus/templates/design/app/lib/design-import.ts +24 -0
  191. package/corpus/templates/design/app/lib/figma-clipboard.ts +157 -0
  192. package/corpus/templates/design/app/lib/figma-svg-copy.ts +221 -0
  193. package/corpus/templates/design/app/pages/design-editor/code-layer-state.ts +143 -30
  194. package/corpus/templates/design/app/pages/design-editor/collab-sync.ts +15 -0
  195. package/corpus/templates/design/app/pages/design-editor/editor-session.ts +33 -0
  196. package/corpus/templates/design/app/pages/design-editor/editor-state.ts +38 -0
  197. package/corpus/templates/design/app/pages/design-editor/overview-annotation-context.ts +157 -0
  198. package/corpus/templates/design/app/pages/design-editor/overview-camera.ts +62 -6
  199. package/corpus/templates/design/app/pages/design-editor/pending-edits.ts +245 -0
  200. package/corpus/templates/design/app/pages/design-editor/react-semantic-handoff.ts +586 -0
  201. package/corpus/templates/design/app/pages/design-editor/screen-auto-layout.ts +169 -0
  202. package/corpus/templates/design/app/pages/design-editor/selection-state.ts +201 -1
  203. package/corpus/templates/design/changelog/2026-07-09-added-underline-strikethrough-and-text-case-uppercase-lowerc.md +6 -0
  204. package/corpus/templates/design/changelog/2026-07-09-annotate-now-draws-across-the-all-screens-canvas-without-swi.md +6 -0
  205. package/corpus/templates/design/changelog/2026-07-09-auto-layout-children-can-now-be-dragged-into-freeform-screen.md +6 -0
  206. package/corpus/templates/design/changelog/2026-07-09-board-shapes-now-stay-visible-and-nest-correctly-across-the-.md +6 -0
  207. package/corpus/templates/design/changelog/2026-07-09-component-source-navigation-now-opens-reliably-from-the-insp.md +6 -0
  208. package/corpus/templates/design/changelog/2026-07-09-cross-screen-local-react-layer-moves-now-hand-off-exact-sour.md +6 -0
  209. package/corpus/templates/design/changelog/2026-07-09-cross-screen-moves-undo-and-redo-now-keep-both-screens-stabl.md +6 -0
  210. package/corpus/templates/design/changelog/2026-07-09-detach-a-component-instance-into-plain-editable-eleme.md +6 -0
  211. package/corpus/templates/design/changelog/2026-07-09-dragging-layers-into-auto-layout-now-respects-ignore-auto-la.md +6 -0
  212. package/corpus/templates/design/changelog/2026-07-09-drawing-annotations-sent-to-the-agent-now-include-a-rendered.md +6 -0
  213. package/corpus/templates/design/changelog/2026-07-09-editor-sidebars-now-use-figma-matched-default-widths-for-a-r.md +6 -0
  214. package/corpus/templates/design/changelog/2026-07-09-empty-frames-and-rectangles-now-expose-full-auto-layout-cont.md +6 -0
  215. package/corpus/templates/design/changelog/2026-07-09-export-any-design-screen-as-a-real-vector-svg-for-figma-with.md +6 -0
  216. package/corpus/templates/design/changelog/2026-07-09-grid-auto-layout-now-uses-true-responsive-rows-and-columns-w.md +6 -0
  217. package/corpus/templates/design/changelog/2026-07-09-hidden-and-locked-screens-now-behave-consistently-between-la.md +6 -0
  218. package/corpus/templates/design/changelog/2026-07-09-image-fills-now-preserve-valid-urls-and-layered-fit-settings.md +6 -0
  219. package/corpus/templates/design/changelog/2026-07-09-import-figma-frames-as-pixel-accurate-design-screens-via-a-s.md +6 -0
  220. package/corpus/templates/design/changelog/2026-07-09-inspect-code-now-hides-internal-styling-metadata-and-wraps-l.md +6 -0
  221. package/corpus/templates/design/changelog/2026-07-09-inspector-blur-and-shadow-effects-now-preserve-other-effects.md +6 -0
  222. package/corpus/templates/design/changelog/2026-07-09-jump-to-a-components-other-instances-from-the-inspecto.md +6 -0
  223. package/corpus/templates/design/changelog/2026-07-09-keyboard-shortcuts-are-now-discoverable-in-a-figma-style-bot.md +6 -0
  224. package/corpus/templates/design/changelog/2026-07-09-layer-selection-now-keeps-cmd-or-ctrl-toggles-and-shift-rang.md +6 -0
  225. package/corpus/templates/design/changelog/2026-07-09-layers-dropped-into-auto-layout-now-participate-correctly-in.md +6 -0
  226. package/corpus/templates/design/changelog/2026-07-09-local-live-edit-screens-now-enter-and-leave-full-view-withou.md +6 -0
  227. package/corpus/templates/design/changelog/2026-07-09-local-preview-urls-now-live-in-the-inspector-instead-of-taki.md +6 -0
  228. package/corpus/templates/design/changelog/2026-07-09-local-react-layers-can-now-be-hidden-and-locked-from-layers-.md +6 -0
  229. package/corpus/templates/design/changelog/2026-07-09-local-react-layers-now-keep-exact-source-provenance-and-supp.md +6 -0
  230. package/corpus/templates/design/changelog/2026-07-09-local-react-visual-editing-now-loads-reliably-and-shows-live.md +6 -0
  231. package/corpus/templates/design/changelog/2026-07-09-paste-from-figma-cmd-v-now-imports-exact-editable-nodes-when.md +6 -0
  232. package/corpus/templates/design/changelog/2026-07-09-pressing-escape-after-selecting-a-screen-now-reveals-page-pr.md +6 -0
  233. package/corpus/templates/design/changelog/2026-07-09-right-click-overlapping-or-nested-objects-to-choose-the-exac.md +6 -0
  234. package/corpus/templates/design/changelog/2026-07-09-scale-edge-stretch-and-center-constraints-now-preserve-layer.md +6 -0
  235. package/corpus/templates/design/changelog/2026-07-09-screen-rows-can-now-be-reordered-in-layers-with-matching-can.md +6 -0
  236. package/corpus/templates/design/changelog/2026-07-09-screen-selection-stays-responsive-in-designs-with-hundreds-o.md +6 -0
  237. package/corpus/templates/design/changelog/2026-07-09-shift-a-now-enables-auto-layout-on-a-selected-screen-with-fl.md +6 -0
  238. package/corpus/templates/design/changelog/2026-07-09-swap-a-component-instance-for-another-from-the-libra.md +6 -0
  239. package/corpus/templates/design/changelog/2026-07-10-drawing-annotations-now-stay-available-to-retry-when-the.md +6 -0
  240. package/corpus/templates/design/e2e/global-setup.ts +3 -6
  241. package/corpus/templates/design/e2e/helpers.ts +27 -6
  242. package/corpus/templates/design/server/lib/design-to-figma-svg.ts +1412 -0
  243. package/corpus/templates/design/server/lib/figma-clipboard-match.ts +196 -0
  244. package/corpus/templates/design/server/lib/figma-node-import.ts +311 -0
  245. package/corpus/templates/design/server/lib/figma-node-to-html.ts +1334 -0
  246. package/corpus/templates/design/server/lib/figma-paste-fallback.ts +64 -0
  247. package/corpus/templates/design/server/lib/playwright-runtime.ts +78 -0
  248. package/corpus/templates/design/server/plugins/db.ts +41 -0
  249. package/corpus/templates/design/server/source-workspace.ts +64 -0
  250. package/corpus/templates/design/shared/board-file.ts +13 -2
  251. package/corpus/templates/design/shared/canvas-math.ts +231 -33
  252. package/corpus/templates/design/shared/code-layer.ts +122 -8
  253. package/corpus/templates/design/shared/component-library.ts +161 -0
  254. package/corpus/templates/design/shared/db-conflict.ts +27 -0
  255. package/corpus/templates/design/shared/design-source-capabilities.ts +2 -1
  256. package/corpus/templates/design/shared/figma-url.ts +149 -0
  257. package/corpus/templates/design/shared/pen-path.ts +16 -7
  258. package/corpus/templates/design/shared/source-mode.ts +1 -0
  259. package/corpus/templates/plan/app/components/plan/CanvasArea.tsx +7 -19
  260. package/corpus/templates/plan/changelog/2026-07-09-canvas-panning-now-keeps-zoom-fixed-even-at-the-edges.md +6 -0
  261. package/corpus/templates/slides/actions/index-design-system-with-builder.ts +16 -1
  262. package/corpus/templates/slides/app/context/DeckContext.tsx +311 -70
  263. package/corpus/templates/slides/changelog/2026-07-09-fixed-the-slide-rail-going-permanently-stale-during-a-long-a.md +6 -0
  264. package/dist/agent/production-agent.d.ts +79 -6
  265. package/dist/agent/production-agent.d.ts.map +1 -1
  266. package/dist/agent/production-agent.js +161 -14
  267. package/dist/agent/production-agent.js.map +1 -1
  268. package/dist/agent/run-manager.d.ts.map +1 -1
  269. package/dist/agent/run-manager.js +29 -5
  270. package/dist/agent/run-manager.js.map +1 -1
  271. package/dist/agent/run-store.d.ts +46 -0
  272. package/dist/agent/run-store.d.ts.map +1 -1
  273. package/dist/agent/run-store.js +61 -0
  274. package/dist/agent/run-store.js.map +1 -1
  275. package/dist/cli/design-connect.d.ts +6 -0
  276. package/dist/cli/design-connect.d.ts.map +1 -1
  277. package/dist/cli/design-connect.js +336 -139
  278. package/dist/cli/design-connect.js.map +1 -1
  279. package/dist/cli/skills.d.ts.map +1 -1
  280. package/dist/cli/skills.js +14 -0
  281. package/dist/cli/skills.js.map +1 -1
  282. package/dist/client/AssistantChat.d.ts +2 -0
  283. package/dist/client/AssistantChat.d.ts.map +1 -1
  284. package/dist/client/AssistantChat.js +19 -3
  285. package/dist/client/AssistantChat.js.map +1 -1
  286. package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
  287. package/dist/client/MultiTabAssistantChat.js +38 -18
  288. package/dist/client/MultiTabAssistantChat.js.map +1 -1
  289. package/dist/client/agent-chat.d.ts +60 -0
  290. package/dist/client/agent-chat.d.ts.map +1 -1
  291. package/dist/client/agent-chat.js +122 -1
  292. package/dist/client/agent-chat.js.map +1 -1
  293. package/dist/client/chat/index.d.ts +1 -1
  294. package/dist/client/chat/index.d.ts.map +1 -1
  295. package/dist/client/chat/index.js +1 -1
  296. package/dist/client/chat/index.js.map +1 -1
  297. package/dist/client/extensions/EmbeddedExtension.d.ts +12 -0
  298. package/dist/client/extensions/EmbeddedExtension.d.ts.map +1 -1
  299. package/dist/client/extensions/EmbeddedExtension.js +36 -2
  300. package/dist/client/extensions/EmbeddedExtension.js.map +1 -1
  301. package/dist/client/index.d.ts +1 -1
  302. package/dist/client/index.d.ts.map +1 -1
  303. package/dist/client/index.js +1 -1
  304. package/dist/client/index.js.map +1 -1
  305. package/dist/client/rich-markdown-editor/useCollabReconcile.d.ts.map +1 -1
  306. package/dist/client/rich-markdown-editor/useCollabReconcile.js +18 -6
  307. package/dist/client/rich-markdown-editor/useCollabReconcile.js.map +1 -1
  308. package/dist/collab/awareness-store.d.ts +5 -0
  309. package/dist/collab/awareness-store.d.ts.map +1 -1
  310. package/dist/collab/awareness-store.js +19 -12
  311. package/dist/collab/awareness-store.js.map +1 -1
  312. package/dist/collab/awareness.d.ts +2 -2
  313. package/dist/collab/awareness.d.ts.map +1 -1
  314. package/dist/collab/client.d.ts.map +1 -1
  315. package/dist/collab/client.js +13 -5
  316. package/dist/collab/client.js.map +1 -1
  317. package/dist/collab/index.d.ts +1 -0
  318. package/dist/collab/index.d.ts.map +1 -1
  319. package/dist/collab/index.js +1 -0
  320. package/dist/collab/index.js.map +1 -1
  321. package/dist/collab/recent-edits.d.ts +3 -1
  322. package/dist/collab/recent-edits.d.ts.map +1 -1
  323. package/dist/collab/recent-edits.js +57 -2
  324. package/dist/collab/recent-edits.js.map +1 -1
  325. package/dist/collab/routes.d.ts +1 -1
  326. package/dist/deploy/build.d.ts +24 -0
  327. package/dist/deploy/build.d.ts.map +1 -1
  328. package/dist/deploy/build.js +102 -5
  329. package/dist/deploy/build.js.map +1 -1
  330. package/dist/index.browser.d.ts +1 -1
  331. package/dist/index.browser.d.ts.map +1 -1
  332. package/dist/index.browser.js +1 -1
  333. package/dist/index.browser.js.map +1 -1
  334. package/dist/notifications/routes.d.ts +1 -1
  335. package/dist/observability/routes.d.ts +5 -5
  336. package/dist/progress/routes.d.ts +1 -1
  337. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  338. package/dist/server/agent-chat-plugin.js +87 -10
  339. package/dist/server/agent-chat-plugin.js.map +1 -1
  340. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  341. package/dist/server/builder-design-systems.d.ts +15 -0
  342. package/dist/server/builder-design-systems.d.ts.map +1 -1
  343. package/dist/server/builder-design-systems.js +9 -4
  344. package/dist/server/builder-design-systems.js.map +1 -1
  345. package/package.json +1 -1
@@ -631,6 +631,20 @@ replace them with copied \`srcdoc\` HTML unless the user explicitly asks for a
631
631
  frozen snapshot. To change a state, rerun \`add-localhost-screens\` with the new
632
632
  path/query or duplicate the screen and update the copy's URL metadata.
633
633
 
634
+ ## React Source Writeback
635
+
636
+ - Use compiler/debug provenance (project-relative file, line, column,
637
+ component, and runtime multiplicity) to locate React/TSX source. Treat it as
638
+ evidence, not as permission for a generic AST structural transform.
639
+ - Reparenting, grouping/ungrouping, wrappers, dynamic expressions, repeated
640
+ \`.map()\` instances, shared components, and cross-file changes go through the
641
+ coding agent with exact subject/target anchors and their runtime
642
+ relationship.
643
+ - Before each write, read the file and pass its exact \`versionHash\` to
644
+ \`write-local-file\` with \`requireExpectedVersionHash: true\`; on conflict,
645
+ re-read and re-plan. Keep the optimistic preview until HMR/runtime confirms
646
+ the result. Human write consent remains mandatory and agents cannot grant it.
647
+
634
648
  ## Verification
635
649
 
636
650
  - \`list-localhost-connections\` returns the expected connection and routes.
@@ -55,9 +55,11 @@ import {
55
55
  appendAgentChatContextToMessage,
56
56
  formatAgentChatContextItemsForPrompt,
57
57
  getAgentChatContextState,
58
+ isAgentChatSubmitCancelled,
58
59
  normalizeAgentChatContextItem,
59
60
  publishAgentChatContextItems,
60
61
  refreshAgentChatContext,
62
+ reportAgentChatSubmitResult,
61
63
  subscribeAgentChatContext,
62
64
  type AgentChatContextItem,
63
65
  } from "./agent-chat.js";
@@ -197,6 +199,8 @@ export type AgentRecoveryAction = "continue" | "retry";
197
199
  export interface AssistantChatSendOptions {
198
200
  trackInRunsTray?: boolean;
199
201
  requestMode?: AgentRequestMode;
202
+ /** Correlates with `AGENT_CHAT_SUBMIT_RESULT_EVENT` — see agent-chat.ts. */
203
+ submitMessageId?: string;
200
204
  }
201
205
 
202
206
  function createUserMessageRunConfig(
@@ -3651,9 +3655,12 @@ const AssistantChatInner = forwardRef<
3651
3655
  trackInRunsTray = false,
3652
3656
  preserveReconnectAutoRecoveryBudget = false,
3653
3657
  hideUserMessage = false,
3658
+ submitMessageId?: string,
3654
3659
  ) => {
3660
+ if (isAgentChatSubmitCancelled(submitMessageId)) return;
3655
3661
  if (agentEngineConfigured.state === "missing") {
3656
3662
  void ensureAgentEngineReadyForSubmit();
3663
+ reportAgentChatSubmitResult(submitMessageId, false, "missing-engine");
3657
3664
  return;
3658
3665
  }
3659
3666
  if (!preserveReconnectAutoRecoveryBudget) {
@@ -3687,8 +3694,10 @@ const AssistantChatInner = forwardRef<
3687
3694
  ? err.message
3688
3695
  : "Attachment could not be processed.";
3689
3696
  setComposerError(msg);
3697
+ reportAgentChatSubmitResult(submitMessageId, false, "attachment-error");
3690
3698
  return;
3691
3699
  }
3700
+ if (isAgentChatSubmitCancelled(submitMessageId)) return;
3692
3701
  const imageAttachments = createAgentImageAttachments(images);
3693
3702
  const allAttachments = [
3694
3703
  ...(queuedAttachments ?? []),
@@ -3782,6 +3791,11 @@ const AssistantChatInner = forwardRef<
3782
3791
  setComposerError(
3783
3792
  `"${rejected.name}" makes the message too large to send (combined attachments must be under ${Math.round(MAX_ESTIMATED_BODY_BYTES / 1024 / 1024)} MB). Remove it or use a smaller image.`,
3784
3793
  );
3794
+ reportAgentChatSubmitResult(
3795
+ submitMessageId,
3796
+ false,
3797
+ "attachment-too-large",
3798
+ );
3785
3799
  return;
3786
3800
  }
3787
3801
  }
@@ -3789,6 +3803,7 @@ const AssistantChatInner = forwardRef<
3789
3803
  }
3790
3804
  }
3791
3805
  // ── End body-size guard ──────────────────────────────────────────
3806
+ if (isAgentChatSubmitCancelled(submitMessageId)) return;
3792
3807
  // Snapshot the exec mode at enqueue time when the caller didn't
3793
3808
  // pass an explicit override. Without this, a plan-mode message that
3794
3809
  // sits in the queue runs as 'act' if the user flips the global toggle
@@ -3863,9 +3878,16 @@ const AssistantChatInner = forwardRef<
3863
3878
  } as Parameters<typeof threadRuntime.append>[0]);
3864
3879
  } catch (error) {
3865
3880
  setOptimisticRunning(false);
3881
+ reportAgentChatSubmitResult(submitMessageId, false, "append-failed");
3866
3882
  throw error;
3867
3883
  }
3868
3884
  }
3885
+ // The turn is now either queued behind the active run or already a
3886
+ // visible message — either way it has reached the chat. This is
3887
+ // intentionally reported before the agent's response resolves: a
3888
+ // caller like sendToAgentChatAndConfirm only needs to know the submit
3889
+ // wasn't silently dropped, not whether the run itself later succeeds.
3890
+ reportAgentChatSubmitResult(submitMessageId, true);
3869
3891
  if (submitted.includesContext) {
3870
3892
  updateComposerContextItems(() => []);
3871
3893
  }
@@ -3927,6 +3949,9 @@ const AssistantChatInner = forwardRef<
3927
3949
  undefined,
3928
3950
  false,
3929
3951
  options?.trackInRunsTray === true,
3952
+ false,
3953
+ false,
3954
+ options?.submitMessageId,
3930
3955
  );
3931
3956
  },
3932
3957
  prefillMessage(text: string) {
@@ -30,8 +30,10 @@ import {
30
30
  claimAgentChatSubmit,
31
31
  drainBufferedAgentChatOpenRequests,
32
32
  drainBufferedAgentChatSubmits,
33
+ isAgentChatSubmitCancelled,
33
34
  normalizeAgentChatContextItem,
34
35
  parseSubmitChatMessage,
36
+ reportAgentChatSubmitResult,
35
37
  type AgentChatContextItem,
36
38
  } from "./agent-chat.js";
37
39
  import { agentNativePath, appPath } from "./api-path.js";
@@ -79,6 +81,8 @@ interface PendingSend {
79
81
  submit: boolean;
80
82
  trackInRunsTray?: boolean;
81
83
  requestMode?: "act" | "plan";
84
+ /** Correlates with `AGENT_CHAT_SUBMIT_RESULT_EVENT` — see agent-chat.ts. */
85
+ submitMessageId?: string;
82
86
  }
83
87
 
84
88
  /**
@@ -93,14 +97,18 @@ interface PendingDelivery {
93
97
 
94
98
  /** The single path that hands a queued send to a mounted chat ref. */
95
99
  function deliverPendingSend(ref: AssistantChatHandle, send: PendingSend): void {
100
+ if (isAgentChatSubmitCancelled(send.submitMessageId)) return;
96
101
  if (!send.submit) {
97
102
  ref.prefillMessage(send.message);
98
103
  return;
99
104
  }
100
- if (send.trackInRunsTray || send.requestMode) {
105
+ if (send.trackInRunsTray || send.requestMode || send.submitMessageId) {
101
106
  ref.sendMessage(send.message, send.images, {
102
107
  ...(send.trackInRunsTray ? { trackInRunsTray: true } : {}),
103
108
  ...(send.requestMode ? { requestMode: send.requestMode } : {}),
109
+ ...(send.submitMessageId
110
+ ? { submitMessageId: send.submitMessageId }
111
+ : {}),
104
112
  });
105
113
  } else {
106
114
  ref.sendMessage(send.message, send.images);
@@ -1767,6 +1775,7 @@ export function MultiTabAssistantChat({
1767
1775
  background,
1768
1776
  submit,
1769
1777
  images,
1778
+ submitMessageId,
1770
1779
  } = parsed;
1771
1780
  const requestedTabId = parsed.tabId;
1772
1781
  const requestMode =
@@ -1777,7 +1786,14 @@ export function MultiTabAssistantChat({
1777
1786
  if (openSidebar !== false && !background) {
1778
1787
  window.dispatchEvent(new CustomEvent("agent-panel:open"));
1779
1788
  }
1780
- if (postMessageSubmissionsDisabled) return;
1789
+ if (postMessageSubmissionsDisabled) {
1790
+ reportAgentChatSubmitResult(
1791
+ submitMessageId,
1792
+ false,
1793
+ "composer-disabled",
1794
+ );
1795
+ return;
1796
+ }
1781
1797
 
1782
1798
  // Plan mode is sent as request metadata by the chat adapter. Keep the
1783
1799
  // user-visible message clean so mode instructions never enter history.
@@ -1791,9 +1807,11 @@ export function MultiTabAssistantChat({
1791
1807
  submit,
1792
1808
  ...(background ? { trackInRunsTray: true } : {}),
1793
1809
  ...(requestMode ? { requestMode } : {}),
1810
+ ...(submitMessageId ? { submitMessageId } : {}),
1794
1811
  };
1795
1812
 
1796
1813
  const sendToTab = (threadId: string) => {
1814
+ if (isAgentChatSubmitCancelled(submitMessageId)) return;
1797
1815
  // If a model override was specified, apply it only if we recognize it
1798
1816
  if (model) {
1799
1817
  const matchedGroup = availableModels.find((g) =>
@@ -1826,8 +1844,17 @@ export function MultiTabAssistantChat({
1826
1844
 
1827
1845
  if (newTab) {
1828
1846
  const previousTabId = activeThreadIdRef.current;
1829
- createThread(requestedTabId).then((newId) => {
1830
- if (newId) {
1847
+ createThread(requestedTabId)
1848
+ .then((newId) => {
1849
+ if (isAgentChatSubmitCancelled(submitMessageId)) return;
1850
+ if (!newId) {
1851
+ reportAgentChatSubmitResult(
1852
+ submitMessageId,
1853
+ false,
1854
+ "thread-create-failed",
1855
+ );
1856
+ return;
1857
+ }
1831
1858
  newThreadIds.current.add(newId);
1832
1859
  if (background) {
1833
1860
  mountedTabsRef.current.add(newId);
@@ -1842,8 +1869,14 @@ export function MultiTabAssistantChat({
1842
1869
  if (background && previousTabId) {
1843
1870
  switchThreadState(previousTabId);
1844
1871
  }
1845
- }
1846
- });
1872
+ })
1873
+ .catch(() => {
1874
+ reportAgentChatSubmitResult(
1875
+ submitMessageId,
1876
+ false,
1877
+ "thread-create-failed",
1878
+ );
1879
+ });
1847
1880
  } else {
1848
1881
  const currentTabId = activeThreadIdRef.current;
1849
1882
  if (currentTabId) {
@@ -1901,6 +1934,7 @@ export function MultiTabAssistantChat({
1901
1934
  const active = activeThreadIdRef.current;
1902
1935
  const remaining: PendingDelivery[] = [];
1903
1936
  for (const delivery of pendingDeliveries.current) {
1937
+ if (isAgentChatSubmitCancelled(delivery.send.submitMessageId)) continue;
1904
1938
  const threadId = delivery.threadId ?? active ?? null;
1905
1939
  const ref = threadId ? chatRefs.current.get(threadId) : null;
1906
1940
  if (threadId && ref) {
@@ -96,6 +96,11 @@ export interface AgentChatMessage {
96
96
  * focusing it or opening the sidebar. The message runs silently.
97
97
  */
98
98
  background?: boolean;
99
+ /**
100
+ * Stable id used to deduplicate a submit and correlate it with
101
+ * {@link AGENT_CHAT_SUBMIT_RESULT_EVENT}. Auto-generated if omitted.
102
+ */
103
+ submitMessageId?: string;
99
104
  }
100
105
 
101
106
  export interface AgentChatContextItem {
@@ -210,6 +215,45 @@ export const AGENT_CHAT_INSERT_REFERENCE_EVENT =
210
215
  "agentNative:insert-composer-reference";
211
216
  const AGENT_PANEL_PREPARE_EVENT = "agent-panel:prepare";
212
217
 
218
+ /**
219
+ * Fired once a submitted turn's fate is known: `delivered: true` once the
220
+ * receiving AssistantChat has actually committed the turn (added it to the
221
+ * visible thread, independent of whether the agent's response later
222
+ * succeeds), or `delivered: false` when it was rejected before ever
223
+ * appearing — e.g. no LLM/agent engine configured. Callers that must know
224
+ * whether their submit truly landed (rather than fire-and-forget) should use
225
+ * {@link sendToAgentChatAndConfirm} instead of listening for this directly.
226
+ */
227
+ export const AGENT_CHAT_SUBMIT_RESULT_EVENT = "agentNative.chatSubmitResult";
228
+
229
+ export interface AgentChatSubmitResult {
230
+ submitMessageId: string;
231
+ delivered: boolean;
232
+ reason?: string;
233
+ }
234
+
235
+ /** Report a submit's definitive outcome so `sendToAgentChatAndConfirm` (or any
236
+ * other correlated listener) can resolve. No-ops without a submitMessageId or
237
+ * a window (SSR). */
238
+ export function reportAgentChatSubmitResult(
239
+ submitMessageId: string | undefined,
240
+ delivered: boolean,
241
+ reason?: string,
242
+ ): void {
243
+ if (
244
+ !submitMessageId ||
245
+ cancelledSubmitIds.has(submitMessageId) ||
246
+ typeof window === "undefined"
247
+ ) {
248
+ return;
249
+ }
250
+ window.dispatchEvent(
251
+ new CustomEvent<AgentChatSubmitResult>(AGENT_CHAT_SUBMIT_RESULT_EVENT, {
252
+ detail: { submitMessageId, delivered, reason },
253
+ }),
254
+ );
255
+ }
256
+
213
257
  let agentChatContextState: AgentChatContextState = {
214
258
  items: [],
215
259
  updatedAt: 0,
@@ -262,6 +306,7 @@ interface BufferedSelfSubmit {
262
306
  const SELF_SUBMIT_BUFFER_TTL_MS = 8000;
263
307
  const bufferedSelfSubmits: BufferedSelfSubmit[] = [];
264
308
  const claimedSubmitIds = new Set<string>();
309
+ const cancelledSubmitIds = new Set<string>();
265
310
 
266
311
  interface BufferedOpenRequest extends BufferedAgentChatOpenRequest {
267
312
  at: number;
@@ -289,6 +334,28 @@ function bufferSelfSubmit(data: Record<string, unknown>): void {
289
334
  bufferedSelfSubmits.push({ id, data, at: now });
290
335
  }
291
336
 
337
+ /**
338
+ * Permanently tombstone a submit for this page lifetime and remove its
339
+ * cold-start replay. A confirmation timeout must be terminal: callers may
340
+ * retry while preserving their own state, so the original submit cannot be
341
+ * allowed to appear later when a lazy panel or thread ref finally mounts.
342
+ */
343
+ export function cancelAgentChatSubmit(id: string | undefined): void {
344
+ if (!id) return;
345
+ cancelledSubmitIds.add(id);
346
+ claimedSubmitIds.add(id);
347
+ for (let index = bufferedSelfSubmits.length - 1; index >= 0; index -= 1) {
348
+ if (bufferedSelfSubmits[index]?.id === id) {
349
+ bufferedSelfSubmits.splice(index, 1);
350
+ }
351
+ }
352
+ }
353
+
354
+ /** Whether a confirmed-submit timeout has made this delivery terminal. */
355
+ export function isAgentChatSubmitCancelled(id: string | undefined): boolean {
356
+ return Boolean(id && cancelledSubmitIds.has(id));
357
+ }
358
+
292
359
  function pruneOpenRequestBuffer(now: number): void {
293
360
  for (let i = bufferedOpenRequests.length - 1; i >= 0; i -= 1) {
294
361
  if (now - bufferedOpenRequests[i].at > OPEN_REQUEST_BUFFER_TTL_MS) {
@@ -328,6 +395,7 @@ export function drainBufferedAgentChatSubmits(): Array<
328
395
  /** Claim a submit; false if already handled. Idless submits always pass. */
329
396
  export function claimAgentChatSubmit(id: string | undefined): boolean {
330
397
  if (!id) return true;
398
+ if (cancelledSubmitIds.has(id)) return false;
331
399
  if (claimedSubmitIds.has(id)) return false;
332
400
  claimedSubmitIds.add(id);
333
401
  return true;
@@ -353,6 +421,7 @@ export function claimAgentChatOpenRequest(id: unknown): boolean {
353
421
  export function _resetAgentChatSubmitBufferForTests(): void {
354
422
  bufferedSelfSubmits.length = 0;
355
423
  claimedSubmitIds.clear();
424
+ cancelledSubmitIds.clear();
356
425
  bufferedOpenRequests.length = 0;
357
426
  claimedOpenRequestIds.clear();
358
427
  }
@@ -896,7 +965,7 @@ export function sendToAgentChat(opts: AgentChatMessage): string {
896
965
  return tabId;
897
966
  }
898
967
 
899
- const submitMessageId = generateSubmitMessageId();
968
+ const submitMessageId = opts.submitMessageId ?? generateSubmitMessageId();
900
969
  const payload = {
901
970
  type: AGENT_CHAT_MESSAGE_TYPE,
902
971
  data: {
@@ -977,6 +1046,101 @@ export function sendToAgentChat(opts: AgentChatMessage): string {
977
1046
  return tabId;
978
1047
  }
979
1048
 
1049
+ // Must exceed SELF_SUBMIT_BUFFER_TTL_MS. Otherwise confirmation can time out
1050
+ // while its replay is still eligible to mount and deliver later.
1051
+ const DEFAULT_SUBMIT_CONFIRM_TIMEOUT_MS = SELF_SUBMIT_BUFFER_TTL_MS + 2000;
1052
+
1053
+ export interface SendToAgentChatAndConfirmResult {
1054
+ tabId: string;
1055
+ /** True once the message actually became a visible turn in the chat. */
1056
+ delivered: boolean;
1057
+ /** Set when `delivered` is false: "missing-engine", "timeout", etc. */
1058
+ reason?: string;
1059
+ }
1060
+
1061
+ /**
1062
+ * Like {@link sendToAgentChat}, but resolves once the submit's fate is known
1063
+ * instead of firing and forgetting. Use this whenever the caller must decide
1064
+ * whether to keep/restore its own state on failure (e.g. a draw/annotate
1065
+ * overlay that should not discard the user's work unless the message actually
1066
+ * reached the chat) — see the `AGENT_CHAT_SUBMIT_RESULT_EVENT` contract.
1067
+ * This acknowledgement is intentionally limited to submitted, non-code
1068
+ * messages with `chatTarget: "local"`; parent-frame and MCP host chats use
1069
+ * different delivery protocols and return `unsupported-target` here.
1070
+ *
1071
+ * Resolves `delivered: false` if the receiving chat explicitly rejects the
1072
+ * submit (e.g. no LLM/agent engine configured) OR if no result arrives within
1073
+ * `timeoutMs` — a silently-stuck submit (panel never mounts, thread never
1074
+ * gets a ref, message type not handled by this build) must fail the same way
1075
+ * an explicit rejection does, since the caller cannot otherwise tell the
1076
+ * difference between "still in flight" and "dropped."
1077
+ */
1078
+ export function sendToAgentChatAndConfirm(
1079
+ opts: Omit<AgentChatMessage, "submitMessageId">,
1080
+ options?: { timeoutMs?: number },
1081
+ ): Promise<SendToAgentChatAndConfirmResult> {
1082
+ const tabId = opts.tabId ?? generateTabId();
1083
+ if (typeof window === "undefined") {
1084
+ return Promise.resolve({ tabId, delivered: false, reason: "no-window" });
1085
+ }
1086
+ // Confirmation is deliberately a same-window/local-chat contract. Parent
1087
+ // frames, Builder code chat, and MCP host relays have independent protocols
1088
+ // and cannot answer this window-local CustomEvent acknowledgement.
1089
+ if (
1090
+ opts.chatTarget !== "local" ||
1091
+ opts.type === "code" ||
1092
+ opts.requiresCode === true ||
1093
+ opts.submit === false
1094
+ ) {
1095
+ return Promise.resolve({
1096
+ tabId,
1097
+ delivered: false,
1098
+ reason: "unsupported-target",
1099
+ });
1100
+ }
1101
+
1102
+ const submitMessageId = generateSubmitMessageId();
1103
+ const timeoutMs = Math.max(
1104
+ 0,
1105
+ options?.timeoutMs ?? DEFAULT_SUBMIT_CONFIRM_TIMEOUT_MS,
1106
+ );
1107
+
1108
+ return new Promise<SendToAgentChatAndConfirmResult>((resolve) => {
1109
+ let settled = false;
1110
+ let timer: number | undefined;
1111
+ const cleanup = () => {
1112
+ window.removeEventListener(
1113
+ AGENT_CHAT_SUBMIT_RESULT_EVENT,
1114
+ onResult as EventListener,
1115
+ );
1116
+ if (timer !== undefined) window.clearTimeout(timer);
1117
+ };
1118
+ const finish = (delivered: boolean, reason?: string, cancel = false) => {
1119
+ if (settled) return;
1120
+ settled = true;
1121
+ if (cancel) cancelAgentChatSubmit(submitMessageId);
1122
+ cleanup();
1123
+ resolve({ tabId, delivered, reason });
1124
+ };
1125
+ const onResult = (event: Event) => {
1126
+ const detail = (event as CustomEvent<AgentChatSubmitResult>).detail;
1127
+ if (!detail || detail.submitMessageId !== submitMessageId) return;
1128
+ finish(detail.delivered, detail.reason);
1129
+ };
1130
+
1131
+ window.addEventListener(
1132
+ AGENT_CHAT_SUBMIT_RESULT_EVENT,
1133
+ onResult as EventListener,
1134
+ );
1135
+ timer = window.setTimeout(() => finish(false, "timeout", true), timeoutMs);
1136
+ try {
1137
+ sendToAgentChat({ ...opts, tabId, submitMessageId });
1138
+ } catch {
1139
+ finish(false, "send-failed", true);
1140
+ }
1141
+ });
1142
+ }
1143
+
980
1144
  /**
981
1145
  * Add or replace a keyed context nugget in the active agent chat composer.
982
1146
  * The context is not submitted until the user sends the prompt.
@@ -59,7 +59,15 @@ export {
59
59
  } from "../code-agent-chat-adapter.js";
60
60
  export * from "./connectors.js";
61
61
  export * from "./runtime.js";
62
- export { sendToAgentChat, type AgentChatMessage } from "../agent-chat.js";
62
+ export {
63
+ sendToAgentChat,
64
+ sendToAgentChatAndConfirm,
65
+ reportAgentChatSubmitResult,
66
+ AGENT_CHAT_SUBMIT_RESULT_EVENT,
67
+ type AgentChatMessage,
68
+ type AgentChatSubmitResult,
69
+ type SendToAgentChatAndConfirmResult,
70
+ } from "../agent-chat.js";
63
71
  export { useAgentChatGenerating } from "../use-agent-chat.js";
64
72
  export { useSendToAgentChat } from "../use-send-to-agent-chat.js";
65
73
  export {
@@ -85,6 +85,31 @@ function serializeChatValue(value: unknown): string | undefined {
85
85
  }
86
86
  }
87
87
 
88
+ /**
89
+ * Slot contexts (e.g. Design's `DesignExtensionSlotContext`) commonly carry
90
+ * live callback functions alongside plain data — the host component uses
91
+ * those callbacks itself, but `window.postMessage` uses the structured clone
92
+ * algorithm, which throws a `DataCloneError` on any function-valued property
93
+ * (see MDN's postMessage docs). Round-tripping through JSON drops functions
94
+ * (and other non-cloneable values like symbols) the same way
95
+ * `JSON.stringify` already silently omits them, producing a payload that's
96
+ * safe to post. Exported so a test can verify functions never reach the
97
+ * iframe instead of only reading the code.
98
+ */
99
+ export function sanitizeSlotContextForPostMessage(
100
+ context: Record<string, unknown> | null | undefined,
101
+ ): Record<string, unknown> {
102
+ try {
103
+ return JSON.parse(JSON.stringify(context ?? {}));
104
+ } catch {
105
+ // Circular reference or other non-serializable shape — fail safe to an
106
+ // empty context rather than letting postMessage throw and skip every
107
+ // other message this handler sends in the same tick (theme update,
108
+ // ready signal).
109
+ return {};
110
+ }
111
+ }
112
+
88
113
  export interface EmbeddedExtensionProps {
89
114
  extensionId: string;
90
115
  /** Slot identifier passed via the iframe URL so the extension runtime knows it's
@@ -243,8 +268,16 @@ export function EmbeddedExtension({
243
268
  useEffect(() => {
244
269
  const win = iframeRef.current?.contentWindow;
245
270
  if (!win) return;
271
+ // Post the JSON-round-tripped context, not the raw `context` object —
272
+ // slot contexts (e.g. Design's DesignExtensionSlotContext) carry live
273
+ // callback functions the host uses internally, and postMessage's
274
+ // structured clone throws a DataCloneError on any function-valued
275
+ // property. See sanitizeSlotContextForPostMessage's docblock.
246
276
  win.postMessage(
247
- { type: "agent-native-slot-context", context: context ?? {} },
277
+ {
278
+ type: "agent-native-slot-context",
279
+ context: sanitizeSlotContextForPostMessage(context),
280
+ },
248
281
  "*",
249
282
  );
250
283
  }, [contextJson]);
@@ -400,7 +433,10 @@ export function EmbeddedExtension({
400
433
  style={{ width: "100%", border: 0, height, display: "block" }}
401
434
  onLoad={() => {
402
435
  iframeRef.current?.contentWindow?.postMessage(
403
- { type: "agent-native-slot-context", context: context ?? {} },
436
+ {
437
+ type: "agent-native-slot-context",
438
+ context: sanitizeSlotContextForPostMessage(context),
439
+ },
404
440
  "*",
405
441
  );
406
442
  // Re-assert theme once the iframe document is live. The src bakes in
@@ -19,6 +19,9 @@ export {
19
19
  requestAgentChatThreadOpen,
20
20
  requestAgentTaskOpen,
21
21
  sendToAgentChat,
22
+ sendToAgentChatAndConfirm,
23
+ reportAgentChatSubmitResult,
24
+ AGENT_CHAT_SUBMIT_RESULT_EVENT,
22
25
  parseSubmitChatMessage,
23
26
  setAgentChatContextItem,
24
27
  setContextToAgentChat,
@@ -33,6 +36,8 @@ export {
33
36
  type AgentChatContextSetOptions,
34
37
  type AgentChatContextState,
35
38
  type AgentChatMessage,
39
+ type AgentChatSubmitResult,
40
+ type SendToAgentChatAndConfirmResult,
36
41
  type AgentComposerReference,
37
42
  type AgentComposerReferenceInsertOptions,
38
43
  type AgentComposerReferenceInsertPayload,
@@ -385,6 +385,8 @@ export function useCollabReconcile({
385
385
  const editorUnchangedSinceApply =
386
386
  lastAppliedSerializedRef.current !== null &&
387
387
  currentMarkdown === lastAppliedSerializedRef.current;
388
+ const typingRecently =
389
+ editor.isFocused && Date.now() - lastTypedAtRef.current < 1500;
388
390
 
389
391
  // Doc-equivalence skip. Never re-apply content the editor already
390
392
  // represents — comparing by DOC EQUIVALENCE, not raw strings/timestamps:
@@ -407,11 +409,15 @@ export function useCollabReconcile({
407
409
  // `<p>` → `&lt;p&gt;` → `&amp;lt;p&amp;gt;` escalation.
408
410
  if (
409
411
  currentMarkdown === normalizedValue ||
410
- value === lastEmittedRef.current ||
411
- // A stale-but-recent echo of our own (possibly partial) save applying
412
- // it would clobber the freshly-typed tail. External edits never match.
413
- recentEmittedRef.current.includes(value) ||
414
- recentEmittedRef.current.includes(normalizedValue) ||
412
+ (typingRecently &&
413
+ // A stale echo of our own (possibly partial) save while the user is
414
+ // actively typing would clobber the fresh tail. Outside active
415
+ // typing, the same bytes can be a deliberate newer external revert
416
+ // (history restore / provider conflict choice) and must be allowed
417
+ // through even if mount-time normalization emitted them.
418
+ (value === lastEmittedRef.current ||
419
+ recentEmittedRef.current.includes(value) ||
420
+ recentEmittedRef.current.includes(normalizedValue))) ||
415
421
  (editorUnchangedSinceApply &&
416
422
  (value === lastAppliedValueRef.current ||
417
423
  normalizedValue === lastAppliedSerializedRef.current))
@@ -442,8 +448,6 @@ export function useCollabReconcile({
442
448
  // for non-idempotent input, fight every keystroke. Newer external content
443
449
  // retries so it still lands once they pause; older-or-equal content is a
444
450
  // stale poll and is dropped outright while focused.
445
- const typingRecently =
446
- editor.isFocused && Date.now() - lastTypedAtRef.current < 1500;
447
451
  if (typingRecently) {
448
452
  if (externalNewer) {
449
453
  retry = setTimeout(() => apply(deferred), 700);
@@ -549,6 +553,13 @@ export function useCollabReconcile({
549
553
 
550
554
  const shouldIgnoreUpdate = (transaction: Transaction): boolean => {
551
555
  if (!editable || isSettingContentRef.current) return true;
556
+ // Collaboration can emit schema/default-paragraph transactions while the
557
+ // persisted Y.Doc is still loading and before the authoritative value has
558
+ // seeded/reconciled. Never let those transient pre-seed updates reach an
559
+ // app's local mirror or autosave path (Content serializes an empty paragraph
560
+ // as `<empty-block/>`, which is non-empty text and bypasses the generic
561
+ // registerEmitted empty-string guard).
562
+ if (collab && !seededRef.current) return true;
552
563
  if (transaction.getMeta(RICH_MARKDOWN_PROGRAMMATIC_TRANSACTION)) {
553
564
  return true;
554
565
  }
@@ -149,23 +149,34 @@ export async function loadAwarenessRows(
149
149
  now: number = Date.now(),
150
150
  ): Promise<AwarenessEntry[]> {
151
151
  try {
152
- await ensureTable();
153
- const client = getDbExec();
154
- const { rows } = await client.execute({
155
- sql: `SELECT client_id, state, last_seen FROM _collab_awareness
156
- WHERE doc_id = ? AND last_seen >= ?`,
157
- args: [docId, now - ROW_TTL_MS],
158
- });
159
- return rows.map((row: any) => ({
160
- clientId: Number(row.client_id),
161
- state: String(row.state),
162
- lastSeen: Number(row.last_seen),
163
- }));
152
+ return await loadAwarenessRowsStrict(docId, now);
164
153
  } catch {
165
154
  return [];
166
155
  }
167
156
  }
168
157
 
158
+ /**
159
+ * Strict awareness read for safety-critical callers that must distinguish
160
+ * "no active participants" from "presence storage could not be read."
161
+ */
162
+ export async function loadAwarenessRowsStrict(
163
+ docId: string,
164
+ now: number = Date.now(),
165
+ ): Promise<AwarenessEntry[]> {
166
+ await ensureTable();
167
+ const client = getDbExec();
168
+ const { rows } = await client.execute({
169
+ sql: `SELECT client_id, state, last_seen FROM _collab_awareness
170
+ WHERE doc_id = ? AND last_seen >= ?`,
171
+ args: [docId, now - ROW_TTL_MS],
172
+ });
173
+ return rows.map((row: any) => ({
174
+ clientId: Number(row.client_id),
175
+ state: String(row.state),
176
+ lastSeen: Number(row.last_seen),
177
+ }));
178
+ }
179
+
169
180
  async function maybePurge(docId: string, now: number): Promise<void> {
170
181
  const last = _lastPurges.get(docId) ?? 0;
171
182
  if (now - last < PURGE_INTERVAL_MS) return;