@agent-native/core 0.83.0 → 0.84.1

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 (508) hide show
  1. package/README.md +2 -2
  2. package/corpus/README.md +1 -1
  3. package/corpus/core/CHANGELOG.md +34 -0
  4. package/corpus/core/README.md +2 -2
  5. package/corpus/core/docs/content/cloneable-saas.mdx +2 -2
  6. package/corpus/core/docs/content/evals.mdx +34 -12
  7. package/corpus/core/docs/content/faq.mdx +1 -1
  8. package/corpus/core/docs/content/locales/ar-SA/cloneable-saas.mdx +2 -2
  9. package/corpus/core/docs/content/locales/ar-SA/evals.mdx +7 -0
  10. package/corpus/core/docs/content/locales/ar-SA/faq.mdx +1 -1
  11. package/corpus/core/docs/content/locales/ar-SA/template-chat.mdx +1 -1
  12. package/corpus/core/docs/content/locales/de-DE/cloneable-saas.mdx +2 -2
  13. package/corpus/core/docs/content/locales/de-DE/evals.mdx +8 -0
  14. package/corpus/core/docs/content/locales/de-DE/faq.mdx +1 -1
  15. package/corpus/core/docs/content/locales/de-DE/template-chat.mdx +1 -1
  16. package/corpus/core/docs/content/locales/es-ES/cloneable-saas.mdx +2 -2
  17. package/corpus/core/docs/content/locales/es-ES/evals.mdx +8 -0
  18. package/corpus/core/docs/content/locales/es-ES/faq.mdx +1 -1
  19. package/corpus/core/docs/content/locales/es-ES/template-chat.mdx +1 -1
  20. package/corpus/core/docs/content/locales/fr-FR/cloneable-saas.mdx +2 -2
  21. package/corpus/core/docs/content/locales/fr-FR/evals.mdx +8 -0
  22. package/corpus/core/docs/content/locales/fr-FR/faq.mdx +1 -1
  23. package/corpus/core/docs/content/locales/fr-FR/template-chat.mdx +1 -1
  24. package/corpus/core/docs/content/locales/hi-IN/cloneable-saas.mdx +2 -2
  25. package/corpus/core/docs/content/locales/hi-IN/evals.mdx +8 -0
  26. package/corpus/core/docs/content/locales/hi-IN/faq.mdx +1 -1
  27. package/corpus/core/docs/content/locales/hi-IN/template-chat.mdx +1 -1
  28. package/corpus/core/docs/content/locales/ja-JP/cloneable-saas.mdx +2 -2
  29. package/corpus/core/docs/content/locales/ja-JP/evals.mdx +7 -0
  30. package/corpus/core/docs/content/locales/ja-JP/faq.mdx +1 -1
  31. package/corpus/core/docs/content/locales/ja-JP/template-chat.mdx +1 -1
  32. package/corpus/core/docs/content/locales/ko-KR/cloneable-saas.mdx +2 -2
  33. package/corpus/core/docs/content/locales/ko-KR/evals.mdx +7 -0
  34. package/corpus/core/docs/content/locales/ko-KR/faq.mdx +1 -1
  35. package/corpus/core/docs/content/locales/ko-KR/template-chat.mdx +1 -1
  36. package/corpus/core/docs/content/locales/pt-BR/cloneable-saas.mdx +2 -2
  37. package/corpus/core/docs/content/locales/pt-BR/evals.mdx +8 -0
  38. package/corpus/core/docs/content/locales/pt-BR/faq.mdx +1 -1
  39. package/corpus/core/docs/content/locales/pt-BR/template-chat.mdx +1 -1
  40. package/corpus/core/docs/content/locales/zh-CN/cloneable-saas.mdx +2 -2
  41. package/corpus/core/docs/content/locales/zh-CN/evals.mdx +7 -0
  42. package/corpus/core/docs/content/locales/zh-CN/faq.mdx +1 -1
  43. package/corpus/core/docs/content/locales/zh-CN/template-chat.mdx +1 -1
  44. package/corpus/core/docs/content/locales/zh-TW/cloneable-saas.mdx +2 -2
  45. package/corpus/core/docs/content/locales/zh-TW/evals.mdx +7 -0
  46. package/corpus/core/docs/content/locales/zh-TW/faq.mdx +1 -1
  47. package/corpus/core/docs/content/locales/zh-TW/template-chat.mdx +1 -1
  48. package/corpus/core/docs/content/template-chat.mdx +1 -1
  49. package/corpus/core/package.json +1 -1
  50. package/corpus/core/src/agent/engine/builtin.ts +1 -1
  51. package/corpus/core/src/agent/engine/output-tokens.ts +1 -1
  52. package/corpus/core/src/agent/harness/acp-adapter.ts +28 -4
  53. package/corpus/core/src/agent/harness/ai-sdk-adapter.ts +3 -0
  54. package/corpus/core/src/agent/harness/translate.ts +3 -0
  55. package/corpus/core/src/agent/harness/types.ts +1 -0
  56. package/corpus/core/src/agent/model-config.ts +34 -13
  57. package/corpus/core/src/agent/production-agent.ts +87 -10
  58. package/corpus/core/src/agent/thread-data-builder.ts +29 -4
  59. package/corpus/core/src/agent/tool-call-journal.ts +109 -48
  60. package/corpus/core/src/agent/types.ts +6 -1
  61. package/corpus/core/src/cli/design-connect.ts +106 -0
  62. package/corpus/core/src/cli/gateway-helpers.ts +17 -0
  63. package/corpus/core/src/cli/recap.ts +0 -2
  64. package/corpus/core/src/cli/skills.ts +54 -16
  65. package/corpus/core/src/cli/workspace-dev.ts +29 -5
  66. package/corpus/core/src/client/AssistantChat.tsx +89 -42
  67. package/corpus/core/src/client/DefaultSpinner.tsx +8 -1
  68. package/corpus/core/src/client/chat/message-components.tsx +49 -1
  69. package/corpus/core/src/client/chat/repo-helpers.ts +71 -0
  70. package/corpus/core/src/client/chat/run-recovery.tsx +15 -5
  71. package/corpus/core/src/client/components/ApiKeySettings.tsx +8 -8
  72. package/corpus/core/src/client/components/CodeRequiredDialog.tsx +26 -15
  73. package/corpus/core/src/client/components/MissingKeyCard.tsx +13 -6
  74. package/corpus/core/src/client/composer/TiptapComposer.tsx +4 -3
  75. package/corpus/core/src/client/extensions/ExtensionsSidebarSection.tsx +0 -1
  76. package/corpus/core/src/client/guided-questions.tsx +21 -27
  77. package/corpus/core/src/client/integrations/IntegrationCard.tsx +1 -1
  78. package/corpus/core/src/client/integrations/IntegrationsPanel.tsx +1 -1
  79. package/corpus/core/src/client/onboarding/OnboardingBanner.tsx +2 -2
  80. package/corpus/core/src/client/onboarding/OnboardingPanel.tsx +23 -19
  81. package/corpus/core/src/client/resources/ResourceEditor.tsx +18 -10
  82. package/corpus/core/src/client/resources/ResourcesPanel.tsx +5 -1
  83. package/corpus/core/src/client/settings/SettingsPanel.tsx +8 -3
  84. package/corpus/core/src/client/settings/useBuilderStatus.ts +8 -41
  85. package/corpus/core/src/client/useProductionAgent.ts +6 -2
  86. package/corpus/core/src/deploy/build.ts +1 -1
  87. package/corpus/core/src/eval/define-eval.ts +4 -1
  88. package/corpus/core/src/eval/report.ts +23 -8
  89. package/corpus/core/src/eval/runner.ts +41 -6
  90. package/corpus/core/src/eval/types.ts +11 -0
  91. package/corpus/core/src/observability/traces.ts +3 -3
  92. package/corpus/core/src/onboarding/default-steps.ts +1 -1
  93. package/corpus/core/src/scripts/agent-engines/manage-agent-engine.ts +1 -1
  94. package/corpus/core/src/scripts/agent-engines/set-agent-engine.ts +1 -1
  95. package/corpus/core/src/server/agent-chat-plugin.ts +177 -81
  96. package/corpus/core/src/server/core-routes-plugin.ts +7 -1
  97. package/corpus/core/src/server/open-route.ts +59 -40
  98. package/corpus/core/src/shared/reasoning-effort.ts +2 -0
  99. package/corpus/core/src/sharing/access.ts +22 -2
  100. package/corpus/core/src/sharing/registry.ts +18 -0
  101. package/corpus/core/src/styles/agent-native.css +104 -20
  102. package/corpus/core/src/styles/rich-markdown-editor.css +11 -10
  103. package/corpus/templates/analytics/app/components/dashboard/CumulativeNetChart.tsx +10 -10
  104. package/corpus/templates/analytics/app/components/dashboard/RevenueChart.tsx +10 -10
  105. package/corpus/templates/analytics/app/components/dashboard/RevenueComparisonChart.tsx +6 -5
  106. package/corpus/templates/analytics/app/components/dashboard/TimeSeriesChart.tsx +9 -9
  107. package/corpus/templates/analytics/app/global.css +4 -0
  108. package/corpus/templates/analytics/app/lib/chart-theme.ts +13 -0
  109. package/corpus/templates/analytics/app/pages/adhoc/_shared/KpiChart.tsx +22 -24
  110. package/corpus/templates/analytics/changelog/2026-06-30-dashboard-charts-now-use-theme-aware-tooltip-and-grid-colors.md +6 -0
  111. package/corpus/templates/analytics/changelog/2026-06-30-fixed-ask-composer-spacing-so-the-chat-field-has-a-comfortab.md +6 -0
  112. package/corpus/templates/calendar/AGENTS.md +4 -0
  113. package/corpus/templates/calendar/actions/connect-google-calendar.ts +80 -0
  114. package/corpus/templates/calendar/changelog/2026-06-30-calendar-now-gives-the-agent-a-safe-google-connection-link-i.md +6 -0
  115. package/corpus/templates/calendar/server/plugins/agent-chat.ts +4 -0
  116. package/corpus/templates/content/AGENTS.md +9 -2
  117. package/corpus/templates/content/actions/_database-row-batch.ts +230 -0
  118. package/corpus/templates/content/actions/_database-utils.ts +17 -9
  119. package/corpus/templates/content/actions/_notion-action-utils.ts +26 -0
  120. package/corpus/templates/content/actions/connect-notion-status.ts +13 -4
  121. package/corpus/templates/content/actions/create-and-link-notion-page.ts +23 -0
  122. package/corpus/templates/content/actions/delete-database-items.ts +55 -0
  123. package/corpus/templates/content/actions/delete-document.ts +4 -4
  124. package/corpus/templates/content/actions/disconnect-notion.ts +16 -0
  125. package/corpus/templates/content/actions/duplicate-database-item.ts +1 -1
  126. package/corpus/templates/content/actions/duplicate-database-items.ts +191 -0
  127. package/corpus/templates/content/actions/link-notion-page.ts +10 -7
  128. package/corpus/templates/content/actions/list-notion-links.ts +3 -4
  129. package/corpus/templates/content/actions/pull-notion-page.ts +7 -9
  130. package/corpus/templates/content/actions/push-notion-page.ts +7 -9
  131. package/corpus/templates/content/actions/refresh-notion-sync-status.ts +24 -0
  132. package/corpus/templates/content/actions/resolve-notion-sync-conflict.ts +23 -0
  133. package/corpus/templates/content/actions/search-notion-pages.ts +67 -0
  134. package/corpus/templates/content/actions/unlink-notion-page.ts +23 -0
  135. package/corpus/templates/content/app/components/editor/BubbleToolbar.tsx +5 -5
  136. package/corpus/templates/content/app/components/editor/DocumentDatabase.tsx +27 -34
  137. package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +16 -13
  138. package/corpus/templates/content/app/components/editor/DocumentToolbar.tsx +10 -10
  139. package/corpus/templates/content/app/components/editor/NotionConflictBanner.tsx +1 -1
  140. package/corpus/templates/content/app/components/editor/NotionSyncBar.tsx +18 -9
  141. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +27 -34
  142. package/corpus/templates/content/app/components/editor/database/navigation-state.ts +10 -4
  143. package/corpus/templates/content/app/components/sidebar/NotionButton.tsx +32 -17
  144. package/corpus/templates/content/app/global.css +6 -5
  145. package/corpus/templates/content/app/hooks/use-content-database.ts +35 -0
  146. package/corpus/templates/content/app/hooks/use-notion.ts +108 -99
  147. package/corpus/templates/content/changelog/2026-06-30-editor-floating-toolbars-now-use-theme-aware-colors-in-light.md +6 -0
  148. package/corpus/templates/content/package.json +3 -0
  149. package/corpus/templates/content/parity/README.md +69 -0
  150. package/corpus/templates/content/parity/eval-scenarios.ts +118 -0
  151. package/corpus/templates/content/parity/exceptions.allowlist.ts +17 -0
  152. package/corpus/templates/content/parity/matrix.md +28 -0
  153. package/corpus/templates/content/parity/matrix.ts +606 -0
  154. package/corpus/templates/content/parity/matrix.types.ts +42 -0
  155. package/corpus/templates/content/parity/parity-evals.eval.ts +4 -0
  156. package/corpus/templates/content/parity/render-matrix.ts +90 -0
  157. package/corpus/templates/content/parity/scenario-to-eval.ts +57 -0
  158. package/corpus/templates/content/shared/api.ts +11 -0
  159. package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +6 -1
  160. package/corpus/templates/design/actions/add-localhost-screens.ts +63 -8
  161. package/corpus/templates/design/actions/apply-motion-edit.ts +12 -46
  162. package/corpus/templates/design/actions/list-design-native-assets.ts +2 -1
  163. package/corpus/templates/design/actions/migrate-board-objects-to-file.ts +49 -1
  164. package/corpus/templates/design/actions/navigate.ts +3 -12
  165. package/corpus/templates/design/actions/open-visual-edit.ts +341 -0
  166. package/corpus/templates/design/actions/view-screen.ts +1 -1
  167. package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +1 -1
  168. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +345 -14
  169. package/corpus/templates/design/app/components/design/DesignEditorSkeleton.tsx +71 -91
  170. package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +25 -27
  171. package/corpus/templates/design/app/components/design/DeviceFrame.tsx +1 -1
  172. package/corpus/templates/design/app/components/design/DrawOverlay.tsx +2 -2
  173. package/corpus/templates/design/app/components/design/EditPanel.tsx +1111 -570
  174. package/corpus/templates/design/app/components/design/LayersPanel.tsx +162 -32
  175. package/corpus/templates/design/app/components/design/MotionDock.tsx +268 -240
  176. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +1038 -150
  177. package/corpus/templates/design/app/components/design/QuestionFlow.tsx +1 -1
  178. package/corpus/templates/design/app/components/design/ReviewPanel.tsx +81 -102
  179. package/corpus/templates/design/app/components/design/StatesPanel.tsx +6 -6
  180. package/corpus/templates/design/app/components/design/TokensPanel.tsx +11 -11
  181. package/corpus/templates/design/app/components/design/TweaksPanel.tsx +9 -7
  182. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +1129 -155
  183. package/corpus/templates/design/app/components/design/bridge/hit-test.bridge.ts +130 -37
  184. package/corpus/templates/design/app/components/design/canvas-primitive-style.ts +24 -11
  185. package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +184 -162
  186. package/corpus/templates/design/app/components/design/inspector/ConstraintsWidget.tsx +137 -79
  187. package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +13 -13
  188. package/corpus/templates/design/app/components/design/inspector/ExportSettingsPanel.tsx +8 -8
  189. package/corpus/templates/design/app/components/design/inspector/GradientEditor.tsx +2 -2
  190. package/corpus/templates/design/app/components/design/inspector/ImageFillControls.tsx +4 -4
  191. package/corpus/templates/design/app/components/design/inspector/InspectorAiActions.tsx +3 -3
  192. package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +13 -7
  193. package/corpus/templates/design/app/components/design/inspector/ShaderControls.tsx +14 -11
  194. package/corpus/templates/design/app/components/design/inspector/ShaderFillsPanel.tsx +4 -4
  195. package/corpus/templates/design/app/components/design/inspector/index.ts +2 -0
  196. package/corpus/templates/design/app/components/design/types.ts +19 -0
  197. package/corpus/templates/design/app/components/editor/PromptDialog.tsx +61 -78
  198. package/corpus/templates/design/app/components/layout/Header.tsx +0 -1
  199. package/corpus/templates/design/app/components/layout/Layout.tsx +3 -2
  200. package/corpus/templates/design/app/components/layout/Sidebar.tsx +0 -2
  201. package/corpus/templates/design/app/components/visual-editor/CanvasCommentPins.tsx +5 -4
  202. package/corpus/templates/design/app/components/visual-editor/DrawOverlay.tsx +1 -1
  203. package/corpus/templates/design/app/components/visual-editor/SaveStatusIndicator.tsx +2 -2
  204. package/corpus/templates/design/app/global.css +56 -0
  205. package/corpus/templates/design/app/hooks/use-agent-generating.ts +4 -5
  206. package/corpus/templates/design/app/hooks/use-navigation-state.ts +6 -7
  207. package/corpus/templates/design/app/hooks/use-question-flow.ts +3 -2
  208. package/corpus/templates/design/app/hooks/useAgentEditRequest.ts +6 -4
  209. package/corpus/templates/design/app/i18n/zh-TW.ts +5 -14
  210. package/corpus/templates/design/app/i18n-data.ts +63 -188
  211. package/corpus/templates/design/app/lib/agent-chat.ts +13 -0
  212. package/corpus/templates/design/app/pages/DesignEditor.tsx +1428 -384
  213. package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +9 -5
  214. package/corpus/templates/design/app/pages/DesignSystems.tsx +2 -2
  215. package/corpus/templates/design/app/pages/Index.tsx +49 -26
  216. package/corpus/templates/design/app/pages/NotFound.tsx +25 -8
  217. package/corpus/templates/design/app/pages/VisualEdit.tsx +17 -5
  218. package/corpus/templates/design/app/routes/examples.tsx +1 -7
  219. package/corpus/templates/design/app/routes/templates.tsx +7 -4
  220. package/corpus/templates/design/changelog/2026-06-30-board-shapes-and-auto-layout-controls-are-clearer.md +6 -0
  221. package/corpus/templates/design/changelog/2026-06-30-component-selections-now-use-purple-component-chrome-and-sho.md +6 -0
  222. package/corpus/templates/design/changelog/2026-06-30-constraints-now-stay-tucked-into-a-compact-inspector-preview.md +6 -0
  223. package/corpus/templates/design/changelog/2026-06-30-creating-a-component-now-opens-in-a-compact-inspector-popove.md +6 -0
  224. package/corpus/templates/design/changelog/2026-06-30-design-cards-no-longer-show-legacy-project-type-badges.md +6 -0
  225. package/corpus/templates/design/changelog/2026-06-30-design-chat-now-keeps-new-turns-in-the-visible-design-agent-.md +6 -0
  226. package/corpus/templates/design/changelog/2026-06-30-design-editor-panels-now-keep-their-compact-text-sizing-cons.md +6 -0
  227. package/corpus/templates/design/changelog/2026-06-30-design-no-longer-shows-the-connected-local-code-warning-bann.md +6 -0
  228. package/corpus/templates/design/changelog/2026-06-30-design-now-opens-to-the-files-panel-unless-a-generation-is-a.md +6 -0
  229. package/corpus/templates/design/changelog/2026-06-30-design-opens-with-a-cleaner-empty-canvas-and-loading-preview.md +6 -0
  230. package/corpus/templates/design/changelog/2026-06-30-design-tools-use-simpler-less-decorative-icons.md +6 -0
  231. package/corpus/templates/design/changelog/2026-06-30-dragging-absolute-elements-no-longer-shows-coordinate-labels.md +6 -0
  232. package/corpus/templates/design/changelog/2026-06-30-fixed-all-screens-opening-too-zoomed-in-by-default.md +6 -0
  233. package/corpus/templates/design/changelog/2026-06-30-fixed-the-public-design-assets-sidebar-so-native-components-.md +6 -0
  234. package/corpus/templates/design/changelog/2026-06-30-fixed-the-screen-overview-board-so-empty-board-space-no-long.md +6 -0
  235. package/corpus/templates/design/changelog/2026-06-30-full-view-screens-can-now-scroll-while-remaining-editable.md +6 -0
  236. package/corpus/templates/design/changelog/2026-06-30-inspector-element-headers-show-the-selected-html-tag-and-the.md +6 -0
  237. package/corpus/templates/design/changelog/2026-06-30-inspector-inputs-stay-compact-across-desktop-breakpoints.md +6 -0
  238. package/corpus/templates/design/changelog/2026-06-30-layers-can-be-multi-selected-from-the-canvas-and-edited-toge.md +6 -0
  239. package/corpus/templates/design/changelog/2026-06-30-local-visual-edit-links-can-edit-localhost-frames-without-si.md +6 -0
  240. package/corpus/templates/design/changelog/2026-06-30-localhost-visual-edit-frames-can-now-be-selected-and-edited.md +6 -0
  241. package/corpus/templates/design/changelog/2026-06-30-marquee-selection-now-works-inside-screens-multi-selected-la.md +6 -0
  242. package/corpus/templates/design/changelog/2026-06-30-motion-is-now-available-from-the-persistent-left-rail-and-th.md +6 -0
  243. package/corpus/templates/design/changelog/2026-06-30-motion-timeline-collapse-and-reopen-animations-feel-smoother.md +6 -0
  244. package/corpus/templates/design/changelog/2026-06-30-multi-select-outlines-now-stay-consistent-across-all-screens.md +6 -0
  245. package/corpus/templates/design/changelog/2026-06-30-new-board-shapes-now-start-with-neutral-gray-styling.md +6 -0
  246. package/corpus/templates/design/changelog/2026-06-30-new-designs-open-into-a-steadier-loading-state-before-the-ed.md +6 -0
  247. package/corpus/templates/design/changelog/2026-06-30-new-designs-show-a-loading-state-during-handoff-and-keep-edi.md +6 -0
  248. package/corpus/templates/design/changelog/2026-06-30-public-visual-edit-design-links-now-open-directly-instead-of.md +6 -0
  249. package/corpus/templates/design/changelog/2026-06-30-removed-the-confusing-states-section-from-the-design-editor-.md +6 -0
  250. package/corpus/templates/design/changelog/2026-06-30-removed-the-templates-gallery-from-design-navigation.md +6 -0
  251. package/corpus/templates/design/changelog/2026-06-30-share-stays-visible-as-soon-as-editable-designs-load.md +6 -0
  252. package/corpus/templates/design/changelog/2026-06-30-signed-out-save-and-share-controls-use-simpler-labels-and-op.md +6 -0
  253. package/corpus/templates/design/changelog/2026-06-30-text-added-on-the-canvas-now-focuses-cleanly-with-the-right-.md +6 -0
  254. package/corpus/templates/design/changelog/2026-06-30-the-design-editor-loading-skeleton-is-simpler-and-its-bottom.md +6 -0
  255. package/corpus/templates/design/changelog/2026-06-30-the-design-editor-loading-skeleton-now-follows-the-current-t.md +6 -0
  256. package/corpus/templates/design/changelog/2026-06-30-the-design-inspector-no-longer-shows-child-layout-controls-o.md +6 -0
  257. package/corpus/templates/design/changelog/2026-06-30-the-design-inspector-no-longer-shows-the-ask-ai-section-abov.md +6 -0
  258. package/corpus/templates/design/changelog/2026-06-30-the-design-review-panel-no-longer-shows-a-separate-inline-au.md +6 -0
  259. package/corpus/templates/design/changelog/2026-06-30-the-empty-designs-page-now-focuses-on-a-single-primary-new-d.md +6 -0
  260. package/corpus/templates/design/changelog/2026-06-30-the-layers-panel-header-stays-as-layers-while-multiple-items.md +6 -0
  261. package/corpus/templates/design/changelog/2026-06-30-the-missing-design-screen-now-uses-a-neutral-back-button.md +6 -0
  262. package/corpus/templates/design/changelog/2026-06-30-the-motion-timeline-now-closes-without-squeezing-the-canvas-.md +6 -0
  263. package/corpus/templates/design/changelog/2026-06-30-the-new-design-connect-ai-setup-now-lines-up-with-the-compos.md +6 -0
  264. package/corpus/templates/design/changelog/2026-06-30-the-screen-overview-board-now-blends-into-the-canvas-and-new.md +6 -0
  265. package/corpus/templates/design/changelog/2026-06-30-top-level-canvas-drops-now-show-screen-guides.md +6 -0
  266. package/corpus/templates/design/changelog/2026-06-30-top-level-canvas-layers-now-keep-a-transparent-backdrop-and-.md +6 -0
  267. package/corpus/templates/design/changelog/2026-06-30-top-level-canvas-layers-now-select-reliably-from-all-screens.md +6 -0
  268. package/corpus/templates/design/changelog/2026-06-30-visual-edit-can-open-and-refresh-localhost-screens-in-one-au.md +6 -0
  269. package/corpus/templates/design/e2e/helpers.ts +15 -4
  270. package/corpus/templates/design/server/db/index.ts +52 -0
  271. package/corpus/templates/design/server/plugins/agent-chat.ts +5 -0
  272. package/corpus/templates/design/server/plugins/auth.ts +1 -0
  273. package/corpus/templates/design/server/plugins/core-routes.ts +4 -0
  274. package/corpus/templates/design/shared/board-file.ts +380 -5
  275. package/corpus/templates/design/shared/board-objects.ts +2 -2
  276. package/corpus/templates/design/shared/code-layer.ts +50 -6
  277. package/corpus/templates/design/shared/motion-compiler.ts +27 -0
  278. package/corpus/templates/macros/app/components/DailyProgress.tsx +41 -37
  279. package/corpus/templates/macros/app/components/ExerciseCard.tsx +2 -2
  280. package/corpus/templates/macros/app/components/MealCard.tsx +2 -2
  281. package/corpus/templates/macros/app/components/VoiceDictation.tsx +1 -1
  282. package/corpus/templates/macros/app/components/WeeklyCaloriesChart.tsx +1 -1
  283. package/corpus/templates/macros/app/components/WeightCard.tsx +2 -2
  284. package/corpus/templates/macros/app/components/WeightTracker.tsx +1 -1
  285. package/corpus/templates/macros/app/components/layout/AppLayout.tsx +1 -1
  286. package/corpus/templates/macros/app/routes/_index.tsx +5 -5
  287. package/corpus/templates/macros/app/routes/analytics.tsx +1 -1
  288. package/corpus/templates/macros/changelog/2026-06-30-dashboard-loading-and-chart-surfaces-now-use-theme-aware-col.md +6 -0
  289. package/corpus/templates/mail/app/components/email/ComposeBubbleToolbar.tsx +14 -11
  290. package/corpus/templates/mail/app/global.css +3 -2
  291. package/corpus/templates/mail/changelog/2026-06-30-compose-floating-toolbars-now-use-theme-aware-colors-in-ligh.md +6 -0
  292. package/corpus/templates/plan/app/pages/PlansPage.tsx +2 -2
  293. package/corpus/templates/slides/app/components/deck/DeckCard.tsx +3 -3
  294. package/corpus/templates/slides/app/global.css +10 -0
  295. package/corpus/templates/slides/app/pages/Index.tsx +55 -51
  296. package/corpus/templates/slides/changelog/2026-06-30-deck-thumbnails-collapse-cleanly-to-one-column-when-the-chat.md +6 -0
  297. package/dist/agent/engine/anthropic-engine.d.ts +2 -2
  298. package/dist/agent/engine/anthropic-engine.d.ts.map +1 -1
  299. package/dist/agent/engine/builder-engine.d.ts +2 -2
  300. package/dist/agent/engine/builder-engine.d.ts.map +1 -1
  301. package/dist/agent/engine/builtin.js +1 -1
  302. package/dist/agent/engine/builtin.js.map +1 -1
  303. package/dist/agent/engine/output-tokens.js +1 -1
  304. package/dist/agent/engine/output-tokens.js.map +1 -1
  305. package/dist/agent/harness/acp-adapter.d.ts +4 -1
  306. package/dist/agent/harness/acp-adapter.d.ts.map +1 -1
  307. package/dist/agent/harness/acp-adapter.js +19 -2
  308. package/dist/agent/harness/acp-adapter.js.map +1 -1
  309. package/dist/agent/harness/ai-sdk-adapter.d.ts.map +1 -1
  310. package/dist/agent/harness/ai-sdk-adapter.js +3 -0
  311. package/dist/agent/harness/ai-sdk-adapter.js.map +1 -1
  312. package/dist/agent/harness/translate.d.ts.map +1 -1
  313. package/dist/agent/harness/translate.js +3 -0
  314. package/dist/agent/harness/translate.js.map +1 -1
  315. package/dist/agent/harness/types.d.ts +1 -0
  316. package/dist/agent/harness/types.d.ts.map +1 -1
  317. package/dist/agent/harness/types.js.map +1 -1
  318. package/dist/agent/model-config.d.ts +21 -20
  319. package/dist/agent/model-config.d.ts.map +1 -1
  320. package/dist/agent/model-config.js +31 -13
  321. package/dist/agent/model-config.js.map +1 -1
  322. package/dist/agent/production-agent.d.ts.map +1 -1
  323. package/dist/agent/production-agent.js +85 -10
  324. package/dist/agent/production-agent.js.map +1 -1
  325. package/dist/agent/thread-data-builder.d.ts.map +1 -1
  326. package/dist/agent/thread-data-builder.js +27 -6
  327. package/dist/agent/thread-data-builder.js.map +1 -1
  328. package/dist/agent/tool-call-journal.d.ts +11 -9
  329. package/dist/agent/tool-call-journal.d.ts.map +1 -1
  330. package/dist/agent/tool-call-journal.js +92 -40
  331. package/dist/agent/tool-call-journal.js.map +1 -1
  332. package/dist/agent/types.d.ts +5 -1
  333. package/dist/agent/types.d.ts.map +1 -1
  334. package/dist/agent/types.js.map +1 -1
  335. package/dist/cli/design-connect.d.ts.map +1 -1
  336. package/dist/cli/design-connect.js +84 -0
  337. package/dist/cli/design-connect.js.map +1 -1
  338. package/dist/cli/gateway-helpers.d.ts +4 -0
  339. package/dist/cli/gateway-helpers.d.ts.map +1 -1
  340. package/dist/cli/gateway-helpers.js +9 -0
  341. package/dist/cli/gateway-helpers.js.map +1 -1
  342. package/dist/cli/recap.d.ts.map +1 -1
  343. package/dist/cli/recap.js +0 -2
  344. package/dist/cli/recap.js.map +1 -1
  345. package/dist/cli/skills.d.ts.map +1 -1
  346. package/dist/cli/skills.js +54 -16
  347. package/dist/cli/skills.js.map +1 -1
  348. package/dist/cli/workspace-dev.d.ts.map +1 -1
  349. package/dist/cli/workspace-dev.js +33 -6
  350. package/dist/cli/workspace-dev.js.map +1 -1
  351. package/dist/client/AssistantChat.d.ts.map +1 -1
  352. package/dist/client/AssistantChat.js +72 -26
  353. package/dist/client/AssistantChat.js.map +1 -1
  354. package/dist/client/DefaultSpinner.d.ts.map +1 -1
  355. package/dist/client/DefaultSpinner.js +8 -1
  356. package/dist/client/DefaultSpinner.js.map +1 -1
  357. package/dist/client/chat/message-components.d.ts +7 -1
  358. package/dist/client/chat/message-components.d.ts.map +1 -1
  359. package/dist/client/chat/message-components.js +37 -1
  360. package/dist/client/chat/message-components.js.map +1 -1
  361. package/dist/client/chat/repo-helpers.d.ts +1 -0
  362. package/dist/client/chat/repo-helpers.d.ts.map +1 -1
  363. package/dist/client/chat/repo-helpers.js +59 -0
  364. package/dist/client/chat/repo-helpers.js.map +1 -1
  365. package/dist/client/chat/run-recovery.d.ts.map +1 -1
  366. package/dist/client/chat/run-recovery.js +4 -4
  367. package/dist/client/chat/run-recovery.js.map +1 -1
  368. package/dist/client/components/ApiKeySettings.js +8 -8
  369. package/dist/client/components/ApiKeySettings.js.map +1 -1
  370. package/dist/client/components/CodeRequiredDialog.d.ts.map +1 -1
  371. package/dist/client/components/CodeRequiredDialog.js +24 -15
  372. package/dist/client/components/CodeRequiredDialog.js.map +1 -1
  373. package/dist/client/components/MissingKeyCard.d.ts.map +1 -1
  374. package/dist/client/components/MissingKeyCard.js +11 -6
  375. package/dist/client/components/MissingKeyCard.js.map +1 -1
  376. package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
  377. package/dist/client/composer/TiptapComposer.js +4 -3
  378. package/dist/client/composer/TiptapComposer.js.map +1 -1
  379. package/dist/client/extensions/ExtensionsSidebarSection.d.ts.map +1 -1
  380. package/dist/client/extensions/ExtensionsSidebarSection.js +1 -1
  381. package/dist/client/extensions/ExtensionsSidebarSection.js.map +1 -1
  382. package/dist/client/guided-questions.d.ts.map +1 -1
  383. package/dist/client/guided-questions.js +6 -6
  384. package/dist/client/guided-questions.js.map +1 -1
  385. package/dist/client/integrations/IntegrationCard.js +1 -1
  386. package/dist/client/integrations/IntegrationCard.js.map +1 -1
  387. package/dist/client/integrations/IntegrationsPanel.js +1 -1
  388. package/dist/client/integrations/IntegrationsPanel.js.map +1 -1
  389. package/dist/client/onboarding/OnboardingBanner.js +2 -2
  390. package/dist/client/onboarding/OnboardingBanner.js.map +1 -1
  391. package/dist/client/onboarding/OnboardingPanel.js +23 -19
  392. package/dist/client/onboarding/OnboardingPanel.js.map +1 -1
  393. package/dist/client/resources/ResourceEditor.d.ts.map +1 -1
  394. package/dist/client/resources/ResourceEditor.js +13 -10
  395. package/dist/client/resources/ResourceEditor.js.map +1 -1
  396. package/dist/client/resources/ResourcesPanel.d.ts.map +1 -1
  397. package/dist/client/resources/ResourcesPanel.js +2 -1
  398. package/dist/client/resources/ResourcesPanel.js.map +1 -1
  399. package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
  400. package/dist/client/settings/SettingsPanel.js +8 -3
  401. package/dist/client/settings/SettingsPanel.js.map +1 -1
  402. package/dist/client/settings/useBuilderStatus.d.ts.map +1 -1
  403. package/dist/client/settings/useBuilderStatus.js +6 -38
  404. package/dist/client/settings/useBuilderStatus.js.map +1 -1
  405. package/dist/client/useProductionAgent.d.ts +2 -1
  406. package/dist/client/useProductionAgent.d.ts.map +1 -1
  407. package/dist/client/useProductionAgent.js.map +1 -1
  408. package/dist/collab/awareness.d.ts +2 -2
  409. package/dist/collab/awareness.d.ts.map +1 -1
  410. package/dist/collab/routes.d.ts +1 -1
  411. package/dist/deploy/build.js +1 -1
  412. package/dist/deploy/build.js.map +1 -1
  413. package/dist/eval/define-eval.d.ts.map +1 -1
  414. package/dist/eval/define-eval.js +2 -1
  415. package/dist/eval/define-eval.js.map +1 -1
  416. package/dist/eval/report.d.ts.map +1 -1
  417. package/dist/eval/report.js +19 -4
  418. package/dist/eval/report.js.map +1 -1
  419. package/dist/eval/runner.d.ts.map +1 -1
  420. package/dist/eval/runner.js +38 -6
  421. package/dist/eval/runner.js.map +1 -1
  422. package/dist/eval/types.d.ts +11 -0
  423. package/dist/eval/types.d.ts.map +1 -1
  424. package/dist/eval/types.js.map +1 -1
  425. package/dist/file-upload/actions/upload-image.d.ts +2 -2
  426. package/dist/notifications/routes.d.ts +3 -3
  427. package/dist/observability/routes.d.ts +5 -5
  428. package/dist/observability/traces.d.ts.map +1 -1
  429. package/dist/observability/traces.js.map +1 -1
  430. package/dist/onboarding/default-steps.js +1 -1
  431. package/dist/onboarding/default-steps.js.map +1 -1
  432. package/dist/progress/routes.d.ts +1 -1
  433. package/dist/resources/handlers.d.ts +1 -1
  434. package/dist/scripts/agent-engines/manage-agent-engine.js +1 -1
  435. package/dist/scripts/agent-engines/manage-agent-engine.js.map +1 -1
  436. package/dist/scripts/agent-engines/set-agent-engine.js +1 -1
  437. package/dist/scripts/agent-engines/set-agent-engine.js.map +1 -1
  438. package/dist/server/agent-chat-plugin.d.ts +3 -0
  439. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  440. package/dist/server/agent-chat-plugin.js +134 -72
  441. package/dist/server/agent-chat-plugin.js.map +1 -1
  442. package/dist/server/core-routes-plugin.d.ts +3 -0
  443. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  444. package/dist/server/core-routes-plugin.js +4 -1
  445. package/dist/server/core-routes-plugin.js.map +1 -1
  446. package/dist/server/open-route.d.ts +9 -0
  447. package/dist/server/open-route.d.ts.map +1 -1
  448. package/dist/server/open-route.js +48 -38
  449. package/dist/server/open-route.js.map +1 -1
  450. package/dist/shared/reasoning-effort.js +3 -0
  451. package/dist/shared/reasoning-effort.js.map +1 -1
  452. package/dist/sharing/access.d.ts.map +1 -1
  453. package/dist/sharing/access.js +18 -2
  454. package/dist/sharing/access.js.map +1 -1
  455. package/dist/sharing/registry.d.ts +10 -0
  456. package/dist/sharing/registry.d.ts.map +1 -1
  457. package/dist/sharing/registry.js.map +1 -1
  458. package/dist/styles/agent-native.css +104 -20
  459. package/dist/styles/rich-markdown-editor.css +11 -10
  460. package/docs/content/cloneable-saas.mdx +2 -2
  461. package/docs/content/evals.mdx +34 -12
  462. package/docs/content/faq.mdx +1 -1
  463. package/docs/content/locales/ar-SA/cloneable-saas.mdx +2 -2
  464. package/docs/content/locales/ar-SA/evals.mdx +7 -0
  465. package/docs/content/locales/ar-SA/faq.mdx +1 -1
  466. package/docs/content/locales/ar-SA/template-chat.mdx +1 -1
  467. package/docs/content/locales/de-DE/cloneable-saas.mdx +2 -2
  468. package/docs/content/locales/de-DE/evals.mdx +8 -0
  469. package/docs/content/locales/de-DE/faq.mdx +1 -1
  470. package/docs/content/locales/de-DE/template-chat.mdx +1 -1
  471. package/docs/content/locales/es-ES/cloneable-saas.mdx +2 -2
  472. package/docs/content/locales/es-ES/evals.mdx +8 -0
  473. package/docs/content/locales/es-ES/faq.mdx +1 -1
  474. package/docs/content/locales/es-ES/template-chat.mdx +1 -1
  475. package/docs/content/locales/fr-FR/cloneable-saas.mdx +2 -2
  476. package/docs/content/locales/fr-FR/evals.mdx +8 -0
  477. package/docs/content/locales/fr-FR/faq.mdx +1 -1
  478. package/docs/content/locales/fr-FR/template-chat.mdx +1 -1
  479. package/docs/content/locales/hi-IN/cloneable-saas.mdx +2 -2
  480. package/docs/content/locales/hi-IN/evals.mdx +8 -0
  481. package/docs/content/locales/hi-IN/faq.mdx +1 -1
  482. package/docs/content/locales/hi-IN/template-chat.mdx +1 -1
  483. package/docs/content/locales/ja-JP/cloneable-saas.mdx +2 -2
  484. package/docs/content/locales/ja-JP/evals.mdx +7 -0
  485. package/docs/content/locales/ja-JP/faq.mdx +1 -1
  486. package/docs/content/locales/ja-JP/template-chat.mdx +1 -1
  487. package/docs/content/locales/ko-KR/cloneable-saas.mdx +2 -2
  488. package/docs/content/locales/ko-KR/evals.mdx +7 -0
  489. package/docs/content/locales/ko-KR/faq.mdx +1 -1
  490. package/docs/content/locales/ko-KR/template-chat.mdx +1 -1
  491. package/docs/content/locales/pt-BR/cloneable-saas.mdx +2 -2
  492. package/docs/content/locales/pt-BR/evals.mdx +8 -0
  493. package/docs/content/locales/pt-BR/faq.mdx +1 -1
  494. package/docs/content/locales/pt-BR/template-chat.mdx +1 -1
  495. package/docs/content/locales/zh-CN/cloneable-saas.mdx +2 -2
  496. package/docs/content/locales/zh-CN/evals.mdx +7 -0
  497. package/docs/content/locales/zh-CN/faq.mdx +1 -1
  498. package/docs/content/locales/zh-CN/template-chat.mdx +1 -1
  499. package/docs/content/locales/zh-TW/cloneable-saas.mdx +2 -2
  500. package/docs/content/locales/zh-TW/evals.mdx +7 -0
  501. package/docs/content/locales/zh-TW/faq.mdx +1 -1
  502. package/docs/content/locales/zh-TW/template-chat.mdx +1 -1
  503. package/docs/content/template-chat.mdx +1 -1
  504. package/package.json +1 -1
  505. package/corpus/templates/content/server/routes/api/notion/disconnect.post.ts +0 -12
  506. package/corpus/templates/design/app/components/design/LocalSourceEditBanner.tsx +0 -157
  507. package/corpus/templates/design/app/lib/design-templates.ts +0 -1305
  508. package/corpus/templates/design/app/pages/Templates.tsx +0 -279
@@ -278,8 +278,17 @@ function getMessageRunId(message: any): string | undefined {
278
278
  }
279
279
 
280
280
  function messageContentIsEmpty(content: unknown): boolean {
281
- if (Array.isArray(content)) return content.length === 0;
282
- return content == null || content === "";
281
+ if (typeof content === "string") return content.trim().length === 0;
282
+ if (Array.isArray(content)) {
283
+ return !content.some((part: any) => {
284
+ if (!part || typeof part !== "object") return false;
285
+ if (part.type === "text") {
286
+ return typeof part.text === "string" && part.text.trim().length > 0;
287
+ }
288
+ return true;
289
+ });
290
+ }
291
+ return content == null;
283
292
  }
284
293
 
285
294
  function messageText(content: unknown): string {
@@ -790,7 +799,15 @@ export function mergeThreadDataForClientSave(
790
799
  const idRewrites = new Map<string, string>();
791
800
 
792
801
  for (const existingEntry of existingMessages) {
793
- const existingKeys = messageIdentityKeys(getStoredMessage(existingEntry));
802
+ const existingMessage = getStoredMessage(existingEntry);
803
+ if (
804
+ existingMessage?.role === "assistant" &&
805
+ messageContentIsEmpty(existingMessage.content)
806
+ ) {
807
+ continue;
808
+ }
809
+
810
+ const existingKeys = messageIdentityKeys(existingMessage);
794
811
  const incomingIndex = incomingKeySets.findIndex(
795
812
  (keys: Set<string>, index: number) =>
796
813
  !usedIncoming.has(index) && existingKeys.some((key) => keys.has(key)),
@@ -813,7 +830,15 @@ export function mergeThreadDataForClientSave(
813
830
  }
814
831
 
815
832
  for (let index = 0; index < incomingMessages.length; index++) {
816
- if (!usedIncoming.has(index)) nextMessages.push(incomingMessages[index]);
833
+ if (usedIncoming.has(index)) continue;
834
+ const incomingMessage = getStoredMessage(incomingMessages[index]);
835
+ if (
836
+ incomingMessage?.role === "assistant" &&
837
+ messageContentIsEmpty(incomingMessage.content)
838
+ ) {
839
+ continue;
840
+ }
841
+ nextMessages.push(incomingMessages[index]);
817
842
  }
818
843
 
819
844
  merged.messages = nextMessages.map((entry) =>
@@ -8,16 +8,18 @@
8
8
  * told about any tool call that started but whose outcome was never recorded.
9
9
  *
10
10
  * IMPORTANT — this is a pure read-over-the-ledger view. There is no new
11
- * recording hook anywhere in the hot path. The classification reuses the exact
12
- * positional `tool_start` → `tool_done` matching that `thread-data-builder.ts`
13
- * already relies on to rebuild durable turns:
11
+ * recording hook anywhere in the hot path. Classification uses input-aware
12
+ * `tool_start` → `tool_done` matching when a `tool_done` event carries its
13
+ * input, while preserving the legacy positional fallback that older ledger
14
+ * events and `thread-data-builder.ts` rely on:
14
15
  *
15
16
  * - `tool_start` events carry `{ tool, input }` (no tool-call id at this
16
17
  * layer; the ledger event stream omits it).
17
- * - `tool_done` events carry `{ tool, result }`.
18
- * - A `tool_done` is matched to the OLDEST still-open `tool_start` for the
19
- * same tool name (FIFO per tool), mirroring how the dispatch loop emits a
20
- * start immediately before its matching done.
18
+ * - `tool_done` events carry `{ tool, input?, result }`.
19
+ * - A `tool_done` with input is matched to the open start with the same tool
20
+ * name and deep-canonicalized input.
21
+ * - A legacy `tool_done` without input is matched to the OLDEST still-open
22
+ * `tool_start` for the same tool name (FIFO per tool).
21
23
  *
22
24
  * A `tool_start` with no matching `tool_done` is the dangerous case: the call
23
25
  * began, its side effect may or may not have landed, and the interruption ate
@@ -32,7 +34,7 @@
32
34
  * effect again. Layer 2 is the stronger guarantee.
33
35
  */
34
36
 
35
- import type { AgentChatEvent } from "./types.js";
37
+ import type { AgentChatEvent, AgentToolInput } from "./types.js";
36
38
 
37
39
  /** A single recorded tool-call ledger entry, classified by outcome. */
38
40
  export interface ToolCallJournalEntry {
@@ -46,7 +48,7 @@ export interface ToolCallJournalEntry {
46
48
  /** Tool / action name (from the `tool_start` event). */
47
49
  tool: string;
48
50
  /** Tool input captured at `tool_start`, if any. */
49
- input?: Record<string, string>;
51
+ input?: AgentToolInput;
50
52
  /** 0-based position of the `tool_start` among all tool calls in the turn. */
51
53
  order: number;
52
54
  /** Result text from the matched `tool_done`, when the call completed. */
@@ -70,22 +72,53 @@ const INPUT_SIGNATURE_MAX_CHARS = 120;
70
72
  /** Max length of a per-tool-call result summary surfaced in the resume note. */
71
73
  const RESULT_SUMMARY_MAX_CHARS = 400;
72
74
 
73
- function inputSignature(input: Record<string, string> | undefined): string {
74
- if (!input) return "";
75
- let sig: string;
75
+ function inputSignature(input: unknown): string {
76
+ if (input == null) return "";
76
77
  try {
77
- // Stable key order so the same logical input produces the same signature
78
- // regardless of how the engine serialized the object.
79
- const sorted = Object.keys(input)
80
- .sort()
81
- .reduce<Record<string, string>>((acc, k) => {
82
- acc[k] = input[k];
83
- return acc;
84
- }, {});
85
- sig = JSON.stringify(sorted);
78
+ return JSON.stringify(canonicalizeForSignature(input));
86
79
  } catch {
87
- sig = String(input);
80
+ return String(input);
81
+ }
82
+ }
83
+
84
+ function canonicalizeForSignature(
85
+ input: unknown,
86
+ seen = new WeakSet(),
87
+ ): unknown {
88
+ if (input == null) return input;
89
+ if (typeof input === "bigint") return input.toString();
90
+ if (typeof input === "function" || typeof input === "symbol") {
91
+ return String(input);
92
+ }
93
+ if (typeof input !== "object") return input;
94
+
95
+ if (seen.has(input)) return "[Circular]";
96
+ seen.add(input);
97
+ if (Array.isArray(input)) {
98
+ const output = input.map((value) =>
99
+ value === undefined
100
+ ? "[Undefined]"
101
+ : canonicalizeForSignature(value, seen),
102
+ );
103
+ seen.delete(input);
104
+ return output;
88
105
  }
106
+
107
+ const object = input as Record<string, unknown>;
108
+ const output: Record<string, unknown> = {};
109
+ for (const key of Object.keys(object).sort()) {
110
+ const value = object[key];
111
+ output[key] =
112
+ value === undefined
113
+ ? "[Undefined]"
114
+ : canonicalizeForSignature(value, seen);
115
+ }
116
+ seen.delete(input);
117
+ return output;
118
+ }
119
+
120
+ function displayInputSignature(input: unknown): string {
121
+ const sig = inputSignature(input);
89
122
  return sig.length > INPUT_SIGNATURE_MAX_CHARS
90
123
  ? sig.slice(0, INPUT_SIGNATURE_MAX_CHARS)
91
124
  : sig;
@@ -101,10 +134,9 @@ function inputSignature(input: Record<string, string> | undefined): string {
101
134
  export function classifyToolCallJournal(
102
135
  events: readonly AgentChatEvent[],
103
136
  ): ToolCallJournal {
104
- // Open tool_start entries awaiting a matching tool_done, in FIFO order. We
105
- // index by tool name so a tool_done matches the OLDEST open start for that
106
- // same tool the dispatch loop always emits start-then-done per call, so the
107
- // first unmatched start of a given name is the one this done belongs to.
137
+ // Open tool_start entries awaiting a matching tool_done. We index by tool
138
+ // name first, then prefer matching input signatures when the done event
139
+ // carries input. Older ledger entries without done input fall back to FIFO.
108
140
  const openByTool = new Map<string, ToolCallJournalEntry[]>();
109
141
  const completed: ToolCallJournalEntry[] = [];
110
142
  let order = 0;
@@ -119,11 +151,9 @@ export function classifyToolCallJournal(
119
151
 
120
152
  if (event.type === "tool_start") {
121
153
  const tool = event.tool ?? "unknown";
122
- const input = (event.input ?? undefined) as
123
- | Record<string, string>
124
- | undefined;
154
+ const input = event.input ?? undefined;
125
155
  const entry: ToolCallJournalEntry = {
126
- key: `${tool}#${order}:${inputSignature(input)}`,
156
+ key: `${tool}#${order}:${displayInputSignature(input)}`,
127
157
  tool,
128
158
  ...(input ? { input } : {}),
129
159
  order,
@@ -138,8 +168,14 @@ export function classifyToolCallJournal(
138
168
  if (event.type === "tool_done") {
139
169
  const tool = event.tool ?? "unknown";
140
170
  const queue = openByTool.get(tool);
141
- const entry = queue?.shift();
171
+ const entry = takeMatchingOpenEntry(queue, event);
142
172
  if (entry) {
173
+ if (isNonCompletedToolDone(event)) {
174
+ // Failed/blocked calls reached a terminal tool_done, but their side
175
+ // effect did not complete. Drop the matching start instead of
176
+ // classifying it as completed or interrupted.
177
+ continue;
178
+ }
143
179
  entry.result = event.result ?? "";
144
180
  completed.push(entry);
145
181
  }
@@ -159,6 +195,45 @@ export function classifyToolCallJournal(
159
195
  return { completed, interrupted };
160
196
  }
161
197
 
198
+ function takeMatchingOpenEntry(
199
+ queue: ToolCallJournalEntry[] | undefined,
200
+ event: Extract<AgentChatEvent, { type: "tool_done" }>,
201
+ ): ToolCallJournalEntry | undefined {
202
+ if (!queue || queue.length === 0) return undefined;
203
+ if (event.input !== undefined) {
204
+ const doneSig = inputSignature(event.input);
205
+ const index = queue.findIndex(
206
+ (entry) => inputSignature(entry.input) === doneSig,
207
+ );
208
+ if (index >= 0) return queue.splice(index, 1)[0];
209
+ }
210
+ return queue.shift();
211
+ }
212
+
213
+ function isNonCompletedToolDone(
214
+ event: Extract<AgentChatEvent, { type: "tool_done" }>,
215
+ ): boolean {
216
+ if (event.completedSideEffect === false) return true;
217
+ if (event.isError === true) return true;
218
+
219
+ const result = (event.result ?? "").trim();
220
+ if (!result) return false;
221
+
222
+ // Legacy run events did not persist isError. Keep known non-execution results
223
+ // from becoming durable "completed" write calls after deploy.
224
+ return (
225
+ result.startsWith("Error: Unknown tool") ||
226
+ result.startsWith("Error running ") ||
227
+ result.startsWith("Invalid action parameters for ") ||
228
+ result.startsWith("Plan mode blocked ") ||
229
+ result.startsWith("Skipped ") ||
230
+ result.startsWith("Stopped after ") ||
231
+ result.startsWith("The tool was not executed") ||
232
+ result.startsWith("Awaiting human approval") ||
233
+ result.includes(" did NOT execute")
234
+ );
235
+ }
236
+
162
237
  /** True when the journal has nothing worth telling a resuming model about. */
163
238
  export function isJournalEmpty(journal: ToolCallJournal): boolean {
164
239
  return journal.completed.length === 0 && journal.interrupted.length === 0;
@@ -184,7 +259,7 @@ export function findCompletedJournalEntry(
184
259
  input: unknown,
185
260
  consumedKeys?: Set<string>,
186
261
  ): ToolCallJournalEntry | undefined {
187
- const wantSig = inputSignature(normalizeInputForSignature(input));
262
+ const wantSig = inputSignature(input);
188
263
  for (const entry of journal.completed) {
189
264
  if (entry.tool !== toolName) continue;
190
265
  if (inputSignature(entry.input) !== wantSig) continue;
@@ -195,20 +270,6 @@ export function findCompletedJournalEntry(
195
270
  return undefined;
196
271
  }
197
272
 
198
- /**
199
- * Coerce an arbitrary tool input into the `Record<string, string>` shape the
200
- * journal recorded at `tool_start` so signatures compare apples-to-apples. The
201
- * ledger stores `tool_start.input` as a string map; the live call's `input` is
202
- * the parsed object — both pass through `inputSignature`, which sorts keys and
203
- * JSON-stringifies, so a plain object compares correctly.
204
- */
205
- function normalizeInputForSignature(
206
- input: unknown,
207
- ): Record<string, string> | undefined {
208
- if (input == null || typeof input !== "object") return undefined;
209
- return input as Record<string, string>;
210
- }
211
-
212
273
  function summarizeResult(result: string | undefined): string {
213
274
  if (!result) return "(no result recorded)";
214
275
  const oneLine = result.replace(/\s+/g, " ").trim();
@@ -218,9 +279,9 @@ function summarizeResult(result: string | undefined): string {
218
279
  : oneLine;
219
280
  }
220
281
 
221
- function describeInput(input: Record<string, string> | undefined): string {
282
+ function describeInput(input: unknown): string {
222
283
  if (!input) return "";
223
- const sig = inputSignature(input);
284
+ const sig = displayInputSignature(input);
224
285
  return sig && sig !== "{}" ? ` input: ${sig}` : "";
225
286
  }
226
287
 
@@ -207,16 +207,21 @@ export interface AgentChatRequest {
207
207
  approvedToolCalls?: string[];
208
208
  }
209
209
 
210
+ export type AgentToolInput = Record<string, unknown>;
211
+
210
212
  export type AgentChatEvent =
211
213
  | { type: "text"; text: string }
212
214
  | { type: "thinking"; text: string }
213
215
  | { type: "activity"; label: string; tool?: string }
214
216
  | { type: "stream_keepalive" }
215
- | { type: "tool_start"; tool: string; input: Record<string, string> }
217
+ | { type: "tool_start"; tool: string; input: AgentToolInput }
216
218
  | {
217
219
  type: "tool_done";
218
220
  tool: string;
221
+ input?: AgentToolInput;
219
222
  result: string;
223
+ isError?: boolean;
224
+ completedSideEffect?: boolean;
220
225
  mcpApp?: AgentMcpAppPayload;
221
226
  chatUI?: ActionChatUIConfig;
222
227
  }
@@ -402,6 +402,78 @@ function sendJson(
402
402
  res.end(`${JSON.stringify(body, null, 2)}\n`);
403
403
  }
404
404
 
405
+ function sameOrigin(a: string, b: string): boolean {
406
+ try {
407
+ return new URL(a).origin === new URL(b).origin;
408
+ } catch {
409
+ return false;
410
+ }
411
+ }
412
+
413
+ function resolvePreviewSnapshotUrl(
414
+ devServerUrl: string,
415
+ rawUrl: string | null,
416
+ ): string {
417
+ const base = normalizeHttpUrl(devServerUrl);
418
+ const parsed = new URL(rawUrl?.trim() || "/", `${base}/`);
419
+ parsed.hash = "";
420
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
421
+ throw new Error("Snapshot URL must use http(s).");
422
+ }
423
+ if (!sameOrigin(parsed.toString(), base)) {
424
+ throw new Error("Snapshot URL must stay on the connected dev server.");
425
+ }
426
+ return parsed.toString();
427
+ }
428
+
429
+ async function fetchPreviewSnapshot(
430
+ devServerUrl: string,
431
+ targetUrl: string,
432
+ redirects = 0,
433
+ ): Promise<{
434
+ url: string;
435
+ status: number;
436
+ contentType: string;
437
+ html: string;
438
+ }> {
439
+ if (redirects > 5) {
440
+ throw new Error("Too many redirects while fetching preview snapshot.");
441
+ }
442
+ const controller = new AbortController();
443
+ const timeout = setTimeout(() => controller.abort(), 10_000);
444
+ try {
445
+ const response = await fetch(targetUrl, {
446
+ method: "GET",
447
+ redirect: "manual",
448
+ signal: controller.signal,
449
+ headers: {
450
+ accept: "text/html,application/xhtml+xml;q=0.9,*/*;q=0.8",
451
+ },
452
+ });
453
+ const location = response.headers.get("location");
454
+ if (location && response.status >= 300 && response.status < 400) {
455
+ const redirected = new URL(location, targetUrl);
456
+ redirected.hash = "";
457
+ if (!sameOrigin(redirected.toString(), devServerUrl)) {
458
+ throw new Error("Snapshot redirect left the connected dev server.");
459
+ }
460
+ return fetchPreviewSnapshot(
461
+ devServerUrl,
462
+ redirected.toString(),
463
+ redirects + 1,
464
+ );
465
+ }
466
+ return {
467
+ url: response.url || targetUrl,
468
+ status: response.status,
469
+ contentType: response.headers.get("content-type") ?? "",
470
+ html: await response.text(),
471
+ };
472
+ } finally {
473
+ clearTimeout(timeout);
474
+ }
475
+ }
476
+
405
477
  /** Read the full request body as a UTF-8 string. */
406
478
  async function readRequestBody(req: IncomingMessage): Promise<string> {
407
479
  return new Promise<string>((resolve, reject) => {
@@ -514,6 +586,40 @@ export async function startDesignConnectBridge(
514
586
  sendJson(res, 200, { ok: true, source: manifest.source });
515
587
  return;
516
588
  }
589
+ if (pathname === "/snapshot") {
590
+ if (req.method !== "GET") {
591
+ sendJson(res, 405, { ok: false, error: "method not allowed" });
592
+ return;
593
+ }
594
+ void (async () => {
595
+ try {
596
+ const requestUrl = new URL(req.url ?? "/", manifest.bridgeUrl);
597
+ const targetUrl = resolvePreviewSnapshotUrl(
598
+ manifest.devServerUrl,
599
+ requestUrl.searchParams.get("url") ??
600
+ requestUrl.searchParams.get("path"),
601
+ );
602
+ const snapshot = await fetchPreviewSnapshot(
603
+ manifest.devServerUrl,
604
+ targetUrl,
605
+ );
606
+ sendJson(res, snapshot.status >= 400 ? snapshot.status : 200, {
607
+ ok: snapshot.status < 400,
608
+ source: manifest.source,
609
+ url: snapshot.url,
610
+ status: snapshot.status,
611
+ contentType: snapshot.contentType,
612
+ html: snapshot.html,
613
+ });
614
+ } catch (err: unknown) {
615
+ sendJson(res, 400, {
616
+ ok: false,
617
+ error: err instanceof Error ? err.message : String(err),
618
+ });
619
+ }
620
+ })();
621
+ return;
622
+ }
517
623
 
518
624
  // ── Token-gated write endpoints (POST only) ───────────────────────────
519
625
 
@@ -1,3 +1,20 @@
1
+ import type { EventEmitter } from "node:events";
2
+
3
+ type ErrorEventEmitter = Pick<EventEmitter, "on"> & object;
4
+
5
+ const gatewaySocketsWithErrorSink = new WeakSet<object>();
6
+
7
+ export function attachGatewaySocketErrorSink(
8
+ socket: ErrorEventEmitter | null | undefined,
9
+ onError?: (error: unknown) => void,
10
+ ): void {
11
+ if (!socket || gatewaySocketsWithErrorSink.has(socket)) return;
12
+ gatewaySocketsWithErrorSink.add(socket);
13
+ socket.on("error", (error) => {
14
+ onError?.(error);
15
+ });
16
+ }
17
+
1
18
  /**
2
19
  * When an app returns a redirect to a root-relative path that doesn't already
3
20
  * include the app prefix, prepend it. This handles the common case where a
@@ -1995,7 +1995,6 @@ export function buildCommentBody(env: NodeJS.ProcessEnv = process.env): string {
1995
1995
 
1996
1996
  lines.push(`Here's a [visual recap](${safeUrl}) of what changed:`);
1997
1997
  lines.push("");
1998
- lines.push(`<a href="${safeUrl}">`);
1999
1998
  lines.push(`<picture>`);
2000
1999
  if (lightImageUrl && darkImageUrl) {
2001
2000
  lines.push(
@@ -2004,7 +2003,6 @@ export function buildCommentBody(env: NodeJS.ProcessEnv = process.env): string {
2004
2003
  }
2005
2004
  lines.push(` <img alt="Visual recap" src="${fallbackImageUrl}">`);
2006
2005
  lines.push(`</picture>`);
2007
- lines.push(`</a>`);
2008
2006
  lines.push("");
2009
2007
  lines.push(`**Open the [full interactive recap](${safeUrl})**`);
2010
2008
  if (env.DIFF_HUGE === "true") {
@@ -449,6 +449,10 @@ iframe-backed screens on the infinite canvas.
449
449
  - The \`/visual-edit\` entry route can open before the viewer signs in. Public
450
450
  \`/design/:id\` editor links can also render read-only public designs without a
451
451
  session.
452
+ - Prefer links returned by Design actions or \`/_agent-native/open\` deep links.
453
+ Do not surface URLs with \`_session=\` tokens. Query sessions are only a
454
+ fallback after normal cookie resolution, so an existing browser session can
455
+ still open the design as a different user and show "Design not found".
452
456
  - Do not attempt anonymous write actions. Bridge registration, design creation,
453
457
  screen placement, generation, saving, and sharing are account-backed. If a
454
458
  signed-out visitor wants to save or share, send them through the framework
@@ -471,30 +475,44 @@ For one-shot agent setup, ask for JSON and keep the long-running bridge open in
471
475
  a second terminal if the user needs live updates:
472
476
 
473
477
  \`\`\`bash
474
- npx @agent-native/core@latest design connect --url http://localhost:5173 --root . --json
478
+ npx @agent-native/core@latest design connect --url http://localhost:5173 --root .
479
+ curl http://127.0.0.1:7331/manifest.json
475
480
  \`\`\`
476
481
 
482
+ Do not use \`--json\` for an editable session. \`--json\`, \`--once\`, and
483
+ \`--dry-run\` print the manifest and exit, so Design will fall back to a
484
+ non-editable live iframe as soon as it tries to refresh the snapshot.
485
+
477
486
  ## Action Flow
478
487
 
479
- 1. Register or refresh the bridge with \`connect-localhost\`, passing the
480
- \`/manifest.json\` result as \`routeManifest\` and \`capabilities\`.
481
- 2. Create or reuse a Design project with \`create-design\`.
482
- 3. Place URL-backed screens with \`add-localhost-screens\`:
488
+ Prefer the single authenticated \`open-visual-edit\` action. It registers or
489
+ refreshes the localhost bridge, creates or reuses a Design project, places
490
+ URL-backed screens, stores the active visual-edit context, and navigates to
491
+ overview mode in one call. This avoids creating a private design under a
492
+ synthetic CLI user and then handing the browser a tokenized URL that may be
493
+ shadowed by an existing session.
483
494
 
484
495
  \`\`\`bash
485
- pnpm action add-localhost-screens '{
486
- "designId": "<design-id>",
487
- "connectionId": "<connection-id>",
496
+ pnpm action open-visual-edit '{
497
+ "title": "Docs homepage visual edit",
498
+ "devServerUrl": "http://localhost:5173",
499
+ "bridgeUrl": "http://127.0.0.1:7331",
500
+ "rootPath": "/absolute/path/to/app",
501
+ "routeManifest": { "...": "from /manifest.json" },
488
502
  "paths": ["/", "/pricing", "/checkout?step=payment"]
489
503
  }'
490
504
  \`\`\`
491
505
 
506
+ The action returns \`designId\`, \`connectionId\`, \`screens\`, \`urlPath\`, and
507
+ \`openUrl\`. Keep those IDs in the chat context for follow-ups.
508
+
492
509
  For a numbered flow the user describes in chat, keep the labels and order:
493
510
 
494
511
  \`\`\`bash
495
- pnpm action add-localhost-screens '{
496
- "designId": "<design-id>",
497
- "connectionId": "<connection-id>",
512
+ pnpm action open-visual-edit '{
513
+ "designId": "<existing-design-id>",
514
+ "connectionId": "<existing-connection-id>",
515
+ "devServerUrl": "http://localhost:1234",
498
516
  "routes": [
499
517
  { "url": "localhost:1234/onboarding/1", "title": "Screen 1" },
500
518
  { "url": "localhost:1234/onboarding/2", "title": "Screen 2" },
@@ -503,21 +521,41 @@ pnpm action add-localhost-screens '{
503
521
  }'
504
522
  \`\`\`
505
523
 
506
- If no \`routes\` or \`paths\` are supplied, \`add-localhost-screens\` uses every
507
- route from the latest localhost manifest.
508
-
509
- 4. Navigate to overview mode:
524
+ For responsive follow-ups, call \`open-visual-edit\` again with the same
525
+ \`designId\` and \`connectionId\`, plus explicit viewport dimensions:
510
526
 
511
527
  \`\`\`bash
512
- pnpm action navigate --view editor --designId "<design-id>" --editorView overview
528
+ pnpm action open-visual-edit '{
529
+ "designId": "<existing-design-id>",
530
+ "connectionId": "<existing-connection-id>",
531
+ "devServerUrl": "http://localhost:5173",
532
+ "paths": ["/"],
533
+ "defaultWidth": 390,
534
+ "defaultHeight": 844,
535
+ "startX": 1600,
536
+ "startY": 0
537
+ }'
513
538
  \`\`\`
514
539
 
540
+ If no \`routes\` or \`paths\` are supplied, \`open-visual-edit\` uses every route
541
+ from the localhost manifest.
542
+
543
+ Fallback, only when \`open-visual-edit\` is unavailable:
544
+
545
+ 1. Register or refresh the bridge with \`connect-localhost\`, passing the
546
+ \`/manifest.json\` result as \`routeManifest\` and \`capabilities\`.
547
+ 2. Create or reuse a Design project with \`create-design\`.
548
+ 3. Place URL-backed screens with \`add-localhost-screens\`.
549
+ 4. Navigate to overview mode with \`navigate\`.
550
+
515
551
  ## Open The Design Surface
516
552
 
517
553
  - Use the \`link\`, \`deepLink\`, or MCP App embed returned by Design actions so
518
554
  the user sees the canvas. In Codex Desktop or VS Code, prefer opening that
519
555
  Design URL in the available preview/webview panel; otherwise surface the
520
556
  "Open design" link.
557
+ - Return or open the \`openUrl\` / action link, not a hand-built
558
+ \`/design/:id?_session=...\` URL.
521
559
  - If the user is working in VS Code, the Agent Native extension can open the
522
560
  same URL via
523
561
  \`vscode://builder.agent-native/open?url=<encoded-design-url>\`. Its
@@ -17,6 +17,7 @@ import {
17
17
  type WorkspaceAppAudience,
18
18
  } from "../shared/workspace-app-audience.js";
19
19
  import {
20
+ attachGatewaySocketErrorSink,
20
21
  normalizeOrigin,
21
22
  rewriteRedirectLocation,
22
23
  escapeHtml,
@@ -1048,9 +1049,18 @@ export async function runWorkspaceDev(
1048
1049
  if (rewritten) responseHeaders.location = rewritten;
1049
1050
  }
1050
1051
  res.writeHead(statusCode, responseHeaders);
1052
+ proxyRes.once("error", () => {
1053
+ if (!res.destroyed) res.destroy();
1054
+ });
1051
1055
  proxyRes.pipe(res);
1052
1056
  },
1053
1057
  );
1058
+ proxyReq.once("socket", (socket) => {
1059
+ attachGatewaySocketErrorSink(socket);
1060
+ });
1061
+ res.once("error", () => {
1062
+ proxyReq.destroy();
1063
+ });
1054
1064
  responseTimer = setTimeout(() => {
1055
1065
  if (settled) return;
1056
1066
  settled = true;
@@ -1123,6 +1133,13 @@ export async function runWorkspaceDev(
1123
1133
  head: Buffer,
1124
1134
  ): void {
1125
1135
  startApp(app);
1136
+ let target: net.Socket | undefined;
1137
+ attachGatewaySocketErrorSink(socket, () => {
1138
+ target?.destroy();
1139
+ });
1140
+ socket.once("close", () => {
1141
+ target?.destroy();
1142
+ });
1126
1143
  void waitForPort(app.port, Date.now() + proxyReadyTimeoutMs).then(
1127
1144
  (ready) => {
1128
1145
  if (!ready) {
@@ -1130,8 +1147,9 @@ export async function runWorkspaceDev(
1130
1147
  socket.destroy();
1131
1148
  return;
1132
1149
  }
1150
+ if (socket.destroyed) return;
1133
1151
  app.ready = true;
1134
- const target = net.connect(app.port, "127.0.0.1", () => {
1152
+ const upstream = net.connect(app.port, "127.0.0.1", () => {
1135
1153
  const headers = Object.entries(
1136
1154
  proxyHeaders(req, `127.0.0.1:${app.port}`),
1137
1155
  )
@@ -1141,14 +1159,20 @@ export async function runWorkspaceDev(
1141
1159
  : [`${key}: ${value ?? ""}`],
1142
1160
  )
1143
1161
  .join("\r\n");
1144
- target.write(
1162
+ upstream.write(
1145
1163
  `${req.method} ${req.url} HTTP/${req.httpVersion}\r\n${headers}\r\n\r\n`,
1146
1164
  );
1147
- if (head.length) target.write(head);
1148
- socket.pipe(target).pipe(socket);
1165
+ if (head.length) upstream.write(head);
1166
+ socket.pipe(upstream).pipe(socket);
1149
1167
  });
1168
+ target = upstream;
1150
1169
 
1151
- target.on("error", () => socket.destroy());
1170
+ attachGatewaySocketErrorSink(upstream, () => {
1171
+ if (!socket.destroyed) socket.destroy();
1172
+ });
1173
+ upstream.once("close", () => {
1174
+ if (!socket.destroyed) socket.destroy();
1175
+ });
1152
1176
  },
1153
1177
  );
1154
1178
  }