@agent-native/core 0.131.5 → 0.131.7

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 (350) hide show
  1. package/corpus/README.md +3 -3
  2. package/corpus/core/CHANGELOG.md +70 -0
  3. package/corpus/core/docs/content/agent-native-toolkit.mdx +13 -6
  4. package/corpus/core/docs/content/cloneable-saas.mdx +5 -5
  5. package/corpus/core/docs/content/creating-templates.mdx +9 -0
  6. package/corpus/core/docs/content/deployment.mdx +18 -12
  7. package/corpus/core/docs/content/doctor.mdx +8 -1
  8. package/corpus/core/docs/content/drop-in-agent.mdx +1 -1
  9. package/corpus/core/docs/content/extensions.mdx +18 -18
  10. package/corpus/core/docs/content/faq.mdx +4 -4
  11. package/corpus/core/docs/content/key-concepts.mdx +30 -21
  12. package/corpus/core/docs/content/security.mdx +2 -2
  13. package/corpus/core/docs/content/template-brain-developers.mdx +4 -1
  14. package/corpus/core/docs/content/template-mail-developers.mdx +4 -1
  15. package/corpus/core/docs/content/what-is-agent-native.mdx +6 -6
  16. package/corpus/core/package.json +1 -1
  17. package/corpus/core/src/a2a/client.ts +449 -66
  18. package/corpus/core/src/a2a/correlation.ts +31 -0
  19. package/corpus/core/src/a2a/invoke.ts +4 -2
  20. package/corpus/core/src/a2a/types.ts +4 -0
  21. package/corpus/core/src/action.ts +3 -0
  22. package/corpus/core/src/agent/action-continuation-guidance.ts +2 -1
  23. package/corpus/core/src/agent/engine/registry.ts +98 -6
  24. package/corpus/core/src/agent/production-agent.ts +151 -18
  25. package/corpus/core/src/agent/run-loop-with-resume.ts +190 -3
  26. package/corpus/core/src/agent/run-manager.ts +117 -41
  27. package/corpus/core/src/agent/thread-debug-history.ts +86 -0
  28. package/corpus/core/src/agent/types.ts +3 -1
  29. package/corpus/core/src/cli/create.ts +11 -1
  30. package/corpus/core/src/cli/templates-meta.ts +2 -2
  31. package/corpus/core/src/client/agent-chat-adapter.ts +91 -13
  32. package/corpus/core/src/client/resources/McpIntegrationDialog.tsx +11 -9
  33. package/corpus/core/src/client/sse-event-processor.ts +17 -3
  34. package/corpus/core/src/db/client.ts +57 -17
  35. package/corpus/core/src/guards/no-env-credentials.ts +2 -0
  36. package/corpus/core/src/integrations/adapters/slack.ts +8 -3
  37. package/corpus/core/src/localization/default-messages.ts +44 -0
  38. package/corpus/core/src/observability/traces.ts +78 -5
  39. package/corpus/core/src/scripts/call-agent.ts +294 -11
  40. package/corpus/core/src/scripts/describe-workspace-apps.ts +2 -2
  41. package/corpus/core/src/secrets/crypto.ts +156 -35
  42. package/corpus/core/src/secrets/storage.ts +64 -26
  43. package/corpus/core/src/server/agent-capabilities.ts +1 -1
  44. package/corpus/core/src/server/agent-chat/action-filters-a2a.ts +113 -7
  45. package/corpus/core/src/server/agent-chat/context-tools.ts +2 -0
  46. package/corpus/core/src/server/agent-chat/plugin-options.ts +34 -1
  47. package/corpus/core/src/server/agent-chat/prompt-resources.ts +1 -1
  48. package/corpus/core/src/server/agent-chat-plugin.ts +91 -59
  49. package/corpus/core/src/templates/default/.agents/skills/secrets/SKILL.md +19 -7
  50. package/corpus/core/src/templates/headless/.agents/skills/secrets/SKILL.md +19 -7
  51. package/corpus/core/src/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +18 -7
  52. package/corpus/core/src/templates/workspace-core/.agents/skills/composable-mini-apps/SKILL.md +15 -14
  53. package/corpus/core/src/templates/workspace-core/.agents/skills/secrets/SKILL.md +19 -7
  54. package/corpus/core/src/vite/client.ts +1 -0
  55. package/corpus/templates/analytics/.agents/skills/dashboard-ops/SKILL.md +5 -4
  56. package/corpus/templates/analytics/.agents/skills/gong/SKILL.md +6 -4
  57. package/corpus/templates/analytics/AGENTS.md +7 -4
  58. package/corpus/templates/analytics/actions/query-agent-native-analytics.ts +4 -3
  59. package/corpus/templates/analytics/changelog/2026-07-30-delegated-analytics-questions-now-return-a-useful-current-me.md +6 -0
  60. package/corpus/templates/analytics/changelog/2026-07-30-scheduled-dashboard-emails-complete-every-panel-without-pool.md +6 -0
  61. package/corpus/templates/analytics/scripts/emit-netlify-dashboard-report-cron.ts +28 -1
  62. package/corpus/templates/analytics/server/jobs/dashboard-report.ts +18 -11
  63. package/corpus/templates/analytics/server/lib/analytics-connector-catalog.ts +4 -4
  64. package/corpus/templates/analytics/server/lib/canonical-first-party-dashboard-repair.ts +9 -5
  65. package/corpus/templates/analytics/server/lib/dashboard-panel-query.ts +6 -2
  66. package/corpus/templates/analytics/server/lib/dashboard-panel-source-resolver.ts +11 -4
  67. package/corpus/templates/analytics/server/lib/dashboard-report-render.ts +49 -31
  68. package/corpus/templates/analytics/server/lib/first-party-analytics.ts +16 -8
  69. package/corpus/templates/analytics/server/lib/first-party-metric-catalog.ts +9 -3
  70. package/corpus/templates/analytics/server/lib/production-serverless-runtime.ts +11 -0
  71. package/corpus/templates/analytics/server/lib/real-data-actions.ts +10 -0
  72. package/corpus/templates/analytics/server/plugins/agent-chat.ts +3 -2
  73. package/corpus/templates/analytics/server/plugins/analytics-alert-jobs.ts +2 -1
  74. package/corpus/templates/analytics/server/plugins/dashboard-report-jobs.ts +2 -1
  75. package/corpus/templates/analytics/server/plugins/uptime-monitor-jobs.ts +1 -12
  76. package/corpus/templates/brain/.agents/skills/brain/RUNBOOK.md +54 -6
  77. package/corpus/templates/brain/.agents/skills/brain/SKILL.md +20 -9
  78. package/corpus/templates/brain/.agents/skills/ingestion-and-connectors/SKILL.md +33 -1
  79. package/corpus/templates/brain/AGENTS.md +3 -1
  80. package/corpus/templates/brain/README.md +4 -1
  81. package/corpus/templates/brain/actions/_schemas.ts +30 -8
  82. package/corpus/templates/brain/actions/ask-brain.ts +124 -14
  83. package/corpus/templates/brain/actions/create-source.ts +18 -2
  84. package/corpus/templates/brain/actions/enqueue-captures-distillation.ts +8 -123
  85. package/corpus/templates/brain/actions/enqueue-distillation.ts +6 -127
  86. package/corpus/templates/brain/actions/import-capture.ts +17 -5
  87. package/corpus/templates/brain/actions/import-transcript.ts +17 -5
  88. package/corpus/templates/brain/actions/update-source.ts +25 -5
  89. package/corpus/templates/brain/app/components/layout/Sidebar.tsx +1 -1
  90. package/corpus/templates/brain/changelog/2026-07-30-blessed-resources-can-feed-cited-company-answers.md +6 -0
  91. package/corpus/templates/brain/changelog/2026-07-30-the-left-sidebar-no-longer-shows-a-blank-organization-placeh.md +6 -0
  92. package/corpus/templates/brain/server/lib/brain.ts +49 -19
  93. package/corpus/templates/brain/server/lib/distillation-queue.ts +147 -0
  94. package/corpus/templates/brain/server/lib/source-policy.ts +264 -0
  95. package/corpus/templates/brain/server/plugins/agent-chat.ts +3 -2
  96. package/corpus/templates/brain/server/routes/api/_agent-native/brain/ingest.post.ts +116 -19
  97. package/corpus/templates/clips/app/components/meetings/google-oauth-identity-notice.tsx +51 -0
  98. package/corpus/templates/clips/app/i18n/en-US.ts +4 -0
  99. package/corpus/templates/clips/app/routes/_app.meetings._index.tsx +3 -1
  100. package/corpus/templates/clips/changelog/2026-07-30-google-calendar-warning.md +6 -0
  101. package/corpus/templates/content/app/components/editor/VisualEditor.tsx +72 -8
  102. package/corpus/templates/content/changelog/2026-07-30-the-slash-command-hint-now-stays-on-only-the-active-empty-li.md +6 -0
  103. package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +506 -11
  104. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +148 -17
  105. package/corpus/templates/design/app/components/design/canvas-interactions/design-canvas-interactions.ts +357 -0
  106. package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +7 -466
  107. package/corpus/templates/design/app/components/design/inspector/scrub-input-utils.ts +15 -311
  108. package/corpus/templates/design/app/components/visual-editor/CanvasCommentPins.tsx +19 -997
  109. package/corpus/templates/design/app/components/visual-editor/DrawOverlay.tsx +8 -1216
  110. package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +31 -38
  111. package/corpus/templates/design/package.json +0 -1
  112. package/corpus/templates/dispatch/AGENTS.md +8 -0
  113. package/corpus/templates/dispatch/DEVELOPING.md +29 -0
  114. package/corpus/templates/dispatch/actions/view-screen.ts +34 -2
  115. package/corpus/templates/dispatch/app/i18n-data.ts +457 -0
  116. package/corpus/templates/dispatch/changelog/2026-07-30-delegated-dispatch-work-continues-reliably-in-the-background.md +6 -0
  117. package/corpus/templates/dispatch/changelog/2026-07-30-find-failed-runs-across-apps.md +6 -0
  118. package/corpus/templates/dispatch/netlify.toml +3 -0
  119. package/corpus/templates/plan/actions/get-visual-plan.ts +10 -6
  120. package/corpus/templates/plan/actions/patch-visual-plan-source.ts +117 -1
  121. package/corpus/templates/plan/actions/update-visual-plan.ts +56 -14
  122. package/corpus/templates/plan/app/pages/PlansPage.tsx +1 -1
  123. package/corpus/templates/plan/changelog/2026-07-30-plan-agents-recover-with-targeted-writes.md +6 -0
  124. package/corpus/templates/plan/shared/plan-content.ts +35 -0
  125. package/corpus/templates/slides/.agents/skills/analytics-data-for-decks/SKILL.md +8 -12
  126. package/corpus/templates/slides/AGENTS.md +4 -4
  127. package/corpus/templates/slides/actions/create-deck.ts +1 -1
  128. package/corpus/templates/slides/actions/duplicate-deck.ts +19 -4
  129. package/corpus/templates/slides/app/components/deck/DeckCard.tsx +2 -10
  130. package/corpus/templates/slides/app/components/deck/MermaidRenderer.tsx +12 -1
  131. package/corpus/templates/slides/app/components/deck/SlideRenderer.tsx +30 -13
  132. package/corpus/templates/slides/app/components/editor/DeckEditorSkeleton.tsx +35 -0
  133. package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +63 -56
  134. package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +21 -76
  135. package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +1110 -292
  136. package/corpus/templates/slides/app/components/editor/SlideOverflowWarning.tsx +53 -0
  137. package/corpus/templates/slides/app/components/editor/SlideStyleInspector.tsx +235 -18
  138. package/corpus/templates/slides/app/components/editor/SpeakerNotesPanel.tsx +4 -1
  139. package/corpus/templates/slides/app/components/editor/canvas-interactions/index.ts +11 -0
  140. package/corpus/templates/slides/app/components/editor/canvas-interactions/slides-canvas-adapter.ts +129 -0
  141. package/corpus/templates/slides/app/components/editor/selection-overlay-measurement.ts +70 -0
  142. package/corpus/templates/slides/app/components/editor/slide-object-interactions.ts +519 -65
  143. package/corpus/templates/slides/app/components/visual-editor/CanvasCommentPins.tsx +36 -426
  144. package/corpus/templates/slides/app/components/visual-editor/DrawOverlay.tsx +19 -458
  145. package/corpus/templates/slides/app/context/DeckContext.tsx +179 -72
  146. package/corpus/templates/slides/app/global.css +105 -0
  147. package/corpus/templates/slides/app/i18n/en-US.ts +8 -0
  148. package/corpus/templates/slides/app/lib/deck-editor-loading.ts +26 -0
  149. package/corpus/templates/slides/app/lib/mermaid-blocks.ts +27 -0
  150. package/corpus/templates/slides/app/pages/DeckEditor.tsx +71 -38
  151. package/corpus/templates/slides/app/pages/Index.tsx +98 -41
  152. package/corpus/templates/slides/changelog/2026-07-30-deck-loading-no-longer-flashes-an-unavailable-message.md +6 -0
  153. package/corpus/templates/slides/changelog/2026-07-30-duplicating-a-deck-from-the-deck-list-now-opens-the-copy-imm.md +6 -0
  154. package/corpus/templates/slides/changelog/2026-07-30-layout-overflow-warning-is-readable-and-dismissible.md +6 -0
  155. package/corpus/templates/slides/changelog/2026-07-30-new-decks-now-start-with-an-empty-slide-list-when-no-slides-.md +6 -0
  156. package/corpus/templates/slides/changelog/2026-07-30-shared-canvas-annotations.md +6 -0
  157. package/corpus/templates/slides/changelog/2026-07-30-slide-objects-can-be-moved-as-a-group-copied-and-pasted-with.md +6 -0
  158. package/corpus/templates/slides/changelog/2026-07-30-slide-text-editing-and-object-controls-now-match-google-slides.md +6 -0
  159. package/corpus/templates/slides/changelog/2026-07-30-style-panel-now-scrolls-with-speaker-notes-open-shows-the-sl.md +6 -0
  160. package/corpus/templates/slides/changelog/2026-07-30-top-toolbar-controls-now-use-consistent-sizing-with-undo-and.md +6 -0
  161. package/corpus/templates/slides/netlify.toml +3 -1
  162. package/corpus/templates/slides/package.json +0 -1
  163. package/corpus/templates/slides/server/lib/chat-attachments.ts +18 -0
  164. package/corpus/templates/slides/server/plugins/agent-chat.ts +5 -0
  165. package/corpus/templates/slides/vite.config.ts +0 -1
  166. package/corpus/toolkit/CHANGELOG.md +13 -0
  167. package/corpus/toolkit/README.md +19 -0
  168. package/corpus/toolkit/agent-native.eject.json +38 -0
  169. package/corpus/toolkit/package.json +29 -1
  170. package/corpus/toolkit/src/canvas-annotations/CanvasCommentPins.tsx +861 -0
  171. package/corpus/toolkit/src/canvas-annotations/DrawOverlay.tsx +1233 -0
  172. package/corpus/toolkit/src/canvas-annotations/index.ts +15 -0
  173. package/corpus/toolkit/src/canvas-annotations/types.ts +14 -0
  174. package/corpus/toolkit/src/canvas-interactions/canvas-interactions.ts +933 -0
  175. package/corpus/toolkit/src/canvas-interactions/index.ts +67 -0
  176. package/corpus/toolkit/src/design-tweaks/scrub-input-utils.ts +311 -0
  177. package/corpus/toolkit/src/design-tweaks/scrub-input.tsx +491 -0
  178. package/corpus/toolkit/src/design-tweaks/visual-style-controls.tsx +26 -171
  179. package/corpus/toolkit/src/index.ts +1 -0
  180. package/dist/a2a/client.d.ts +23 -0
  181. package/dist/a2a/client.d.ts.map +1 -1
  182. package/dist/a2a/client.js +309 -44
  183. package/dist/a2a/client.js.map +1 -1
  184. package/dist/a2a/correlation.d.ts +1 -0
  185. package/dist/a2a/correlation.d.ts.map +1 -1
  186. package/dist/a2a/correlation.js +27 -0
  187. package/dist/a2a/correlation.js.map +1 -1
  188. package/dist/a2a/invoke.d.ts.map +1 -1
  189. package/dist/a2a/invoke.js +4 -2
  190. package/dist/a2a/invoke.js.map +1 -1
  191. package/dist/a2a/types.d.ts +4 -0
  192. package/dist/a2a/types.d.ts.map +1 -1
  193. package/dist/a2a/types.js.map +1 -1
  194. package/dist/action.d.ts +3 -0
  195. package/dist/action.d.ts.map +1 -1
  196. package/dist/action.js.map +1 -1
  197. package/dist/agent/action-continuation-guidance.d.ts.map +1 -1
  198. package/dist/agent/action-continuation-guidance.js +2 -1
  199. package/dist/agent/action-continuation-guidance.js.map +1 -1
  200. package/dist/agent/engine/registry.d.ts.map +1 -1
  201. package/dist/agent/engine/registry.js +82 -8
  202. package/dist/agent/engine/registry.js.map +1 -1
  203. package/dist/agent/production-agent.d.ts +40 -1
  204. package/dist/agent/production-agent.d.ts.map +1 -1
  205. package/dist/agent/production-agent.js +113 -15
  206. package/dist/agent/production-agent.js.map +1 -1
  207. package/dist/agent/run-loop-with-resume.d.ts +21 -0
  208. package/dist/agent/run-loop-with-resume.d.ts.map +1 -1
  209. package/dist/agent/run-loop-with-resume.js +163 -4
  210. package/dist/agent/run-loop-with-resume.js.map +1 -1
  211. package/dist/agent/run-manager.d.ts +16 -9
  212. package/dist/agent/run-manager.d.ts.map +1 -1
  213. package/dist/agent/run-manager.js +93 -42
  214. package/dist/agent/run-manager.js.map +1 -1
  215. package/dist/agent/thread-debug-history.d.ts +10 -0
  216. package/dist/agent/thread-debug-history.d.ts.map +1 -0
  217. package/dist/agent/thread-debug-history.js +68 -0
  218. package/dist/agent/thread-debug-history.js.map +1 -0
  219. package/dist/agent/types.d.ts +3 -1
  220. package/dist/agent/types.d.ts.map +1 -1
  221. package/dist/agent/types.js.map +1 -1
  222. package/dist/cli/create.d.ts.map +1 -1
  223. package/dist/cli/create.js +6 -1
  224. package/dist/cli/create.js.map +1 -1
  225. package/dist/cli/templates-meta.js +2 -2
  226. package/dist/cli/templates-meta.js.map +1 -1
  227. package/dist/client/agent-chat-adapter.d.ts +1 -1
  228. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  229. package/dist/client/agent-chat-adapter.js +76 -13
  230. package/dist/client/agent-chat-adapter.js.map +1 -1
  231. package/dist/client/resources/McpIntegrationDialog.d.ts.map +1 -1
  232. package/dist/client/resources/McpIntegrationDialog.js +1 -1
  233. package/dist/client/resources/McpIntegrationDialog.js.map +1 -1
  234. package/dist/client/sse-event-processor.d.ts.map +1 -1
  235. package/dist/client/sse-event-processor.js +12 -3
  236. package/dist/client/sse-event-processor.js.map +1 -1
  237. package/dist/collab/routes.d.ts +1 -1
  238. package/dist/db/client.d.ts.map +1 -1
  239. package/dist/db/client.js +48 -17
  240. package/dist/db/client.js.map +1 -1
  241. package/dist/guards/no-env-credentials.d.ts.map +1 -1
  242. package/dist/guards/no-env-credentials.js +2 -0
  243. package/dist/guards/no-env-credentials.js.map +1 -1
  244. package/dist/integrations/adapters/slack.js +7 -3
  245. package/dist/integrations/adapters/slack.js.map +1 -1
  246. package/dist/localization/default-messages.d.ts +43 -0
  247. package/dist/localization/default-messages.d.ts.map +1 -1
  248. package/dist/localization/default-messages.js +43 -0
  249. package/dist/localization/default-messages.js.map +1 -1
  250. package/dist/observability/traces.d.ts +3 -1
  251. package/dist/observability/traces.d.ts.map +1 -1
  252. package/dist/observability/traces.js +73 -4
  253. package/dist/observability/traces.js.map +1 -1
  254. package/dist/progress/routes.d.ts +1 -1
  255. package/dist/provider-api/actions/custom-provider-registration.d.ts +12 -12
  256. package/dist/provider-api/actions/provider-api.d.ts +13 -13
  257. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  258. package/dist/scripts/call-agent.d.ts.map +1 -1
  259. package/dist/scripts/call-agent.js +237 -12
  260. package/dist/scripts/call-agent.js.map +1 -1
  261. package/dist/scripts/describe-workspace-apps.js +2 -2
  262. package/dist/scripts/describe-workspace-apps.js.map +1 -1
  263. package/dist/secrets/crypto.d.ts +36 -21
  264. package/dist/secrets/crypto.d.ts.map +1 -1
  265. package/dist/secrets/crypto.js +134 -33
  266. package/dist/secrets/crypto.js.map +1 -1
  267. package/dist/secrets/storage.d.ts +6 -6
  268. package/dist/secrets/storage.d.ts.map +1 -1
  269. package/dist/secrets/storage.js +43 -26
  270. package/dist/secrets/storage.js.map +1 -1
  271. package/dist/server/agent-capabilities.js +1 -1
  272. package/dist/server/agent-capabilities.js.map +1 -1
  273. package/dist/server/agent-chat/action-filters-a2a.d.ts +17 -3
  274. package/dist/server/agent-chat/action-filters-a2a.d.ts.map +1 -1
  275. package/dist/server/agent-chat/action-filters-a2a.js +94 -3
  276. package/dist/server/agent-chat/action-filters-a2a.js.map +1 -1
  277. package/dist/server/agent-chat/context-tools.d.ts.map +1 -1
  278. package/dist/server/agent-chat/context-tools.js +2 -0
  279. package/dist/server/agent-chat/context-tools.js.map +1 -1
  280. package/dist/server/agent-chat/plugin-options.d.ts +23 -0
  281. package/dist/server/agent-chat/plugin-options.d.ts.map +1 -1
  282. package/dist/server/agent-chat/plugin-options.js +4 -1
  283. package/dist/server/agent-chat/plugin-options.js.map +1 -1
  284. package/dist/server/agent-chat/prompt-resources.js +1 -1
  285. package/dist/server/agent-chat/prompt-resources.js.map +1 -1
  286. package/dist/server/agent-chat-plugin.d.ts +2 -1
  287. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  288. package/dist/server/agent-chat-plugin.js +79 -56
  289. package/dist/server/agent-chat-plugin.js.map +1 -1
  290. package/dist/server/realtime-token.d.ts +1 -1
  291. package/dist/templates/default/.agents/skills/secrets/SKILL.md +19 -7
  292. package/dist/templates/headless/.agents/skills/secrets/SKILL.md +19 -7
  293. package/dist/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +18 -7
  294. package/dist/templates/workspace-core/.agents/skills/composable-mini-apps/SKILL.md +15 -14
  295. package/dist/templates/workspace-core/.agents/skills/secrets/SKILL.md +19 -7
  296. package/dist/vite/client.d.ts.map +1 -1
  297. package/dist/vite/client.js +1 -0
  298. package/dist/vite/client.js.map +1 -1
  299. package/docs/content/agent-native-toolkit.mdx +13 -6
  300. package/docs/content/cloneable-saas.mdx +5 -5
  301. package/docs/content/creating-templates.mdx +9 -0
  302. package/docs/content/deployment.mdx +18 -12
  303. package/docs/content/doctor.mdx +8 -1
  304. package/docs/content/drop-in-agent.mdx +1 -1
  305. package/docs/content/extensions.mdx +18 -18
  306. package/docs/content/faq.mdx +4 -4
  307. package/docs/content/key-concepts.mdx +30 -21
  308. package/docs/content/security.mdx +2 -2
  309. package/docs/content/template-brain-developers.mdx +4 -1
  310. package/docs/content/template-mail-developers.mdx +4 -1
  311. package/docs/content/what-is-agent-native.mdx +6 -6
  312. package/package.json +3 -3
  313. package/src/a2a/client.ts +449 -66
  314. package/src/a2a/correlation.ts +31 -0
  315. package/src/a2a/invoke.ts +4 -2
  316. package/src/a2a/types.ts +4 -0
  317. package/src/action.ts +3 -0
  318. package/src/agent/action-continuation-guidance.ts +2 -1
  319. package/src/agent/engine/registry.ts +98 -6
  320. package/src/agent/production-agent.ts +151 -18
  321. package/src/agent/run-loop-with-resume.ts +190 -3
  322. package/src/agent/run-manager.ts +117 -41
  323. package/src/agent/thread-debug-history.ts +86 -0
  324. package/src/agent/types.ts +3 -1
  325. package/src/cli/create.ts +11 -1
  326. package/src/cli/templates-meta.ts +2 -2
  327. package/src/client/agent-chat-adapter.ts +91 -13
  328. package/src/client/resources/McpIntegrationDialog.tsx +11 -9
  329. package/src/client/sse-event-processor.ts +17 -3
  330. package/src/db/client.ts +57 -17
  331. package/src/guards/no-env-credentials.ts +2 -0
  332. package/src/integrations/adapters/slack.ts +8 -3
  333. package/src/localization/default-messages.ts +44 -0
  334. package/src/observability/traces.ts +78 -5
  335. package/src/scripts/call-agent.ts +294 -11
  336. package/src/scripts/describe-workspace-apps.ts +2 -2
  337. package/src/secrets/crypto.ts +156 -35
  338. package/src/secrets/storage.ts +64 -26
  339. package/src/server/agent-capabilities.ts +1 -1
  340. package/src/server/agent-chat/action-filters-a2a.ts +113 -7
  341. package/src/server/agent-chat/context-tools.ts +2 -0
  342. package/src/server/agent-chat/plugin-options.ts +34 -1
  343. package/src/server/agent-chat/prompt-resources.ts +1 -1
  344. package/src/server/agent-chat-plugin.ts +91 -59
  345. package/src/templates/default/.agents/skills/secrets/SKILL.md +19 -7
  346. package/src/templates/headless/.agents/skills/secrets/SKILL.md +19 -7
  347. package/src/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +18 -7
  348. package/src/templates/workspace-core/.agents/skills/composable-mini-apps/SKILL.md +15 -14
  349. package/src/templates/workspace-core/.agents/skills/secrets/SKILL.md +19 -7
  350. package/src/vite/client.ts +1 -0
@@ -28,6 +28,73 @@ import {
28
28
  } from "../server/plans.js";
29
29
  import type { PlanContent } from "../shared/plan-content.js";
30
30
 
31
+ const agentPlanMdxSourcePatchesSchema = z
32
+ .array(
33
+ z.discriminatedUnion("op", [
34
+ z.object({
35
+ op: z.literal("replace-markdown-block"),
36
+ blockId: z.string().min(1),
37
+ markdown: z.string().max(16_000),
38
+ title: z.string().optional(),
39
+ }),
40
+ z.object({
41
+ op: z.literal("update-component-prop"),
42
+ file: z.enum(["plan.mdx", "canvas.mdx", "prototype.mdx"]),
43
+ componentId: z.string().min(1),
44
+ prop: z.string().min(1),
45
+ value: z.unknown(),
46
+ }),
47
+ z.object({
48
+ op: z.literal("update-wireframe-node"),
49
+ nodeId: z.string().min(1),
50
+ patch: z.record(z.string(), z.unknown()),
51
+ }),
52
+ z.object({
53
+ op: z.literal("update-annotation"),
54
+ annotationId: z.string().min(1),
55
+ patch: z.object({
56
+ type: z.enum(["note", "text", "callout", "arrow"]).optional(),
57
+ title: z.string().optional(),
58
+ text: z.string().max(2_000).optional(),
59
+ points: z
60
+ .array(z.object({ x: z.number(), y: z.number() }))
61
+ .min(1)
62
+ .max(12)
63
+ .optional(),
64
+ style: z
65
+ .object({
66
+ tone: z
67
+ .enum(["default", "accent", "warn", "ok", "muted"])
68
+ .optional(),
69
+ stroke: z.enum(["solid", "dashed"]).optional(),
70
+ width: z.number().min(1).max(12).optional(),
71
+ })
72
+ .optional(),
73
+ targetId: z.string().optional(),
74
+ placement: z
75
+ .enum([
76
+ "top",
77
+ "right",
78
+ "bottom",
79
+ "left",
80
+ "top-left",
81
+ "top-right",
82
+ "bottom-left",
83
+ "bottom-right",
84
+ ])
85
+ .optional(),
86
+ x: z.number().optional(),
87
+ y: z.number().optional(),
88
+ }),
89
+ }),
90
+ ]),
91
+ )
92
+ .max(80)
93
+ .describe(
94
+ "Small exported-MDX patches only. Never send replace-file or replace-artboard " +
95
+ "from an agent; for a live plan use update-visual-plan contentPatches instead.",
96
+ );
97
+
31
98
  type PlanSurfaceCounts = {
32
99
  blocks: number;
33
100
  canvasFrames: number;
@@ -42,6 +109,28 @@ function planSurfaceCounts(content: PlanContent | null | undefined) {
42
109
  } satisfies PlanSurfaceCounts;
43
110
  }
44
111
 
112
+ function compactAgentWriteResult(
113
+ bundle: Awaited<ReturnType<typeof loadPlanBundle>>,
114
+ changed: Record<string, unknown>,
115
+ ) {
116
+ return {
117
+ planId: bundle.plan.id,
118
+ plan: {
119
+ id: bundle.plan.id,
120
+ title: bundle.plan.title,
121
+ brief: bundle.plan.brief,
122
+ kind: bundle.plan.kind,
123
+ status: bundle.plan.status,
124
+ currentFocus: bundle.plan.currentFocus,
125
+ updatedAt: bundle.plan.updatedAt,
126
+ surfaces: planSurfaceCounts(bundle.plan.content),
127
+ },
128
+ changed,
129
+ path: planPath(bundle.plan.id, bundle.plan.kind),
130
+ url: planPath(bundle.plan.id, bundle.plan.kind),
131
+ };
132
+ }
133
+
45
134
  function stableJson(value: unknown): string {
46
135
  if (value === null) return "null";
47
136
  if (value === undefined) return "undefined";
@@ -140,7 +229,7 @@ function assertNoUnexpectedSurfaceCollapse(
140
229
 
141
230
  export default defineAction({
142
231
  description:
143
- "Patch the MDX source for an Agent-Native Plan by stable semantic IDs, then normalize it back into runtime JSON. Use ONLY when working with exported MDX source files (repo check-in workflows); for live plans prefer update-visual-plan with contentPatches. Suitable for tiny source-control friendly diffs: one markdown block, one artboard, one annotation, or one wireframe node.",
232
+ "Patch the MDX source for an Agent-Native Plan by stable semantic IDs, then normalize it back into runtime JSON. Use ONLY when working with exported MDX source files (repo check-in workflows); for live plans prefer update-visual-plan with contentPatches. For agent calls, use only small semantic patches and never resend replace-file or replace-artboard payloads.",
144
233
  schema: z.object({
145
234
  planId: z.string().describe("Plan ID"),
146
235
  expectedUpdatedAt: z
@@ -162,6 +251,21 @@ export default defineAction({
162
251
  ),
163
252
  note: z.string().optional().describe("Short audit note for plan history."),
164
253
  }),
254
+ agentInputSchema: z.object({
255
+ planId: z.string().describe("Plan ID"),
256
+ expectedUpdatedAt: z
257
+ .string()
258
+ .min(1)
259
+ .optional()
260
+ .describe("Revision from the latest plan read when required."),
261
+ allowDestructive: z
262
+ .boolean()
263
+ .optional()
264
+ .default(false)
265
+ .describe("Allow an intentional nonempty-to-empty collapse."),
266
+ patches: agentPlanMdxSourcePatchesSchema,
267
+ note: z.string().optional().describe("Short audit note for plan history."),
268
+ }),
165
269
  publicAgent: {
166
270
  expose: true,
167
271
  readOnly: false,
@@ -321,6 +425,18 @@ export default defineAction({
321
425
  ),
322
426
  })
323
427
  : null;
428
+ if (isAgentCaller) {
429
+ return {
430
+ ...compactAgentWriteResult(updated, {
431
+ patchOps: args.patches.map((patch) => patch.op),
432
+ counts: {
433
+ before: beforeCounts,
434
+ after: afterCounts,
435
+ },
436
+ }),
437
+ ...(local?.written ? { localFiles: local } : {}),
438
+ };
439
+ }
324
440
  return {
325
441
  ...updated,
326
442
  planId: updated.plan.id,
@@ -49,8 +49,7 @@ import {
49
49
  sectionInputSchema,
50
50
  } from "../server/plans.js";
51
51
  import {
52
- agentPlanContentPatchesSchema,
53
- agentPlanContentSchema,
52
+ agentPlanIncrementalContentPatchesSchema,
54
53
  applyPlanContentPatches,
55
54
  planContentPatchesSchema,
56
55
  planContentSchema,
@@ -454,6 +453,32 @@ const CONTENT_DESCRIPTION =
454
453
  const CONTENT_PATCHES_DESCRIPTION =
455
454
  "Targeted structured content edits addressed by stable id. Prefer granular operations for live plans. The destructive replace-blocks operation requires expectedUpdatedAt from a fresh read. patch-visual-plan-source is only for exported MDX folders. Supported ops: set-metadata for title/brief; set-visual-render-mode to persist `design` (high fidelity, authored CSS, no rough.js for any viewer) or `wireframe` across canvas, linked blocks, and prototype screens; set-prototype / remove-prototype / update-prototype-screen / patch-prototype-html for live prototype surfaces; update-block / replace-block, update-rich-text, patch-wireframe-html, patch-diagram-html, update-wireframe-node, replace-wireframe-screen, update-canvas-frame, update-canvas-annotation / append-canvas-annotation, append-block / remove-block, update-custom-html. A fidelity upgrade must also replace or patch the existing screen HTML/CSS with deliberate branded design; changing render mode alone only removes the sketch treatment.";
456
455
 
456
+ function compactAgentWriteResult(
457
+ bundle: Awaited<ReturnType<typeof loadPlanBundle>>,
458
+ changed: Record<string, unknown>,
459
+ ) {
460
+ return {
461
+ planId: bundle.plan.id,
462
+ plan: {
463
+ id: bundle.plan.id,
464
+ title: bundle.plan.title,
465
+ brief: bundle.plan.brief,
466
+ kind: bundle.plan.kind,
467
+ status: bundle.plan.status,
468
+ currentFocus: bundle.plan.currentFocus,
469
+ updatedAt: bundle.plan.updatedAt,
470
+ surfaces: {
471
+ blocks: bundle.plan.content?.blocks.length ?? 0,
472
+ canvasFrames: bundle.plan.content?.canvas?.frames.length ?? 0,
473
+ prototypeScreens: bundle.plan.content?.prototype?.screens.length ?? 0,
474
+ },
475
+ },
476
+ changed,
477
+ path: planPath(bundle.plan.id, bundle.plan.kind),
478
+ url: planPath(bundle.plan.id, bundle.plan.kind),
479
+ };
480
+ }
481
+
457
482
  // Named so `agentInputSchema` below can `.extend()` it with compact
458
483
  // `content`/`contentPatches` fields instead of duplicating every other key.
459
484
  const updateVisualPlanSchema = z.object({
@@ -527,21 +552,26 @@ const updateVisualPlanSchema = z.object({
527
552
  .describe("Short label saved as the version-history snapshot label."),
528
553
  });
529
554
 
555
+ // ADVERTISED-ONLY: agent edits to an existing plan must stay incremental. The
556
+ // runtime schema above still accepts full replacements for the browser editor
557
+ // and explicit callers that have intentionally chosen that workflow.
558
+ const agentUpdateVisualPlanSchema = updateVisualPlanSchema
559
+ .omit({ content: true, html: true, markdown: true, sections: true })
560
+ .extend({
561
+ contentPatches: agentPlanIncrementalContentPatchesSchema
562
+ .optional()
563
+ .default([])
564
+ .describe(
565
+ "Small targeted edits only. Do not send full content or replace-blocks; " +
566
+ "append or patch one existing block at a time.",
567
+ ),
568
+ });
569
+
530
570
  export default defineAction({
531
571
  description:
532
- "Update an Agent-Native Plan's structured content blocks, prototype screens, visual fidelity, sections, comments, or status. Prefer contentPatches for targeted edits. When a user asks for higher fidelity on an existing plan, update that same plan in place: use set-visual-render-mode with design and provide polished screen HTML/CSS in the same call instead of creating a duplicate plan or only toggling the viewer-local clean style. Use full content only for broad restructuring. Works on plans and recaps alike when you have editor access; with viewer access (common on PR recaps published by CI) only comment-only calls succeed to change a recap you cannot edit, publish a replacement with create-visual-recap instead of retrying this call.",
572
+ "Update an Agent-Native Plan's structured content blocks, prototype screens, visual fidelity, sections, comments, or status. For an existing plan, use small contentPatches such as append-block, update-rich-text, or a text patch; do not resend the full document for an incremental edit. When a user asks for higher fidelity on an existing plan, update that same plan in place: use set-visual-render-mode with design and provide polished screen HTML/CSS in the same call instead of creating a duplicate plan or only toggling the viewer-local clean style. Use a full replacement only for an explicit broad rebuild. Works on plans and recaps alike when you have editor access; with viewer access (common on PR recaps published by CI) only comment-only calls succeed - to change a recap you cannot edit, publish a replacement with create-visual-recap instead of retrying this call.",
533
573
  schema: updateVisualPlanSchema,
534
- // ADVERTISED-ONLY: same top-level shape, but `content`/`contentPatches`
535
- // swap the deep per-block-type union for a compact `type`-enum stand-in.
536
- // Runtime validation always runs the full schema above — see the `actions`
537
- // skill.
538
- agentInputSchema: updateVisualPlanSchema.extend({
539
- content: agentPlanContentSchema.optional().describe(CONTENT_DESCRIPTION),
540
- contentPatches: agentPlanContentPatchesSchema
541
- .optional()
542
- .default([])
543
- .describe(CONTENT_PATCHES_DESCRIPTION),
544
- }),
574
+ agentInputSchema: agentUpdateVisualPlanSchema,
545
575
  publicAgent: {
546
576
  expose: true,
547
577
  readOnly: false,
@@ -1173,6 +1203,18 @@ export default defineAction({
1173
1203
  ),
1174
1204
  })
1175
1205
  : null;
1206
+ if (isAgentCaller) {
1207
+ return {
1208
+ ...compactAgentWriteResult(bundle, {
1209
+ titleChanged: args.title !== undefined,
1210
+ briefChanged: args.brief !== undefined,
1211
+ statusChanged: args.status !== undefined,
1212
+ contentPatchOps: args.contentPatches.map((patch) => patch.op),
1213
+ commentCount: insertedCommentIds.length,
1214
+ }),
1215
+ ...(local?.written ? { localFiles: local } : {}),
1216
+ };
1217
+ }
1176
1218
  return {
1177
1219
  ...bundle,
1178
1220
  planId: bundle.plan.id,
@@ -1602,7 +1602,7 @@ export function buildPlanAgentContext(input: {
1602
1602
  : [
1603
1603
  "Fast iteration workflow:",
1604
1604
  "1. Call get-visual-plan with this plan ID to read structured content, exported HTML, sections, comments, and activity.",
1605
- '2. Prefer update-visual-plan contentPatches for targeted edits. Examples: update-rich-text for copy, patch-prototype-html / update-prototype-screen for live prototype states, update-wireframe-node for one kit-tree node, update-canvas-frame for frame layout, append-canvas-annotation / update-canvas-annotation for canvas markup, append-block/remove-block for document changes, or replace-block for a single block. When the user asks for higher fidelity, polished mockups, production-like UI, real design, or anything "not sketchy," update this same plan in place: rewrite the relevant screen HTML/CSS and include set-visual-render-mode with renderMode design. Do not create a second plan, put CSS in style tags, or treat the viewer-local Clean toggle as a fidelity upgrade. Use full content only for broad restructuring. Use html only when preserving or importing a legacy standalone HTML artifact.',
1605
+ '2. Prefer small update-visual-plan contentPatches for every edit to an existing plan. Examples: append-block for new tasks, update-rich-text for a bounded copy change, patch-prototype-html / update-prototype-screen for live prototype states, update-wireframe-node for one kit-tree node, update-canvas-frame for frame layout, append-canvas-annotation / update-canvas-annotation for canvas markup, or remove-block for one block. Never resend the full plan, use replace-blocks, or use patch-visual-plan-source replace-file for an incremental change - those payloads can be truncated before the tool receives them. When the user asks for higher fidelity, polished mockups, production-like UI, real design, or anything "not sketchy," update this same plan in place: rewrite the relevant screen HTML/CSS and include set-visual-render-mode with renderMode design. Do not create a second plan, put CSS in style tags, or treat the viewer-local Clean toggle as a fidelity upgrade. Use a full replacement only for an explicit broad rebuild. Use html only when preserving or importing a legacy standalone HTML artifact.',
1606
1606
  "3. Preserve the user's existing annotation comments and intent unless the user asks to remove or resolve them.",
1607
1607
  "4. Match the requested fidelity: ordinary UI planning can use sketch diagrams and wireframes; high-fidelity requests need the persisted Design surface with deliberate branded HTML/CSS and stable data-design-id targets.",
1608
1608
  "5. After applying feedback, keep the plan scannable, editable, and serious instead of turning it into a marketing page.",
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-30
4
+ ---
5
+
6
+ Plan agents now recover interrupted edits with small targeted writes.
@@ -2944,6 +2944,41 @@ export const agentPlanContentPatchesSchema = z
2944
2944
  "replace-block / replace-blocks / append-block.",
2945
2945
  );
2946
2946
 
2947
+ /**
2948
+ * Model-facing patch vocabulary for edits to an existing hosted plan. Full
2949
+ * block/content replacement remains valid at runtime for the browser editor
2950
+ * and explicit callers, but advertising it to an agent makes it too easy to
2951
+ * resend the whole document and hit the provider's streamed-JSON limit.
2952
+ */
2953
+ const AGENT_INCREMENTAL_PATCH_EXCLUDED_OPS = new Set([
2954
+ "set-prototype",
2955
+ "replace-block",
2956
+ "replace-blocks",
2957
+ "replace-wireframe-screen",
2958
+ ]);
2959
+
2960
+ const agentIncrementalPlanContentPatchOptions =
2961
+ agentPlanContentPatchOptions.filter((option) => {
2962
+ const op = (option.shape as { op: { value: string } }).op.value;
2963
+ return !AGENT_INCREMENTAL_PATCH_EXCLUDED_OPS.has(op);
2964
+ });
2965
+
2966
+ export const agentPlanIncrementalContentPatchesSchema = z
2967
+ .array(
2968
+ z.discriminatedUnion(
2969
+ "op",
2970
+ agentIncrementalPlanContentPatchOptions as unknown as Parameters<
2971
+ typeof z.discriminatedUnion
2972
+ >[1],
2973
+ ),
2974
+ )
2975
+ .max(80)
2976
+ .describe(
2977
+ "Small targeted edits to an existing plan. Prefer append-block, update-rich-text, " +
2978
+ "patch-wireframe-html, patch-prototype-html, or update-block. Do not " +
2979
+ "resend the full plan; use get-visual-plan for context and keep each call incremental.",
2980
+ );
2981
+
2947
2982
  export function applyPlanContentPatches(
2948
2983
  content: PlanContent,
2949
2984
  patches: PlanContentPatch[],
@@ -17,18 +17,14 @@ other analytics data.
17
17
 
18
18
  ## Delegation
19
19
 
20
- - For a known, read-only operation, use `call-agent` with `agent: "analytics"`,
21
- the exact `action`, and complete `input`. This invokes Analytics directly
22
- without starting a second model loop.
23
- - For ambiguous, multi-source, or narrative questions, use `call-agent` with a
24
- natural-language `message` and let Analytics decide which source or sources
25
- to use.
26
- - Useful Analytics operations include `hubspot-records` for bounded CRM
27
- records and `hubspot-deals` for pipeline and deal metrics,
28
- `account-deep-dive` for a named account or renewal/risk review, `gong-calls`
29
- for quotes/counts/transcripts and coverage, and `gong-native-insights` for
30
- bounded narrative synthesis. For first-party product activity, send a
31
- natural-language message and let Analytics choose its query.
20
+ - Use `call-agent` with `agent: "analytics"` and a natural-language `message`
21
+ by default. Describe the business question, requested window or cohort, and
22
+ the concise result shape the deck needs. Analytics decides which sources,
23
+ tools, queries, and joins answer it.
24
+ - A direct `action` + `input` call is optional only when an exact bounded
25
+ semantic read and its complete schema are already known. It is not a fallback
26
+ for slow, failed, or unavailable agent delegation. Never invent an Analytics
27
+ action contract to make a demo pass.
32
28
  - Slides must not write SQL, choose a warehouse or provider, call HubSpot/Gong
33
29
  directly, or interpret a provider schema itself. Analytics owns source
34
30
  selection, data dictionary interpretation, filters, joins, and query
@@ -41,10 +41,10 @@ ladder.
41
41
  - Use image-generation and image-selection actions only when the deck genuinely
42
42
  needs imagery; keep citations/asset provenance when available.
43
43
  - Use framework sharing actions for deck visibility and grants.
44
- - For a known, read-only sibling-app operation, use `call-agent` with `action`
45
- and `input` (or `invokeAgentAction`) instead of starting the sibling agent's
46
- model loop. In Analytics, use `gong-native-insights` for provider-synthesized
47
- briefs and `gong-calls` for quotes, counts, transcripts, and coverage claims.
44
+ - Ask a sibling app's agent with a natural-language `call-agent` message by
45
+ default. Let that specialist use its own instructions, skills, sources, and
46
+ tools. Direct action invocation is only for an exact bounded read with a
47
+ fully known schema; never use it as a workaround for slow or failed A2A.
48
48
  - For data requests, read `.agents/skills/analytics-data-for-decks/SKILL.md` and
49
49
  delegate via Analytics over A2A; do not write SQL or call providers directly.
50
50
  - When the user names no reference deck or design system, call
@@ -75,7 +75,7 @@ const SlideSchema = z.object({
75
75
 
76
76
  // Accept either a parsed array (HTTP/agent) or a JSON string (CLI)
77
77
  const SlidesSchema = z.preprocess(
78
- (v) => (typeof v === "string" ? JSON.parse(v) : v),
78
+ (v) => (v === undefined ? [] : typeof v === "string" ? JSON.parse(v) : v),
79
79
  z.array(SlideSchema),
80
80
  );
81
81
 
@@ -26,8 +26,15 @@ export default defineAction({
26
26
  .describe(
27
27
  "Optional client-supplied id for the new deck. Lets the UI pick the id ahead of time so it can navigate optimistically before the server responds.",
28
28
  ),
29
+ slideIds: z
30
+ .array(z.string().min(1).max(64))
31
+ .max(1000)
32
+ .optional()
33
+ .describe(
34
+ "Optional client-supplied ids for the copied slides, in slide order. Same purpose as `newId`: when the UI opens an optimistic copy the user can edit immediately, its slide ids must match the persisted copy or those edits address slides the server never had.",
35
+ ),
29
36
  }),
30
- run: async ({ deckId, title, newId: clientNewId }) => {
37
+ run: async ({ deckId, title, newId: clientNewId, slideIds }) => {
31
38
  const access = await resolveAccess("deck", deckId);
32
39
  if (!access) throw new Error(`Deck not found: ${deckId}`);
33
40
 
@@ -37,9 +44,17 @@ export default defineAction({
37
44
  const now = new Date().toISOString();
38
45
  const deckData = JSON.parse(source.data);
39
46
 
40
- // Generate new IDs for all slides so edits to the copy don't collide
41
- for (const slide of deckData.slides || []) {
42
- slide.id = `slide-${nanoid(8)}`;
47
+ // New IDs for all slides so edits to the copy don't collide with the
48
+ // original. A caller that already rendered an optimistic copy supplies the
49
+ // ids it used; anything it did not cover still gets a fresh one.
50
+ const slides = deckData.slides || [];
51
+ for (const [index, slide] of slides.entries()) {
52
+ slide.id = slideIds?.[index] ?? `slide-${nanoid(8)}`;
53
+ }
54
+ if (
55
+ new Set(slides.map((s: { id: string }) => s.id)).size !== slides.length
56
+ ) {
57
+ throw new Error("slideIds must be unique");
43
58
  }
44
59
 
45
60
  const newTitle = title || `Copy of ${source.title}`;
@@ -32,7 +32,6 @@ interface DeckCardProps {
32
32
  onRename: (id: string, newTitle: string) => void;
33
33
  onDuplicate: (id: string) => void;
34
34
  onToggleStar: (id: string, starred: boolean) => void;
35
- isDuplicating?: boolean;
36
35
  designSystemTitle?: string | null;
37
36
  isWorkspaceDefault?: boolean;
38
37
  canSetWorkspaceDefault?: boolean;
@@ -45,7 +44,6 @@ export default function DeckCard({
45
44
  onRename,
46
45
  onDuplicate,
47
46
  onToggleStar,
48
- isDuplicating = false,
49
47
  designSystemTitle,
50
48
  isWorkspaceDefault = false,
51
49
  canSetWorkspaceDefault = false,
@@ -230,15 +228,9 @@ export default function DeckCard({
230
228
  <IconPencil className="w-3.5 h-3.5 me-2" />
231
229
  Rename
232
230
  </DropdownMenuItem>
233
- <DropdownMenuItem
234
- onSelect={() => {
235
- if (isDuplicating) return;
236
- onDuplicate(deck.id);
237
- }}
238
- disabled={isDuplicating}
239
- >
231
+ <DropdownMenuItem onSelect={() => onDuplicate(deck.id)}>
240
232
  <IconCopy className="w-3.5 h-3.5 me-2" />
241
- {isDuplicating ? "Duplicating..." : "Duplicate"}
233
+ Duplicate
242
234
  </DropdownMenuItem>
243
235
  <DropdownMenuItem
244
236
  onSelect={(event) => {
@@ -46,11 +46,13 @@ let idCounter = 0;
46
46
  interface MermaidRendererProps {
47
47
  definition: string;
48
48
  className?: string;
49
+ index?: number;
49
50
  }
50
51
 
51
52
  export function MermaidRenderer({
52
53
  definition,
53
54
  className,
55
+ index,
54
56
  }: MermaidRendererProps) {
55
57
  const containerRef = useRef<HTMLDivElement>(null);
56
58
  const [svg, setSvg] = useState<string>("");
@@ -98,6 +100,7 @@ export function MermaidRenderer({
98
100
  if (error) {
99
101
  return (
100
102
  <div
103
+ data-mermaid-index={index}
101
104
  className={`flex items-center justify-center p-4 text-xs text-red-400/70 ${className || ""}`}
102
105
  >
103
106
  <pre className="whitespace-pre-wrap">{error}</pre>
@@ -105,11 +108,19 @@ export function MermaidRenderer({
105
108
  );
106
109
  }
107
110
 
108
- if (!svg) return null;
111
+ if (!svg) {
112
+ // Mermaid's import + render is async, so this placeholder is the only
113
+ // thing in the DOM until it resolves. Without it, an edit to another
114
+ // element on the same slide made while the diagram is still loading
115
+ // would see zero `[data-mermaid-index]` nodes when serializing the
116
+ // slide, and silently drop the diagram from the saved content.
117
+ return <div data-mermaid-index={index} className={className} />;
118
+ }
109
119
 
110
120
  return (
111
121
  <div
112
122
  ref={containerRef}
123
+ data-mermaid-index={index}
113
124
  className={`flex items-center justify-center [&_svg]:max-w-full [&_svg]:max-h-full ${className || ""}`}
114
125
  dangerouslySetInnerHTML={{ __html: svg }}
115
126
  />
@@ -14,6 +14,7 @@ import rehypeRaw from "rehype-raw";
14
14
  import { Skeleton } from "@/components/ui/skeleton";
15
15
  import type { Slide } from "@/context/DeckContext";
16
16
  import { type AspectRatio, getAspectRatioDims } from "@/lib/aspect-ratios";
17
+ import { extractMermaidBlocks } from "@/lib/mermaid-blocks";
17
18
  import {
18
19
  sanitizeCssValue,
19
20
  sanitizeSlideHtml,
@@ -37,6 +38,8 @@ interface SlideRendererProps {
37
38
  * The renderer no longer shrinks slides for vertical overflow — instead the
38
39
  * editor surfaces this so the agent can rewrite the slide to fit. */
39
40
  onOverflowChange?: (info: SlideOverflowInfo) => void;
41
+ /** Fires after AutoFit has applied its final transform for this render. */
42
+ onAutofitSettled?: () => void;
40
43
  }
41
44
 
42
45
  export const layoutClasses: Record<string, string> = {
@@ -311,9 +314,12 @@ function useSlideAutofit(
311
314
  canvasHeight: number,
312
315
  fitKey: string,
313
316
  onOverflowChange?: (info: SlideOverflowInfo) => void,
317
+ onAutofitSettled?: () => void,
314
318
  ) {
315
319
  const overflowCallbackRef = useRef(onOverflowChange);
316
320
  overflowCallbackRef.current = onOverflowChange;
321
+ const autofitSettledRef = useRef(onAutofitSettled);
322
+ autofitSettledRef.current = onAutofitSettled;
317
323
 
318
324
  useIsomorphicLayoutEffect(() => {
319
325
  const root = ref.current;
@@ -395,6 +401,7 @@ function useSlideAutofit(
395
401
  viewportHeight: 0,
396
402
  },
397
403
  );
404
+ autofitSettledRef.current?.();
398
405
  }
399
406
  };
400
407
 
@@ -437,6 +444,7 @@ function AutoFitContent({
437
444
  className = "",
438
445
  children,
439
446
  onOverflowChange,
447
+ onAutofitSettled,
440
448
  }: {
441
449
  canvasWidth: number;
442
450
  canvasHeight: number;
@@ -444,9 +452,17 @@ function AutoFitContent({
444
452
  className?: string;
445
453
  children: ReactNode;
446
454
  onOverflowChange?: (info: SlideOverflowInfo) => void;
455
+ onAutofitSettled?: () => void;
447
456
  }) {
448
457
  const ref = useRef<HTMLDivElement>(null);
449
- useSlideAutofit(ref, canvasWidth, canvasHeight, fitKey, onOverflowChange);
458
+ useSlideAutofit(
459
+ ref,
460
+ canvasWidth,
461
+ canvasHeight,
462
+ fitKey,
463
+ onOverflowChange,
464
+ onAutofitSettled,
465
+ );
450
466
 
451
467
  return (
452
468
  <div
@@ -469,18 +485,9 @@ function BlankSlideContent({ content }: { content: string }) {
469
485
  // includes the per-block `contentEditable="true"` set by SlideEditor's
470
486
  // double-click-to-edit flow, which made inline text editing appear to do nothing.
471
487
  const { mermaidBlocks, htmlWithPlaceholders, dangerousHtml } = useMemo(() => {
472
- // Extract mermaid blocks BEFORE sanitization. The sanitizer round-trips
473
- // HTML through DOMParser + innerHTML, which HTML-escapes `>` in text
474
- // nodes to `&gt;` that mangles diagram arrows like `A --> B` into
475
- // `A --&gt; B` and breaks the mermaid parser.
476
- const blocks: string[] = [];
477
- const contentWithPlaceholders = content.replace(
478
- /<div\s+class="mermaid"[^>]*>([\s\S]*?)<\/div>/gi,
479
- (_, definition) => {
480
- blocks.push(String(definition).trim());
481
- return `<div data-mermaid-index="${blocks.length - 1}"></div>`;
482
- },
483
- );
488
+ // Extract mermaid blocks BEFORE sanitization see mermaid-blocks.ts for
489
+ // why (sanitizer HTML-escaping breaks the mermaid parser).
490
+ const { blocks, contentWithPlaceholders } = extractMermaidBlocks(content);
484
491
 
485
492
  // Apply white filter to all logo images (brandfetch, logo.dev, etc.) for dark backgrounds
486
493
  const processed = sanitizeSlideHtml(
@@ -547,6 +554,7 @@ function MermaidHtmlContent({
547
554
  <MermaidRenderer
548
555
  key={`mermaid-${i}`}
549
556
  definition={mermaidBlocks[idx]}
557
+ index={idx}
550
558
  className="my-4 w-full"
551
559
  />
552
560
  );
@@ -564,11 +572,13 @@ export function SlideInner({
564
572
  designSystem,
565
573
  aspectRatio,
566
574
  onOverflowChange,
575
+ onAutofitSettled,
567
576
  }: {
568
577
  slide: Slide;
569
578
  designSystem?: DesignSystemData;
570
579
  aspectRatio?: AspectRatio;
571
580
  onOverflowChange?: (info: SlideOverflowInfo) => void;
581
+ onAutofitSettled?: () => void;
572
582
  }) {
573
583
  const t = useT();
574
584
  const dims = getAspectRatioDims(aspectRatio);
@@ -651,6 +661,7 @@ export function SlideInner({
651
661
  fitKey={left}
652
662
  className="slide-content text-white/90"
653
663
  onOverflowChange={onOverflowChange}
664
+ onAutofitSettled={onAutofitSettled}
654
665
  >
655
666
  <ReactMarkdown
656
667
  components={markdownComponents}
@@ -664,6 +675,7 @@ export function SlideInner({
664
675
  canvasHeight={dims.height}
665
676
  fitKey={right}
666
677
  className="slide-content text-white/90"
678
+ onAutofitSettled={onAutofitSettled}
667
679
  >
668
680
  <ReactMarkdown
669
681
  components={markdownComponents}
@@ -689,6 +701,7 @@ export function SlideInner({
689
701
  fitKey={content}
690
702
  className="h-full w-full"
691
703
  onOverflowChange={onOverflowChange}
704
+ onAutofitSettled={onAutofitSettled}
692
705
  >
693
706
  <BlankSlideContent content={content} />
694
707
  </AutoFitContent>
@@ -714,6 +727,7 @@ export function SlideInner({
714
727
  fitKey={content}
715
728
  className="slide-content text-white/90 w-full"
716
729
  onOverflowChange={onOverflowChange}
730
+ onAutofitSettled={onAutofitSettled}
717
731
  >
718
732
  <ReactMarkdown
719
733
  components={markdownComponents}
@@ -733,6 +747,7 @@ export default function SlideRenderer({
733
747
  designSystem,
734
748
  aspectRatio,
735
749
  onOverflowChange,
750
+ onAutofitSettled,
736
751
  }: SlideRendererProps) {
737
752
  const dims = getAspectRatioDims(aspectRatio);
738
753
 
@@ -753,6 +768,7 @@ export default function SlideRenderer({
753
768
  designSystem={designSystem}
754
769
  aspectRatio={aspectRatio}
755
770
  onOverflowChange={onOverflowChange}
771
+ onAutofitSettled={onAutofitSettled}
756
772
  />
757
773
  </div>
758
774
  <ScaleHelper
@@ -783,6 +799,7 @@ export default function SlideRenderer({
783
799
  designSystem={designSystem}
784
800
  aspectRatio={aspectRatio}
785
801
  onOverflowChange={onOverflowChange}
802
+ onAutofitSettled={onAutofitSettled}
786
803
  />
787
804
  </div>
788
805
  <ScaleHelper targetWidth={dims.width} />