@agent-native/core 0.100.2 → 0.101.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (345) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +65 -0
  3. package/corpus/core/docs/content/locales/ar-SA/pr-visual-recap.mdx +48 -30
  4. package/corpus/core/docs/content/locales/ar-SA/template-plan.mdx +7 -0
  5. package/corpus/core/docs/content/locales/de-DE/pr-visual-recap.mdx +48 -30
  6. package/corpus/core/docs/content/locales/de-DE/template-plan.mdx +7 -0
  7. package/corpus/core/docs/content/locales/es-ES/pr-visual-recap.mdx +48 -30
  8. package/corpus/core/docs/content/locales/es-ES/template-plan.mdx +14 -0
  9. package/corpus/core/docs/content/locales/fr-FR/pr-visual-recap.mdx +48 -30
  10. package/corpus/core/docs/content/locales/fr-FR/template-plan.mdx +7 -0
  11. package/corpus/core/docs/content/locales/hi-IN/pr-visual-recap.mdx +48 -30
  12. package/corpus/core/docs/content/locales/hi-IN/template-plan.mdx +7 -0
  13. package/corpus/core/docs/content/locales/ja-JP/pr-visual-recap.mdx +48 -30
  14. package/corpus/core/docs/content/locales/ja-JP/template-plan.mdx +7 -0
  15. package/corpus/core/docs/content/locales/ko-KR/pr-visual-recap.mdx +48 -30
  16. package/corpus/core/docs/content/locales/ko-KR/template-plan.mdx +7 -0
  17. package/corpus/core/docs/content/locales/pt-BR/pr-visual-recap.mdx +48 -30
  18. package/corpus/core/docs/content/locales/pt-BR/template-plan.mdx +14 -0
  19. package/corpus/core/docs/content/locales/zh-CN/pr-visual-recap.mdx +48 -30
  20. package/corpus/core/docs/content/locales/zh-CN/template-plan.mdx +7 -0
  21. package/corpus/core/docs/content/locales/zh-TW/pr-visual-recap.mdx +48 -30
  22. package/corpus/core/docs/content/locales/zh-TW/template-plan.mdx +7 -0
  23. package/corpus/core/docs/content/pr-visual-recap.mdx +50 -32
  24. package/corpus/core/docs/content/template-plan.mdx +14 -0
  25. package/corpus/core/docs/content/toolkit-comments-review.mdx +16 -2
  26. package/corpus/core/package.json +4 -1
  27. package/corpus/core/src/a2a/index.ts +2 -1
  28. package/corpus/core/src/a2a/server.ts +34 -9
  29. package/corpus/core/src/agent/production-agent.ts +56 -10
  30. package/corpus/core/src/cli/create.ts +47 -19
  31. package/corpus/core/src/cli/pr-visual-recap-workflow.ts +2 -3
  32. package/corpus/core/src/cli/recap.ts +3 -4932
  33. package/corpus/core/src/cli/skills-content/visual-plan-skill.ts +6 -4
  34. package/corpus/core/src/cli/skills-content/visual-recap-skill.ts +7 -1
  35. package/corpus/core/src/client/AgentPanel.tsx +19 -17
  36. package/corpus/core/src/client/analytics.ts +8 -1
  37. package/corpus/core/src/client/chat/markdown-renderer.tsx +54 -8
  38. package/corpus/core/src/client/chat/message-components.tsx +8 -2
  39. package/corpus/core/src/client/clipboard.ts +28 -1
  40. package/corpus/core/src/client/guided-questions.tsx +4 -0
  41. package/corpus/core/src/client/i18n.tsx +2 -1
  42. package/corpus/core/src/client/index.ts +4 -0
  43. package/corpus/core/src/client/review/ReviewCommentComposer.tsx +108 -0
  44. package/corpus/core/src/client/review/ReviewThreadPanel.tsx +482 -150
  45. package/corpus/core/src/client/review/index.ts +8 -0
  46. package/corpus/core/src/client/review/use-review.ts +35 -1
  47. package/corpus/core/src/client/use-agent-chat-context.ts +3 -2
  48. package/corpus/core/src/review/actions/create-review-comment.ts +2 -1
  49. package/corpus/core/src/review/actions/get-review-feedback.ts +6 -11
  50. package/corpus/core/src/review/actions/list-review-comments.ts +38 -4
  51. package/corpus/core/src/review/actions/reply-review-comment.ts +33 -22
  52. package/corpus/core/src/review/actions/resolve-review-thread.ts +29 -2
  53. package/corpus/core/src/review/actions/send-review-thread-to-agent.ts +62 -0
  54. package/corpus/core/src/review/identity.ts +101 -0
  55. package/corpus/core/src/review/index.ts +14 -0
  56. package/corpus/core/src/review/store.ts +315 -8
  57. package/corpus/core/src/review/types.ts +4 -0
  58. package/corpus/core/src/server/action-discovery.ts +4 -0
  59. package/corpus/core/src/server/action-routes.ts +125 -4
  60. package/corpus/core/src/server/agent-chat/plugin-options.ts +16 -0
  61. package/corpus/core/src/server/agent-chat-plugin.ts +1 -0
  62. package/corpus/core/src/server/index.ts +8 -0
  63. package/corpus/core/src/vite/action-types-plugin.ts +4 -0
  64. package/corpus/templates/analytics/changelog/2026-07-14-daily-dashboard-email-captures-authenticate-with-a-sessi.md +6 -0
  65. package/corpus/templates/analytics/server/lib/dashboard-report.ts +156 -3
  66. package/corpus/templates/assets/app/components/generation/GenerationResults.tsx +402 -142
  67. package/corpus/templates/assets/app/i18n/zh-TW.ts +1 -0
  68. package/corpus/templates/assets/app/i18n-data.ts +1 -0
  69. package/corpus/templates/assets/app/routes/library.tsx +187 -48
  70. package/corpus/templates/assets/changelog/2026-07-13-merged-the-candidates-panel-into-the-library-as-a-drafts-tab.md +6 -0
  71. package/corpus/templates/chat/app/lib/agent-page.tsx +40 -0
  72. package/corpus/templates/chat/app/routes/agent.tsx +5 -3
  73. package/corpus/templates/chat/changelog/2026-07-14-fixed-chat-template-startup-with-older-core-versions.md +6 -0
  74. package/corpus/templates/clips/.agents/skills/ai-video-tools/SKILL.md +5 -0
  75. package/corpus/templates/clips/AGENTS.md +4 -0
  76. package/corpus/templates/clips/actions/get-feature-flags.ts +30 -0
  77. package/corpus/templates/clips/actions/lib/transcript-preview.ts +48 -0
  78. package/corpus/templates/clips/actions/request-transcript.ts +41 -1
  79. package/corpus/templates/clips/actions/view-screen.ts +5 -9
  80. package/corpus/templates/clips/app/components/library/library-layout.tsx +1 -1
  81. package/corpus/templates/clips/app/components/player/video-player.tsx +39 -1
  82. package/corpus/templates/clips/app/hooks/use-desktop-promo.ts +6 -12
  83. package/corpus/templates/clips/app/hooks/use-mse-video-source.ts +146 -0
  84. package/corpus/templates/clips/app/lib/capture-install-options.ts +21 -0
  85. package/corpus/templates/clips/app/lib/fmp4.ts +271 -0
  86. package/corpus/templates/clips/app/lib/mse-video-loader.ts +585 -0
  87. package/corpus/templates/clips/app/routes/_app.dictate.tsx +0 -4
  88. package/corpus/templates/clips/app/routes/_app.meetings.$meetingId.tsx +5 -13
  89. package/corpus/templates/clips/app/routes/_app.meetings._index.tsx +2 -37
  90. package/corpus/templates/clips/app/routes/download.tsx +3 -2
  91. package/corpus/templates/clips/changelog/2026-07-10-shared-clips-now-start-playing-instantly-instead-of-showing-.md +6 -0
  92. package/corpus/templates/clips/changelog/2026-07-14-clips-now-distinguishes-transcript-previews-from-incomplete-.md +6 -0
  93. package/corpus/templates/clips/changelog/2026-07-14-desktop-app-prompts-stay-hidden-after-you-download-the-insta.md +6 -0
  94. package/corpus/templates/clips/desktop/src/lib/recorder.ts +5 -0
  95. package/corpus/templates/clips/desktop/src/overlays/recording-pill.tsx +10 -0
  96. package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +4 -0
  97. package/corpus/templates/clips/desktop/src-tauri/src/native_screen/custom_capture.rs +2518 -0
  98. package/corpus/templates/clips/desktop/src-tauri/src/native_screen/live_upload.rs +353 -0
  99. package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +558 -67
  100. package/corpus/templates/clips/desktop/src-tauri/src/recording_indicator.rs +11 -1
  101. package/corpus/templates/clips/desktop/src-tauri/src/remote_flags.rs +188 -0
  102. package/corpus/templates/clips/desktop/src-tauri/src/whisper_speech.rs +1 -1
  103. package/corpus/templates/clips/server/lib/post-finalize-dispatch.ts +1 -0
  104. package/corpus/templates/clips/server/plugins/agent-chat.ts +5 -0
  105. package/corpus/templates/clips/server/routes/api/_agent-native-background/post-finalize-worker.post.ts +5 -1
  106. package/corpus/templates/clips/shared/feature-flags.ts +41 -0
  107. package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +3 -0
  108. package/corpus/templates/design/.agents/skills/design-review-feedback/SKILL.md +42 -0
  109. package/corpus/templates/design/AGENTS.md +6 -0
  110. package/corpus/templates/design/README.md +1 -0
  111. package/corpus/templates/design/actions/navigate.ts +4 -4
  112. package/corpus/templates/design/actions/view-screen.ts +116 -2
  113. package/corpus/templates/design/agent-native.app-skill.json +5 -0
  114. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +52 -59
  115. package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +13 -9
  116. package/corpus/templates/design/app/components/design/EditPanel.tsx +79 -11
  117. package/corpus/templates/design/app/components/design/LayersPanel.tsx +78 -65
  118. package/corpus/templates/design/app/components/design/ReviewCommentsPanel.tsx +161 -0
  119. package/corpus/templates/design/app/components/design/ReviewStatusControl.tsx +96 -0
  120. package/corpus/templates/design/app/components/design/bridge/hit-test.bridge.ts +166 -0
  121. package/corpus/templates/design/app/components/layout/Layout.tsx +1 -0
  122. package/corpus/templates/design/app/components/visual-editor/ReviewCanvasPins.tsx +1077 -0
  123. package/corpus/templates/design/app/components/visual-editor/index.ts +1 -0
  124. package/corpus/templates/design/app/components/visual-editor/review-canvas-state.ts +42 -0
  125. package/corpus/templates/design/app/hooks/use-design-systems.ts +2 -2
  126. package/corpus/templates/design/app/hooks/use-navigation-state.ts +9 -6
  127. package/corpus/templates/design/app/hooks/use-question-flow.ts +7 -1
  128. package/corpus/templates/design/app/i18n/ar-SA.ts +56 -1
  129. package/corpus/templates/design/app/i18n/de-DE.ts +57 -1
  130. package/corpus/templates/design/app/i18n/en-US.ts +56 -1
  131. package/corpus/templates/design/app/i18n/es-ES.ts +56 -1
  132. package/corpus/templates/design/app/i18n/fr-FR.ts +57 -1
  133. package/corpus/templates/design/app/i18n/hi-IN.ts +56 -1
  134. package/corpus/templates/design/app/i18n/ja-JP.ts +56 -1
  135. package/corpus/templates/design/app/i18n/ko-KR.ts +56 -1
  136. package/corpus/templates/design/app/i18n/pt-BR.ts +57 -1
  137. package/corpus/templates/design/app/i18n/zh-CN.ts +56 -1
  138. package/corpus/templates/design/app/i18n/zh-TW.ts +51 -0
  139. package/corpus/templates/design/app/pages/Present.tsx +172 -14
  140. package/corpus/templates/design/app/pages/design-editor/overview-camera.ts +23 -0
  141. package/corpus/templates/design/app/pages/design-editor/tool-state.ts +10 -0
  142. package/corpus/templates/design/app/pages/present-review-state.ts +18 -0
  143. package/corpus/templates/design/app/public-routes.ts +8 -0
  144. package/corpus/templates/design/app/root.tsx +7 -6
  145. package/corpus/templates/design/changelog/2026-07-13-dragged-design-assets-now-appear-at-the-visible-drop-locatio.md +6 -0
  146. package/corpus/templates/design/changelog/2026-07-13-reviewers-can-pin-comments-on-shared-designs-and-apply-verif.md +6 -0
  147. package/corpus/templates/design/server/plugins/agent-chat.ts +10 -0
  148. package/corpus/templates/design/server/plugins/auth.ts +7 -5
  149. package/corpus/templates/design/server/plugins/review.ts +9 -0
  150. package/corpus/templates/design/shared/review-anchor.ts +83 -0
  151. package/corpus/templates/design/shared/review-summary.ts +25 -0
  152. package/corpus/templates/forms/.agents/skills/form-publishing/SKILL.md +2 -0
  153. package/corpus/templates/forms/AGENTS.md +4 -0
  154. package/corpus/templates/forms/actions/create-form.ts +2 -1
  155. package/corpus/templates/forms/actions/update-form.ts +5 -2
  156. package/corpus/templates/forms/app/i18n/ar-SA.ts +3 -0
  157. package/corpus/templates/forms/app/i18n/de-DE.ts +3 -0
  158. package/corpus/templates/forms/app/i18n/en-US.ts +3 -0
  159. package/corpus/templates/forms/app/i18n/es-ES.ts +3 -0
  160. package/corpus/templates/forms/app/i18n/fr-FR.ts +3 -0
  161. package/corpus/templates/forms/app/i18n/hi-IN.ts +3 -0
  162. package/corpus/templates/forms/app/i18n/ja-JP.ts +3 -0
  163. package/corpus/templates/forms/app/i18n/ko-KR.ts +3 -0
  164. package/corpus/templates/forms/app/i18n/pt-BR.ts +3 -0
  165. package/corpus/templates/forms/app/i18n/zh-CN.ts +3 -0
  166. package/corpus/templates/forms/app/i18n/zh-TW.ts +3 -0
  167. package/corpus/templates/forms/app/pages/FormBuilderPage.tsx +19 -0
  168. package/corpus/templates/forms/changelog/2026-07-14-fixed-the-ask-forms-loading-input-appearing-above-the-welcom.md +6 -0
  169. package/corpus/templates/forms/changelog/2026-07-14-form-social-previews-now-show-the-form-title-description-and.md +6 -0
  170. package/corpus/templates/forms/changelog/2026-07-14-forms-can-email-the-form-owner-when-new-responses-arrive.md +6 -0
  171. package/corpus/templates/forms/server/handlers/submissions.ts +17 -0
  172. package/corpus/templates/forms/server/lib/form-og-image.ts +64 -6
  173. package/corpus/templates/forms/server/lib/public-form-ssr.ts +6 -2
  174. package/corpus/templates/forms/server/lib/response-email.ts +65 -0
  175. package/corpus/templates/forms/server/plugins/agent-chat.ts +1 -0
  176. package/corpus/templates/forms/server/routes/api/forms/og/[...slug]/og.png.get.ts +184 -0
  177. package/corpus/templates/forms/shared/types.ts +2 -0
  178. package/corpus/templates/plan/.agents/skills/visual-plan/SKILL.md +6 -4
  179. package/corpus/templates/plan/.agents/skills/visual-recap/SKILL.md +7 -1
  180. package/dist/a2a/index.d.ts +2 -1
  181. package/dist/a2a/index.d.ts.map +1 -1
  182. package/dist/a2a/index.js +1 -1
  183. package/dist/a2a/index.js.map +1 -1
  184. package/dist/a2a/server.d.ts +25 -0
  185. package/dist/a2a/server.d.ts.map +1 -1
  186. package/dist/a2a/server.js +30 -6
  187. package/dist/a2a/server.js.map +1 -1
  188. package/dist/agent/production-agent.d.ts +2 -0
  189. package/dist/agent/production-agent.d.ts.map +1 -1
  190. package/dist/agent/production-agent.js +37 -12
  191. package/dist/agent/production-agent.js.map +1 -1
  192. package/dist/cli/create.d.ts +5 -3
  193. package/dist/cli/create.d.ts.map +1 -1
  194. package/dist/cli/create.js +44 -19
  195. package/dist/cli/create.js.map +1 -1
  196. package/dist/cli/pr-visual-recap-workflow.d.ts +2 -2
  197. package/dist/cli/pr-visual-recap-workflow.d.ts.map +1 -1
  198. package/dist/cli/pr-visual-recap-workflow.js +2 -2
  199. package/dist/cli/pr-visual-recap-workflow.js.map +1 -1
  200. package/dist/cli/recap.d.ts +3 -562
  201. package/dist/cli/recap.d.ts.map +1 -1
  202. package/dist/cli/recap.js +3 -3875
  203. package/dist/cli/recap.js.map +1 -1
  204. package/dist/cli/skills-content/visual-plan-skill.d.ts +1 -1
  205. package/dist/cli/skills-content/visual-plan-skill.d.ts.map +1 -1
  206. package/dist/cli/skills-content/visual-plan-skill.js +6 -4
  207. package/dist/cli/skills-content/visual-plan-skill.js.map +1 -1
  208. package/dist/cli/skills-content/visual-recap-skill.d.ts +1 -1
  209. package/dist/cli/skills-content/visual-recap-skill.d.ts.map +1 -1
  210. package/dist/cli/skills-content/visual-recap-skill.js +7 -1
  211. package/dist/cli/skills-content/visual-recap-skill.js.map +1 -1
  212. package/dist/client/AgentPanel.d.ts.map +1 -1
  213. package/dist/client/AgentPanel.js +3 -3
  214. package/dist/client/AgentPanel.js.map +1 -1
  215. package/dist/client/analytics.d.ts +5 -0
  216. package/dist/client/analytics.d.ts.map +1 -1
  217. package/dist/client/analytics.js +3 -1
  218. package/dist/client/analytics.js.map +1 -1
  219. package/dist/client/chat/markdown-renderer.d.ts +1 -0
  220. package/dist/client/chat/markdown-renderer.d.ts.map +1 -1
  221. package/dist/client/chat/markdown-renderer.js +36 -1
  222. package/dist/client/chat/markdown-renderer.js.map +1 -1
  223. package/dist/client/chat/message-components.d.ts.map +1 -1
  224. package/dist/client/chat/message-components.js +5 -2
  225. package/dist/client/chat/message-components.js.map +1 -1
  226. package/dist/client/clipboard.d.ts +3 -1
  227. package/dist/client/clipboard.d.ts.map +1 -1
  228. package/dist/client/clipboard.js +24 -1
  229. package/dist/client/clipboard.js.map +1 -1
  230. package/dist/client/guided-questions.d.ts +3 -1
  231. package/dist/client/guided-questions.d.ts.map +1 -1
  232. package/dist/client/guided-questions.js +2 -1
  233. package/dist/client/guided-questions.js.map +1 -1
  234. package/dist/client/i18n.d.ts.map +1 -1
  235. package/dist/client/i18n.js +3 -1
  236. package/dist/client/i18n.js.map +1 -1
  237. package/dist/client/index.d.ts +1 -1
  238. package/dist/client/index.d.ts.map +1 -1
  239. package/dist/client/index.js +1 -1
  240. package/dist/client/index.js.map +1 -1
  241. package/dist/client/review/ReviewCommentComposer.d.ts +18 -0
  242. package/dist/client/review/ReviewCommentComposer.d.ts.map +1 -0
  243. package/dist/client/review/ReviewCommentComposer.js +30 -0
  244. package/dist/client/review/ReviewCommentComposer.js.map +1 -0
  245. package/dist/client/review/ReviewThreadPanel.d.ts +28 -1
  246. package/dist/client/review/ReviewThreadPanel.d.ts.map +1 -1
  247. package/dist/client/review/ReviewThreadPanel.js +151 -39
  248. package/dist/client/review/ReviewThreadPanel.js.map +1 -1
  249. package/dist/client/review/index.d.ts +3 -2
  250. package/dist/client/review/index.d.ts.map +1 -1
  251. package/dist/client/review/index.js +2 -1
  252. package/dist/client/review/index.js.map +1 -1
  253. package/dist/client/review/use-review.d.ts +23 -0
  254. package/dist/client/review/use-review.d.ts.map +1 -1
  255. package/dist/client/review/use-review.js +3 -0
  256. package/dist/client/review/use-review.js.map +1 -1
  257. package/dist/client/use-agent-chat-context.d.ts +1 -1
  258. package/dist/client/use-agent-chat-context.d.ts.map +1 -1
  259. package/dist/client/use-agent-chat-context.js +4 -2
  260. package/dist/client/use-agent-chat-context.js.map +1 -1
  261. package/dist/collab/awareness.d.ts +2 -2
  262. package/dist/collab/awareness.d.ts.map +1 -1
  263. package/dist/collab/struct-routes.d.ts +1 -1
  264. package/dist/notifications/routes.d.ts +3 -3
  265. package/dist/observability/routes.d.ts +5 -5
  266. package/dist/progress/routes.d.ts +1 -1
  267. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  268. package/dist/resources/handlers.d.ts +2 -2
  269. package/dist/review/actions/create-review-comment.js +2 -1
  270. package/dist/review/actions/create-review-comment.js.map +1 -1
  271. package/dist/review/actions/get-review-feedback.js +6 -11
  272. package/dist/review/actions/get-review-feedback.js.map +1 -1
  273. package/dist/review/actions/list-review-comments.d.ts +1 -0
  274. package/dist/review/actions/list-review-comments.js +25 -4
  275. package/dist/review/actions/list-review-comments.js.map +1 -1
  276. package/dist/review/actions/reply-review-comment.js +12 -5
  277. package/dist/review/actions/reply-review-comment.js.map +1 -1
  278. package/dist/review/actions/resolve-review-thread.d.ts +3 -0
  279. package/dist/review/actions/resolve-review-thread.js +20 -3
  280. package/dist/review/actions/resolve-review-thread.js.map +1 -1
  281. package/dist/review/actions/send-review-thread-to-agent.d.ts +17 -0
  282. package/dist/review/actions/send-review-thread-to-agent.d.ts.map +1 -0
  283. package/dist/review/actions/send-review-thread-to-agent.js +49 -0
  284. package/dist/review/actions/send-review-thread-to-agent.js.map +1 -0
  285. package/dist/review/identity.d.ts +6 -0
  286. package/dist/review/identity.d.ts.map +1 -0
  287. package/dist/review/identity.js +68 -0
  288. package/dist/review/identity.js.map +1 -0
  289. package/dist/review/index.d.ts +3 -1
  290. package/dist/review/index.d.ts.map +1 -1
  291. package/dist/review/index.js +2 -1
  292. package/dist/review/index.js.map +1 -1
  293. package/dist/review/store.d.ts +36 -0
  294. package/dist/review/store.d.ts.map +1 -1
  295. package/dist/review/store.js +223 -8
  296. package/dist/review/store.js.map +1 -1
  297. package/dist/review/types.d.ts +4 -0
  298. package/dist/review/types.d.ts.map +1 -1
  299. package/dist/review/types.js.map +1 -1
  300. package/dist/secrets/routes.d.ts +9 -9
  301. package/dist/server/action-discovery.d.ts.map +1 -1
  302. package/dist/server/action-discovery.js +4 -0
  303. package/dist/server/action-discovery.js.map +1 -1
  304. package/dist/server/action-routes.d.ts +45 -0
  305. package/dist/server/action-routes.d.ts.map +1 -1
  306. package/dist/server/action-routes.js +71 -5
  307. package/dist/server/action-routes.js.map +1 -1
  308. package/dist/server/agent-chat/plugin-options.d.ts +16 -0
  309. package/dist/server/agent-chat/plugin-options.d.ts.map +1 -1
  310. package/dist/server/agent-chat/plugin-options.js.map +1 -1
  311. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  312. package/dist/server/agent-chat-plugin.js +1 -0
  313. package/dist/server/agent-chat-plugin.js.map +1 -1
  314. package/dist/server/index.d.ts +3 -1
  315. package/dist/server/index.d.ts.map +1 -1
  316. package/dist/server/index.js +2 -0
  317. package/dist/server/index.js.map +1 -1
  318. package/dist/vite/action-types-plugin.d.ts.map +1 -1
  319. package/dist/vite/action-types-plugin.js +4 -0
  320. package/dist/vite/action-types-plugin.js.map +1 -1
  321. package/docs/content/locales/ar-SA/pr-visual-recap.mdx +48 -30
  322. package/docs/content/locales/ar-SA/template-plan.mdx +7 -0
  323. package/docs/content/locales/de-DE/pr-visual-recap.mdx +48 -30
  324. package/docs/content/locales/de-DE/template-plan.mdx +7 -0
  325. package/docs/content/locales/es-ES/pr-visual-recap.mdx +48 -30
  326. package/docs/content/locales/es-ES/template-plan.mdx +14 -0
  327. package/docs/content/locales/fr-FR/pr-visual-recap.mdx +48 -30
  328. package/docs/content/locales/fr-FR/template-plan.mdx +7 -0
  329. package/docs/content/locales/hi-IN/pr-visual-recap.mdx +48 -30
  330. package/docs/content/locales/hi-IN/template-plan.mdx +7 -0
  331. package/docs/content/locales/ja-JP/pr-visual-recap.mdx +48 -30
  332. package/docs/content/locales/ja-JP/template-plan.mdx +7 -0
  333. package/docs/content/locales/ko-KR/pr-visual-recap.mdx +48 -30
  334. package/docs/content/locales/ko-KR/template-plan.mdx +7 -0
  335. package/docs/content/locales/pt-BR/pr-visual-recap.mdx +48 -30
  336. package/docs/content/locales/pt-BR/template-plan.mdx +14 -0
  337. package/docs/content/locales/zh-CN/pr-visual-recap.mdx +48 -30
  338. package/docs/content/locales/zh-CN/template-plan.mdx +7 -0
  339. package/docs/content/locales/zh-TW/pr-visual-recap.mdx +48 -30
  340. package/docs/content/locales/zh-TW/template-plan.mdx +7 -0
  341. package/docs/content/pr-visual-recap.mdx +50 -32
  342. package/docs/content/template-plan.mdx +14 -0
  343. package/docs/content/toolkit-comments-review.mdx +16 -2
  344. package/package.json +4 -1
  345. package/corpus/templates/forms/server/routes/api/forms/og/[slug]/og.png.get.ts +0 -45
@@ -63,6 +63,7 @@ Each form has a `settings` JSON object:
63
63
  "successMessage": "Thank you! Your response has been recorded.",
64
64
  "redirectUrl": null,
65
65
  "showProgressBar": false,
66
+ "emailOnNewResponses": false,
66
67
  "anonymous": false,
67
68
  "integrations": []
68
69
  }
@@ -74,6 +75,7 @@ Each form has a `settings` JSON object:
74
75
  | `successMessage` | string | Message shown after successful submission |
75
76
  | `redirectUrl` | string | URL to redirect to after submission |
76
77
  | `showProgressBar` | boolean | Show progress bar for multi-section forms |
78
+ | `emailOnNewResponses` | boolean | Email the form owner's account when someone submits a response |
77
79
  | `anonymous` | boolean | Suppress IP, submitter identity, chat/run ids, page URL, and client-surface metadata for every response |
78
80
  | `integrations` | array | Webhook/Slack/Discord notification configs |
79
81
 
@@ -32,6 +32,10 @@ Detailed building, publishing, response, storage, and UI rules live in
32
32
  `create-form` call with `status: "published"`, verify the persisted form, and
33
33
  return the action's exact public `/f/<slug>` response URL rather than only
34
34
  the private editor link.
35
+ - To email the form owner when someone submits a response, set
36
+ `settings.emailOnNewResponses: true` through `create-form` or `update-form`.
37
+ Delivery uses the configured framework email provider (`RESEND_API_KEY` or
38
+ `SENDGRID_API_KEY`) and sends to the form owner's account email.
35
39
  - Form UX should stay focused: clear labels, sensible validation, minimal
36
40
  required fields, and progressive disclosure for advanced settings.
37
41
  - Public form submission endpoints must be intentionally public; keep management
@@ -36,7 +36,7 @@ function formDeepLink(formId: string): string {
36
36
 
37
37
  export default defineAction({
38
38
  description:
39
- "Create a draft or published form. Set settings.anonymous=true to suppress submitter IP, identity, and source metadata. Published forms return a direct public response URL; drafts return an editor URL.",
39
+ "Create a draft or published form. Set settings.anonymous=true to suppress submitter IP, identity, and source metadata, or settings.emailOnNewResponses=true to email the form owner when responses arrive. Published forms return a direct public response URL; drafts return an editor URL.",
40
40
  schema: z.object({
41
41
  title: z.string().optional().describe("Form title"),
42
42
  description: z.string().optional().describe("Form description"),
@@ -94,6 +94,7 @@ export default defineAction({
94
94
  submitText: "Submit",
95
95
  successMessage: "Thank you! Your response has been recorded.",
96
96
  showProgressBar: false,
97
+ emailOnNewResponses: false,
97
98
  };
98
99
 
99
100
  let settings = defaultSettings;
@@ -18,7 +18,8 @@ function slugify(text: string): string {
18
18
  }
19
19
 
20
20
  export default defineAction({
21
- description: "Update an existing form.",
21
+ description:
22
+ "Update an existing form, including settings.emailOnNewResponses to email the form owner when new responses arrive.",
22
23
  schema: z.object({
23
24
  id: z.string().describe("Form ID (required)"),
24
25
  title: z.string().optional().describe("New title"),
@@ -34,7 +35,9 @@ export default defineAction({
34
35
  settings: z
35
36
  .union([z.string(), z.record(z.string(), z.any())])
36
37
  .optional()
37
- .describe("Form settings object (or JSON string of the same)"),
38
+ .describe(
39
+ "Form settings object (or JSON string of the same). Set emailOnNewResponses=true to email the form owner for each new response.",
40
+ ),
38
41
  status: z
39
42
  .enum(["draft", "published", "closed"])
40
43
  .optional()
@@ -197,6 +197,9 @@ const messages = {
197
197
  anonymousResponses: "ردود مجهولة الهوية",
198
198
  anonymousResponsesDescription:
199
199
  "عدم الاحتفاظ بعناوين IP أو هوية المجيب أو بيانات المصدر الوصفية.",
200
+ emailNewResponses: "أرسل لي بريدًا إلكترونيًا عند وصول ردود جديدة",
201
+ emailNewResponsesDescription:
202
+ "إرسال إشعار إلى البريد الإلكتروني لحساب مالك النموذج عند إرسال أي رد.",
200
203
  saveSettings: "حفظ الإعدادات",
201
204
  },
202
205
  integrations: {
@@ -196,6 +196,9 @@ const messages = {
196
196
  anonymousResponses: "Anonyme Antworten",
197
197
  anonymousResponsesDescription:
198
198
  "IP-Adressen, Identität der antwortenden Person und Quellmetadaten nicht speichern.",
199
+ emailNewResponses: "Bei neuen Antworten per E-Mail benachrichtigen",
200
+ emailNewResponsesDescription:
201
+ "Sendet bei jeder Antwort eine E-Mail an die Kontoadresse des Formulareigentümers.",
199
202
  saveSettings: "Einstellungen speichern",
200
203
  },
201
204
  integrations: {
@@ -194,6 +194,9 @@ const messages = {
194
194
  anonymousResponses: "Anonymous responses",
195
195
  anonymousResponsesDescription:
196
196
  "Do not retain IP addresses, submitter identity, or source metadata.",
197
+ emailNewResponses: "Email me for new responses",
198
+ emailNewResponsesDescription:
199
+ "Send a notification to the form owner's account email whenever someone submits a response.",
197
200
  saveSettings: "Save Settings",
198
201
  },
199
202
  integrations: {
@@ -197,6 +197,9 @@ const messages = {
197
197
  anonymousResponses: "Respuestas anónimas",
198
198
  anonymousResponsesDescription:
199
199
  "No conservar direcciones IP, identidad del remitente ni metadatos de origen.",
200
+ emailNewResponses: "Enviarme un correo por respuestas nuevas",
201
+ emailNewResponsesDescription:
202
+ "Envía una notificación al correo de la cuenta propietaria del formulario cada vez que alguien responde.",
200
203
  saveSettings: "Guardar ajustes",
201
204
  },
202
205
  integrations: {
@@ -198,6 +198,9 @@ const messages = {
198
198
  anonymousResponses: "Réponses anonymes",
199
199
  anonymousResponsesDescription:
200
200
  "Ne pas conserver les adresses IP, l’identité du répondant ni les métadonnées de provenance.",
201
+ emailNewResponses: "M'envoyer un e-mail pour les nouvelles réponses",
202
+ emailNewResponsesDescription:
203
+ "Envoyer une notification à l'adresse e-mail du compte propriétaire du formulaire à chaque réponse.",
201
204
  saveSettings: "Enregistrer les paramètres",
202
205
  },
203
206
  integrations: {
@@ -193,6 +193,9 @@ const messages = {
193
193
  anonymousResponses: "गुमनाम प्रतिक्रियाएँ",
194
194
  anonymousResponsesDescription:
195
195
  "IP पते, प्रतिक्रिया देने वाले की पहचान या स्रोत मेटाडेटा सहेजें नहीं।",
196
+ emailNewResponses: "नई प्रतिक्रियाओं के लिए मुझे ईमेल करें",
197
+ emailNewResponsesDescription:
198
+ "हर प्रतिक्रिया पर फ़ॉर्म के मालिक के खाते के ईमेल पर सूचना भेजें।",
196
199
  saveSettings: "सेटिंग्स सहेजें",
197
200
  },
198
201
  integrations: {
@@ -194,6 +194,9 @@ const messages = {
194
194
  anonymousResponses: "匿名の回答",
195
195
  anonymousResponsesDescription:
196
196
  "IP アドレス、回答者の身元、送信元メタデータを保存しません。",
197
+ emailNewResponses: "新しい回答をメールで知らせる",
198
+ emailNewResponsesDescription:
199
+ "回答が送信されるたびに、フォーム所有者のアカウントのメールアドレスへ通知します。",
197
200
  saveSettings: "設定を保存",
198
201
  },
199
202
  integrations: {
@@ -191,6 +191,9 @@ const messages = {
191
191
  anonymousResponses: "익명 응답",
192
192
  anonymousResponsesDescription:
193
193
  "IP 주소, 응답자 신원 또는 출처 메타데이터를 저장하지 않습니다.",
194
+ emailNewResponses: "새 응답을 이메일로 받기",
195
+ emailNewResponsesDescription:
196
+ "응답이 제출될 때마다 양식 소유자 계정의 이메일 주소로 알림을 보냅니다.",
194
197
  saveSettings: "설정 저장",
195
198
  },
196
199
  integrations: {
@@ -197,6 +197,9 @@ const messages = {
197
197
  anonymousResponses: "Respostas anônimas",
198
198
  anonymousResponsesDescription:
199
199
  "Não guardar endereços IP, identidade do respondente nem metadados de origem.",
200
+ emailNewResponses: "Enviar-me um e-mail sobre novas respostas",
201
+ emailNewResponsesDescription:
202
+ "Envie uma notificação para o e-mail da conta proprietária do formulário sempre que alguém responder.",
200
203
  saveSettings: "Salvar configurações",
201
204
  },
202
205
  integrations: {
@@ -182,6 +182,9 @@ const messages = {
182
182
  redirectUrl: "重定向 URL(可选)",
183
183
  anonymousResponses: "匿名回复",
184
184
  anonymousResponsesDescription: "不保留 IP 地址、回复者身份或来源元数据。",
185
+ emailNewResponses: "有新回复时通过电子邮件通知我",
186
+ emailNewResponsesDescription:
187
+ "每当有人提交回复时,向表单所有者账户的电子邮件地址发送通知。",
185
188
  saveSettings: "保存设置",
186
189
  },
187
190
  integrations: {
@@ -183,6 +183,9 @@ const messages = {
183
183
  anonymousResponses: "匿名回覆",
184
184
  anonymousResponsesDescription:
185
185
  "不保留 IP 位址、回覆者身分或來源中繼資料。",
186
+ emailNewResponses: "有新回覆時寄信通知我",
187
+ emailNewResponsesDescription:
188
+ "每當有人提交回覆時,就通知表單擁有者帳戶的電子郵件地址。",
186
189
  saveSettings: "儲存設定",
187
190
  },
188
191
  integrations: {
@@ -1621,6 +1621,25 @@ function SettingsEditor({
1621
1621
  />
1622
1622
  </div>
1623
1623
 
1624
+ <div className="flex items-start justify-between gap-4 rounded-lg border border-border/60 bg-card p-3">
1625
+ <div className="space-y-1">
1626
+ <Label htmlFor="email-new-responses" className="text-xs">
1627
+ {t("builder.settings.emailNewResponses")}
1628
+ </Label>
1629
+ <p className="text-xs leading-5 text-muted-foreground">
1630
+ {t("builder.settings.emailNewResponsesDescription")}
1631
+ </p>
1632
+ </div>
1633
+ <Switch
1634
+ id="email-new-responses"
1635
+ checked={settings.emailOnNewResponses === true}
1636
+ onCheckedChange={(emailOnNewResponses) =>
1637
+ update({ emailOnNewResponses })
1638
+ }
1639
+ aria-label={t("builder.settings.emailNewResponses")}
1640
+ />
1641
+ </div>
1642
+
1624
1643
  <Button
1625
1644
  onClick={() => onSave(settings)}
1626
1645
  className="w-full active:scale-[0.96]"
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-14
4
+ ---
5
+
6
+ Fixed the Ask Forms loading input appearing above the welcome message.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-14
4
+ ---
5
+
6
+ Form social previews now show the form title, description, and owner photo.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: added
3
+ date: 2026-07-14
4
+ ---
5
+
6
+ Forms can email the form owner when new responses arrive
@@ -29,6 +29,7 @@ import type {
29
29
  } from "../../shared/types.js";
30
30
  import { getDb, schema } from "../db/index.js";
31
31
  import { fireIntegrations } from "../lib/integrations.js";
32
+ import { sendNewResponseEmail } from "../lib/response-email.js";
32
33
  import {
33
34
  isEmptySubmissionValue,
34
35
  validateSubmissionField,
@@ -265,6 +266,22 @@ export const submitForm = defineEventHandler(async (event: H3Event) => {
265
266
  // Non-critical — don't fail the submission
266
267
  }
267
268
 
269
+ if (settings.emailOnNewResponses === true && form.ownerEmail) {
270
+ try {
271
+ await sendNewResponseEmail({
272
+ to: form.ownerEmail,
273
+ formTitle: form.title,
274
+ fields,
275
+ data,
276
+ submittedAt: now,
277
+ });
278
+ } catch (error) {
279
+ // Email is best-effort — a provider outage must not reject a public
280
+ // submission that was already persisted successfully.
281
+ console.warn("[forms] new response email failed:", error);
282
+ }
283
+ }
284
+
268
285
  // Fire integrations best-effort and never fail the submission. Keep this
269
286
  // awaited: serverless hosts can freeze fire-and-forget work as soon as the
270
287
  // HTTP response returns, which silently drops Slack/webhook delivery.
@@ -1,7 +1,14 @@
1
+ import { resolveOgFontFiles } from "@agent-native/core/server";
1
2
  import type { RenderedImage, ResvgRenderOptions } from "@resvg/resvg-js";
2
3
 
3
4
  export interface FormOgImageInput {
4
5
  title?: string | null;
6
+ description?: string | null;
7
+ profileImageDataUrl?: string | null;
8
+ }
9
+
10
+ interface FormOgRenderOptions {
11
+ fontFiles?: string[];
5
12
  }
6
13
 
7
14
  const WIDTH = 1200;
@@ -23,6 +30,9 @@ const LOGO_MARK = `
23
30
  <path d="M89.446 0H114L76.2921 65.7704H51.7383L89.446 0Z" fill="url(#brand)"/>
24
31
  `;
25
32
 
33
+ const AVATAR_DATA_URL_RE =
34
+ /^data:image\/(?:png|jpe?g|gif|webp);base64,[A-Za-z0-9+/]+={0,2}$/i;
35
+
26
36
  function escapeSvg(value: string): string {
27
37
  return value
28
38
  .replace(/&/g, "&amp;")
@@ -37,6 +47,16 @@ function cleanText(value: string | null | undefined): string {
37
47
  .trim();
38
48
  }
39
49
 
50
+ function validProfileImageDataUrl(
51
+ value: string | null | undefined,
52
+ ): string | undefined {
53
+ const image = value?.trim();
54
+ if (!image || image.length > 2_000_000 || !AVATAR_DATA_URL_RE.test(image)) {
55
+ return undefined;
56
+ }
57
+ return image;
58
+ }
59
+
40
60
  function estimateTextWidth(value: string, fontSize: number): number {
41
61
  let units = 0;
42
62
  for (const char of value) {
@@ -146,14 +166,24 @@ function textBlock({
146
166
 
147
167
  export function renderFormOgImageSvg(input: FormOgImageInput = {}): string {
148
168
  const title = cleanText(input.title) || "Agent-Native Form";
169
+ const description = cleanText(input.description);
149
170
  const titleFitsSingleLine = estimateTextWidth(title, 82) <= 820;
150
171
  const titleLines = titleFitsSingleLine
151
172
  ? [title]
152
173
  : wrapText(title, 66, 820, 2);
174
+ const descriptionLines = description ? wrapText(description, 28, 820, 2) : [];
153
175
  const titleFontSize = titleFitsSingleLine ? 82 : 66;
154
176
  const titleLineHeight = titleLines.length > 1 ? 76 : 92;
155
177
  const titleGroupY = titleLines.length > 1 ? 332 : 382;
178
+ const descriptionY = titleLineHeight * (titleLines.length - 1) + 88;
156
179
  const initials = initialsFor(title);
180
+ const profileImageDataUrl = validProfileImageDataUrl(
181
+ input.profileImageDataUrl,
182
+ );
183
+ const avatarContent = profileImageDataUrl
184
+ ? `<image x="${BADGE_CX - 86}" y="${BADGE_CY - 86}" width="172" height="172" href="${escapeSvg(profileImageDataUrl)}" preserveAspectRatio="xMidYMid slice" mask="url(#avatarMask)"/>`
185
+ : `<circle cx="${BADGE_CX}" cy="${BADGE_CY}" r="72" fill="url(#brand)" fill-opacity="0.2"/>
186
+ <text x="${BADGE_CX}" y="${BADGE_CY + 20}" text-anchor="middle" font-family="${FONT_FAMILY}" font-size="56" font-weight="800" fill="${FG}">${escapeSvg(initials)}</text>`;
157
187
 
158
188
  return `<svg xmlns="http://www.w3.org/2000/svg" width="${WIDTH}" height="${HEIGHT}" viewBox="0 0 ${WIDTH} ${HEIGHT}">
159
189
  <title>${escapeSvg(title)} - Agent-Native Forms preview</title>
@@ -165,6 +195,10 @@ export function renderFormOgImageSvg(input: FormOgImageInput = {}): string {
165
195
  <pattern id="grid" width="48" height="48" patternUnits="userSpaceOnUse">
166
196
  <path d="M 48 0 L 0 0 0 48" fill="none" stroke="#ffffff" stroke-opacity="0.07" stroke-width="1"/>
167
197
  </pattern>
198
+ <mask id="avatarMask">
199
+ <rect width="${WIDTH}" height="${HEIGHT}" fill="black"/>
200
+ <circle cx="${BADGE_CX}" cy="${BADGE_CY}" r="78" fill="white"/>
201
+ </mask>
168
202
  </defs>
169
203
  <rect width="${WIDTH}" height="${HEIGHT}" fill="${BG}"/>
170
204
  <rect width="${WIDTH}" height="${HEIGHT}" fill="url(#grid)"/>
@@ -177,8 +211,7 @@ export function renderFormOgImageSvg(input: FormOgImageInput = {}): string {
177
211
  </g>
178
212
  <g>
179
213
  <circle cx="${BADGE_CX}" cy="${BADGE_CY}" r="86" fill="${SURFACE}" stroke="${BORDER}" stroke-width="2"/>
180
- <circle cx="${BADGE_CX}" cy="${BADGE_CY}" r="72" fill="url(#brand)" fill-opacity="0.2"/>
181
- <text x="${BADGE_CX}" y="${BADGE_CY + 20}" text-anchor="middle" font-family="${FONT_FAMILY}" font-size="56" font-weight="800" fill="${FG}">${escapeSvg(initials)}</text>
214
+ ${avatarContent}
182
215
  <circle cx="${BADGE_CX}" cy="${BADGE_CY}" r="78" fill="none" stroke="#ffffff" stroke-opacity="0.14" stroke-width="1"/>
183
216
  </g>
184
217
  <g transform="translate(80 ${titleGroupY})">
@@ -191,15 +224,35 @@ export function renderFormOgImageSvg(input: FormOgImageInput = {}): string {
191
224
  weight: 800,
192
225
  fill: FG,
193
226
  })}
227
+ ${
228
+ descriptionLines.length
229
+ ? textBlock({
230
+ lines: descriptionLines,
231
+ x: 0,
232
+ y: descriptionY,
233
+ fontSize: 28,
234
+ lineHeight: 38,
235
+ weight: 500,
236
+ fill: MUTED,
237
+ })
238
+ : ""
239
+ }
194
240
  </g>
195
241
  </svg>`;
196
242
  }
197
243
 
198
- function formOgResvgOptions(): ResvgRenderOptions {
244
+ export function formOgResvgOptions(
245
+ options: FormOgRenderOptions = {},
246
+ ): ResvgRenderOptions {
247
+ const fontFiles = options.fontFiles?.length
248
+ ? options.fontFiles
249
+ : resolveOgFontFiles();
250
+ const hasBundledFonts = Boolean(fontFiles?.length);
199
251
  return {
200
252
  fitTo: { mode: "width", value: WIDTH },
201
253
  font: {
202
- loadSystemFonts: true,
254
+ loadSystemFonts: !hasBundledFonts,
255
+ ...(hasBundledFonts ? { fontFiles } : {}),
203
256
  defaultFontFamily: "Liberation Sans",
204
257
  sansSerifFamily: "Liberation Sans",
205
258
  },
@@ -212,13 +265,18 @@ async function loadResvg(): Promise<typeof import("@resvg/resvg-js")> {
212
265
 
213
266
  export async function renderFormOgImage(
214
267
  input: FormOgImageInput = {},
268
+ options: FormOgRenderOptions = {},
215
269
  ): Promise<RenderedImage> {
216
270
  const { Resvg } = await loadResvg();
217
- return new Resvg(renderFormOgImageSvg(input), formOgResvgOptions()).render();
271
+ return new Resvg(
272
+ renderFormOgImageSvg(input),
273
+ formOgResvgOptions(options),
274
+ ).render();
218
275
  }
219
276
 
220
277
  export async function renderFormOgImagePng(
221
278
  input: FormOgImageInput = {},
279
+ options: FormOgRenderOptions = {},
222
280
  ): Promise<Uint8Array> {
223
- return (await renderFormOgImage(input)).asPng();
281
+ return (await renderFormOgImage(input, options)).asPng();
224
282
  }
@@ -60,6 +60,8 @@ export async function getPublicFormBySlugOrId(slugOrId: string) {
60
60
  slug: row.slug,
61
61
  title: row.title,
62
62
  description: row.description,
63
+ ownerEmail: row.ownerEmail,
64
+ updatedAt: row.updatedAt,
63
65
  fields: JSON.parse(row.fields) as FormField[],
64
66
  settings: toPublicFormSettings(settings),
65
67
  };
@@ -302,6 +304,8 @@ function renderFormPage(
302
304
  slug: string;
303
305
  title: string;
304
306
  description?: string | null;
307
+ ownerEmail?: string | null;
308
+ updatedAt?: string | null;
305
309
  fields: FormField[];
306
310
  settings: PublicFormSettings;
307
311
  },
@@ -315,7 +319,7 @@ function renderFormPage(
315
319
  const faviconPath = `${appBasePath}/favicon.svg`;
316
320
  const ogImagePath = `${appBasePath}/api/forms/og/${encodeURIComponent(
317
321
  form.slug || form.id,
318
- )}/og.png`;
322
+ )}/og.png${form.updatedAt ? `?v=${encodeURIComponent(form.updatedAt)}` : ""}`;
319
323
  const ogImageUrl = origin
320
324
  ? new URL(ogImagePath, origin).toString()
321
325
  : ogImagePath;
@@ -342,7 +346,7 @@ function renderFormPage(
342
346
  <meta property="og:image:alt" content="${escapeHtml(`${form.title} form preview`)}">
343
347
  <meta name="twitter:card" content="summary_large_image">
344
348
  <meta name="twitter:image" content="${escapeHtml(ogImageUrl)}">
345
- <meta name="twitter:image:alt" content="${AGENT_NATIVE_SOCIAL_IMAGE_ALT}">
349
+ <meta name="twitter:image:alt" content="${escapeHtml(`${form.title} form preview`)}">
346
350
  <link rel="icon" type="image/svg+xml" href="${faviconPath}">
347
351
  <!-- Self-hosted Inter via Bunny Fonts CDN (privacy-respecting, no tracking) -->
348
352
  <link rel="preconnect" href="https://fonts.bunny.net">
@@ -0,0 +1,65 @@
1
+ import { emailStrong, renderEmail, sendEmail } from "@agent-native/core/server";
2
+
3
+ import type { FormField } from "../../shared/types.js";
4
+
5
+ export interface NewResponseEmailArgs {
6
+ to: string;
7
+ formTitle: string;
8
+ fields: FormField[];
9
+ data: Record<string, unknown>;
10
+ submittedAt: string;
11
+ }
12
+
13
+ function stripCrlf(value: string): string {
14
+ return value.replace(/[\r\n]+/g, " ").trim();
15
+ }
16
+
17
+ function formatResponseValue(value: unknown): string {
18
+ if (Array.isArray(value)) return value.join(", ");
19
+ if (value === null || value === undefined) return "";
20
+ if (typeof value === "object") return JSON.stringify(value);
21
+ return String(value);
22
+ }
23
+
24
+ export function renderNewResponseEmail({
25
+ formTitle,
26
+ fields,
27
+ data,
28
+ submittedAt,
29
+ }: Omit<NewResponseEmailArgs, "to">) {
30
+ const safeTitle = stripCrlf(formTitle) || "Untitled form";
31
+ const responseLines = fields
32
+ .filter((field) => data[field.id] !== undefined)
33
+ .map(
34
+ (field) =>
35
+ `${emailStrong(field.label)}: ${emailStrong(
36
+ formatResponseValue(data[field.id]),
37
+ )}`,
38
+ );
39
+
40
+ return {
41
+ subject: `New response: ${safeTitle}`,
42
+ ...renderEmail({
43
+ preheader: `New response to ${safeTitle}`,
44
+ heading: "New form response",
45
+ paragraphs: [
46
+ `${emailStrong(safeTitle)} received a new response.`,
47
+ `Submitted at ${emailStrong(submittedAt)}.`,
48
+ responseLines.length
49
+ ? responseLines.join("<br />")
50
+ : "No response fields were submitted.",
51
+ ],
52
+ footer:
53
+ "You received this because email notifications are enabled for this form.",
54
+ }),
55
+ };
56
+ }
57
+
58
+ export async function sendNewResponseEmail(
59
+ args: NewResponseEmailArgs,
60
+ ): Promise<void> {
61
+ await sendEmail({
62
+ to: args.to,
63
+ ...renderNewResponseEmail(args),
64
+ });
65
+ }
@@ -17,6 +17,7 @@ Core rules:
17
17
  - Use \`create-form\`, \`update-form\`, \`patch-form-fields\`, \`restore-form\`, and \`delete-form\` for form lifecycle and field edits.
18
18
  - When the user asks for an anonymous feedback form or survey, create the complete form atomically with \`status: "published"\`, then return the exact public response URL from the successful action result. Do not return only the private editor link, and do not claim it is live without verifying the saved status and fields.
19
19
  - Use \`preview-form\` when the user asks about a form's setup, fields, configuration, publish state, or response count. It renders a native chat summary.
20
+ - When the user wants email notifications for new responses, set \`settings.emailOnNewResponses: true\` with \`create-form\` or \`update-form\`; notifications go to the form owner's account email.
20
21
  - Use \`response-insights\` for response analytics. Do not invent SQL or fake chart data. Pass \`displayMode: "chart"\` for chart-only requests, \`displayMode: "table"\` only when the user asks for a table/rows, and \`displayMode: "insights"\` only for combined dashboards or reports.
21
22
  - Use \`list-responses\` and \`export-responses\` for response data review and export. When the user asks to see, open, or view all responses for a form, call \`navigate\` with \`view: "responses"\` instead of listing rows in chat.
22
23
  - Use \`navigate\` to open focused workspace views such as the forms list, builder, published preview, responses, response insights, extensions, or team/settings views. For builder sub-tabs, pass \`view=form\`, the form ID, and \`tab=edit|responses|settings|integrations\`.