@agent-native/core 0.69.0 → 0.70.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (336) hide show
  1. package/README.md +15 -15
  2. package/corpus/README.md +2 -2
  3. package/corpus/core/CHANGELOG.md +109 -0
  4. package/corpus/core/docs/design/durable-agent-runs.md +217 -0
  5. package/corpus/core/package.json +1 -1
  6. package/corpus/core/src/action.ts +89 -0
  7. package/corpus/core/src/agent/model-config.ts +0 -2
  8. package/corpus/core/src/agent/run-loop-with-resume.ts +48 -0
  9. package/corpus/core/src/changelog/parse.ts +245 -0
  10. package/corpus/core/src/cli/changelog.ts +199 -0
  11. package/corpus/core/src/cli/index.ts +19 -0
  12. package/corpus/core/src/cli/recap.ts +24 -1
  13. package/corpus/core/src/cli/skills.ts +74 -47
  14. package/corpus/core/src/client/AgentPanel.tsx +5 -3
  15. package/corpus/core/src/client/AssistantChat.tsx +8 -0
  16. package/corpus/core/src/client/CommandMenu.tsx +183 -75
  17. package/corpus/core/src/client/agent-chat-adapter.ts +44 -7
  18. package/corpus/core/src/client/analytics.ts +181 -0
  19. package/corpus/core/src/client/changelog/Changelog.tsx +321 -0
  20. package/corpus/core/src/client/client-surface.ts +41 -0
  21. package/corpus/core/src/client/composer/TiptapComposer.tsx +123 -12
  22. package/corpus/core/src/client/feedback-context.ts +4 -0
  23. package/corpus/core/src/client/index.ts +12 -0
  24. package/corpus/core/src/client/mcp-apps/McpAppRenderer.tsx +54 -0
  25. package/corpus/core/src/client/settings/useBuilderStatus.ts +82 -20
  26. package/corpus/core/src/client/sse-event-processor.ts +7 -1
  27. package/corpus/core/src/mcp/build-server.ts +57 -1
  28. package/corpus/core/src/mcp/embed-app.ts +197 -7
  29. package/corpus/core/src/mcp/oauth-route.ts +109 -13
  30. package/corpus/core/src/server/attribution.ts +211 -0
  31. package/corpus/core/src/server/auth.ts +6 -0
  32. package/corpus/core/src/server/better-auth-instance.ts +49 -5
  33. package/corpus/core/src/server/builder-browser.ts +27 -1
  34. package/corpus/core/src/server/core-routes-plugin.ts +136 -0
  35. package/corpus/core/src/server/google-oauth.ts +19 -6
  36. package/corpus/core/src/server/onboarding-html.ts +35 -0
  37. package/corpus/core/src/shared/mcp-embed-headers.ts +1 -1
  38. package/corpus/core/src/styles/agent-conversation.css +21 -0
  39. package/corpus/core/src/templates/workspace-core/.agents/skills/changelog/SKILL.md +111 -0
  40. package/corpus/core/src/templates/workspace-core/.agents/skills/reliable-mutations/SKILL.md +72 -0
  41. package/corpus/core/src/templates/workspace-core/.agents/skills/tracking/SKILL.md +46 -14
  42. package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +22 -0
  43. package/corpus/templates/analytics/AGENTS.md +8 -1
  44. package/corpus/templates/analytics/CHANGELOG.md +10 -0
  45. package/corpus/templates/analytics/actions/install-dashboard-template.ts +99 -4
  46. package/corpus/templates/analytics/actions/open-traffic-dashboard.ts +1 -1
  47. package/corpus/templates/analytics/actions/update-dashboard.ts +39 -8
  48. package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +30 -3
  49. package/corpus/templates/analytics/app/components/layout/CommandPalette.tsx +180 -144
  50. package/corpus/templates/analytics/app/components/layout/Header.tsx +2 -3
  51. package/corpus/templates/analytics/app/components/layout/Layout.tsx +7 -5
  52. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +221 -215
  53. package/corpus/templates/analytics/app/hooks/use-navigation-state.ts +6 -6
  54. package/corpus/templates/analytics/app/lib/demo-dashboard-path.ts +1 -1
  55. package/corpus/templates/analytics/app/lib/last-opened.ts +2 -1
  56. package/corpus/templates/analytics/app/pages/Settings.tsx +4 -0
  57. package/corpus/templates/analytics/app/pages/adhoc/explorer-dashboard/index.tsx +3 -3
  58. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/DashboardFilterBar.tsx +2 -2
  59. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +32 -2
  60. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +101 -99
  61. package/corpus/templates/analytics/app/pages/overview/OverviewPage.tsx +1 -1
  62. package/corpus/templates/analytics/app/routes/adhoc.$id.tsx +18 -5
  63. package/corpus/templates/analytics/app/routes/catalog.tsx +1 -1
  64. package/corpus/templates/analytics/app/routes/dashboards.$id.tsx +9 -0
  65. package/corpus/templates/analytics/app/routes/traffic.tsx +1 -1
  66. package/corpus/templates/analytics/changelog/2026-06-23-long-dashboard-and-analysis-names-in-the-sidebar-now-truncat.md +6 -0
  67. package/corpus/templates/analytics/changelog/2026-06-23-open-any-dashboard-chart-full-screen-in-a-modal-from-the-pan.md +6 -0
  68. package/corpus/templates/analytics/changelog/2026-06-23-the-dashboards-and-analyses-sidebar-settings-buttons-now-sta.md +6 -0
  69. package/corpus/templates/analytics/seeds/dashboards/agent-native-templates-first-party.json +145 -0
  70. package/corpus/templates/analytics/server/db/index.ts +1 -1
  71. package/corpus/templates/analytics/server/lib/dashboard-catalog.ts +9 -2
  72. package/corpus/templates/analytics/server/lib/demo-dashboards.ts +1 -1
  73. package/corpus/templates/analytics/server/plugins/agent-chat.ts +2 -2
  74. package/corpus/templates/analytics/server/plugins/core-routes.ts +6 -6
  75. package/corpus/templates/assets/CHANGELOG.md +10 -0
  76. package/corpus/templates/assets/actions/generate-image.ts +24 -0
  77. package/corpus/templates/assets/app/components/layout/Sidebar.tsx +2 -2
  78. package/corpus/templates/assets/app/global.css +17 -1
  79. package/corpus/templates/assets/app/root.tsx +7 -1
  80. package/corpus/templates/assets/app/routes/_index.tsx +94 -14
  81. package/corpus/templates/assets/app/routes/settings.tsx +6 -0
  82. package/corpus/templates/assets/changelog/2026-06-23-chat-dates-in-the-sidebar-no-longer-wrap-onto-two-lines.md +6 -0
  83. package/corpus/templates/assets/changelog/2026-06-23-clicking-image-video-or-refine-on-the-start-screen-now-drops.md +6 -0
  84. package/corpus/templates/assets/changelog/2026-06-23-pick-your-image-generation-model-right-from-the-chat-model-m.md +6 -0
  85. package/corpus/templates/brain/CHANGELOG.md +10 -0
  86. package/corpus/templates/brain/app/root.tsx +7 -1
  87. package/corpus/templates/brain/app/routes/settings.tsx +8 -1
  88. package/corpus/templates/calendar/CHANGELOG.md +10 -0
  89. package/corpus/templates/calendar/app/pages/Settings.tsx +4 -0
  90. package/corpus/templates/calendar/app/root.tsx +7 -1
  91. package/corpus/templates/chat/CHANGELOG.md +10 -0
  92. package/corpus/templates/chat/app/root.tsx +7 -1
  93. package/corpus/templates/clips/CHANGELOG.md +10 -0
  94. package/corpus/templates/clips/app/components/library/library-grid.tsx +1 -20
  95. package/corpus/templates/clips/app/components/player/share-dialog.tsx +33 -7
  96. package/corpus/templates/clips/app/components/player/sign-in-prompt-dialog.tsx +8 -0
  97. package/corpus/templates/clips/app/components/player/video-player.tsx +150 -20
  98. package/corpus/templates/clips/app/components/recorder/countdown-overlay.tsx +72 -48
  99. package/corpus/templates/clips/app/components/recorder/pre-record-panel.tsx +7 -11
  100. package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +31 -0
  101. package/corpus/templates/clips/app/components/sharing/slack-share-hint.tsx +71 -0
  102. package/corpus/templates/clips/app/lib/countdown-audio-cue.ts +35 -17
  103. package/corpus/templates/clips/app/root.tsx +7 -1
  104. package/corpus/templates/clips/app/routes/_app.settings._index.tsx +15 -1
  105. package/corpus/templates/clips/app/routes/record.tsx +78 -5
  106. package/corpus/templates/clips/app/routes/share.$shareId.tsx +86 -5
  107. package/corpus/templates/clips/changelog/2026-06-23-clips-shared-in-slack-no-longer-show-a-could-not-start-playb.md +6 -0
  108. package/corpus/templates/clips/changelog/2026-06-23-made-the-play-button-on-shared-and-embedded-clips-scale-with.md +6 -0
  109. package/corpus/templates/clips/changelog/2026-06-23-public-clip-share-links-now-show-whether-they-ll-play-inline.md +6 -0
  110. package/corpus/templates/clips/changelog/2026-06-23-removed-select-button-from-library-hover-a-clip-to-select-it.md +6 -0
  111. package/corpus/templates/clips/changelog/2026-06-23-screen-camera-recordings-now-keep-the-presenter-s-face-in-th.md +6 -0
  112. package/corpus/templates/clips/changelog/2026-06-23-shared-video-links-now-play-for-anyone-without-signing-in.md +6 -0
  113. package/corpus/templates/clips/changelog/2026-06-23-skip-or-cancel-the-recording-countdown-with-buttons-beside-t.md +6 -0
  114. package/corpus/templates/clips/changelog/2026-06-23-the-desktop-camera-bubble-now-looks-sharp-the-instant-it-ope.md +6 -0
  115. package/corpus/templates/clips/changelog/2026-06-23-the-desktop-recording-widget-now-grows-downward-only-when-yo.md +6 -0
  116. package/corpus/templates/clips/changelog/2026-06-23-the-recording-start-sound-is-now-a-softer-more-modern-chime.md +6 -0
  117. package/corpus/templates/clips/chrome-extension/public/manifest.json +17 -8
  118. package/corpus/templates/clips/chrome-extension/src/background.ts +769 -180
  119. package/corpus/templates/clips/chrome-extension/src/content-script.ts +177 -0
  120. package/corpus/templates/clips/chrome-extension/src/offscreen.ts +466 -329
  121. package/corpus/templates/clips/chrome-extension/src/overlay.css +377 -0
  122. package/corpus/templates/clips/chrome-extension/src/overlay.html +12 -0
  123. package/corpus/templates/clips/chrome-extension/src/overlay.ts +330 -0
  124. package/corpus/templates/clips/chrome-extension/src/permission.html +227 -0
  125. package/corpus/templates/clips/chrome-extension/src/permission.ts +105 -0
  126. package/corpus/templates/clips/chrome-extension/src/popup.html +3 -0
  127. package/corpus/templates/clips/chrome-extension/src/popup.ts +50 -2
  128. package/corpus/templates/clips/chrome-extension/src/styles.css +50 -0
  129. package/corpus/templates/clips/chrome-extension/vite.config.ts +3 -0
  130. package/corpus/templates/clips/desktop/src/lib/audio-cue.ts +41 -24
  131. package/corpus/templates/clips/desktop/src/lib/bubble-webrtc.ts +119 -2
  132. package/corpus/templates/clips/desktop/src/lib/recorder.ts +20 -0
  133. package/corpus/templates/clips/desktop/src/main.tsx +3 -0
  134. package/corpus/templates/clips/desktop/src/overlays/countdown.tsx +21 -2
  135. package/corpus/templates/clips/desktop/src/overlays/region-record-border.tsx +76 -0
  136. package/corpus/templates/clips/desktop/src/styles.css +119 -2
  137. package/corpus/templates/clips/desktop/src-tauri/capabilities/default.json +2 -1
  138. package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +140 -1
  139. package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +2 -0
  140. package/corpus/templates/clips/desktop/src-tauri/src/util.rs +4 -1
  141. package/corpus/templates/clips/server/routes/api/video/[recordingId].get.ts +85 -6
  142. package/corpus/templates/clips/shared/share-attribution.ts +79 -0
  143. package/corpus/templates/content/AGENTS.md +20 -3
  144. package/corpus/templates/content/CHANGELOG.md +10 -0
  145. package/corpus/templates/content/actions/_database-utils.ts +15 -0
  146. package/corpus/templates/content/actions/_property-utils.ts +371 -4
  147. package/corpus/templates/content/actions/configure-document-property.ts +35 -1
  148. package/corpus/templates/content/actions/create-content-database.ts +5 -1
  149. package/corpus/templates/content/actions/delete-document-property.ts +52 -2
  150. package/corpus/templates/content/actions/duplicate-document-property.ts +34 -17
  151. package/corpus/templates/content/actions/reorder-document-property.ts +79 -0
  152. package/corpus/templates/content/actions/set-document-property.ts +39 -0
  153. package/corpus/templates/content/app/components/editor/DocumentBlockFields.tsx +806 -0
  154. package/corpus/templates/content/app/components/editor/DocumentDatabase.tsx +226 -68
  155. package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +59 -30
  156. package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +25 -1
  157. package/corpus/templates/content/app/components/editor/blockFieldSaveController.ts +180 -0
  158. package/corpus/templates/content/app/components/editor/blockFieldSaveRegistry.ts +179 -0
  159. package/corpus/templates/content/app/components/editor/previewDocumentSaveController.ts +244 -0
  160. package/corpus/templates/content/app/components/editor/previewDocumentSaveRegistry.ts +132 -0
  161. package/corpus/templates/content/app/global.css +9 -0
  162. package/corpus/templates/content/app/hooks/use-document-properties.ts +21 -0
  163. package/corpus/templates/content/app/root.tsx +7 -1
  164. package/corpus/templates/content/server/db/schema.ts +30 -0
  165. package/corpus/templates/content/server/plugins/db.ts +87 -0
  166. package/corpus/templates/content/shared/api.ts +7 -0
  167. package/corpus/templates/content/shared/properties.ts +109 -0
  168. package/corpus/templates/design/CHANGELOG.md +10 -0
  169. package/corpus/templates/design/actions/generate-design.ts +4 -0
  170. package/corpus/templates/design/app/root.tsx +7 -1
  171. package/corpus/templates/design/app/routes/settings.tsx +10 -2
  172. package/corpus/templates/dispatch/CHANGELOG.md +10 -0
  173. package/corpus/templates/dispatch/app/root.tsx +7 -1
  174. package/corpus/templates/forms/.agents/skills/form-responses/SKILL.md +10 -0
  175. package/corpus/templates/forms/CHANGELOG.md +10 -0
  176. package/corpus/templates/forms/actions/export-responses.ts +6 -0
  177. package/corpus/templates/forms/actions/list-responses.ts +2 -0
  178. package/corpus/templates/forms/actions/response-insights.ts +2 -0
  179. package/corpus/templates/forms/app/pages/ResponsesPage.tsx +138 -1
  180. package/corpus/templates/forms/app/root.tsx +7 -1
  181. package/corpus/templates/forms/changelog/2026-06-23-response-tables-now-show-the-page-each-submission-came-from-.md +6 -0
  182. package/corpus/templates/forms/changelog/2026-06-23-response-tables-now-show-whether-feedback-came-from-the-web-.md +6 -0
  183. package/corpus/templates/forms/server/db/schema.ts +7 -0
  184. package/corpus/templates/forms/server/handlers/submissions.ts +19 -2
  185. package/corpus/templates/forms/server/lib/integrations.ts +76 -2
  186. package/corpus/templates/forms/server/plugins/db.ts +20 -0
  187. package/corpus/templates/forms/shared/types.ts +12 -0
  188. package/corpus/templates/macros/CHANGELOG.md +10 -0
  189. package/corpus/templates/macros/app/root.tsx +7 -1
  190. package/corpus/templates/mail/CHANGELOG.md +10 -0
  191. package/corpus/templates/mail/app/components/layout/CommandPalette.tsx +3 -0
  192. package/corpus/templates/mail/app/pages/SettingsPage.tsx +27 -0
  193. package/corpus/templates/plan/.agents/skills/visual-plan/SKILL.md +31 -17
  194. package/corpus/templates/plan/.agents/skills/visual-plan/references/wireframe.md +7 -4
  195. package/corpus/templates/plan/.agents/skills/visual-recap/SKILL.md +34 -24
  196. package/corpus/templates/plan/.agents/skills/visual-recap/references/wireframe.md +7 -4
  197. package/corpus/templates/plan/AGENTS.md +4 -0
  198. package/corpus/templates/plan/CHANGELOG.md +10 -0
  199. package/corpus/templates/plan/actions/view-screen.ts +5 -0
  200. package/corpus/templates/plan/app/components/layout/Sidebar.tsx +98 -1
  201. package/corpus/templates/plan/app/components/plan/wireframe/html-artboard.css +3 -1
  202. package/corpus/templates/plan/app/pages/PlansPage.tsx +107 -7
  203. package/corpus/templates/plan/app/root.tsx +7 -1
  204. package/corpus/templates/plan/changelog/2026-06-23-large-diagrams-in-a-plan-now-scroll-within-their-block-inste.md +6 -0
  205. package/corpus/templates/plan/shared/share-attribution.ts +72 -0
  206. package/corpus/templates/slides/CHANGELOG.md +10 -0
  207. package/corpus/templates/slides/app/root.tsx +7 -1
  208. package/corpus/templates/videos/CHANGELOG.md +4 -151
  209. package/corpus/templates/videos/app/root.tsx +7 -1
  210. package/dist/action.js +87 -0
  211. package/dist/action.js.map +1 -1
  212. package/dist/agent/engine/builder-engine.d.ts +1 -1
  213. package/dist/agent/engine/builder-engine.d.ts.map +1 -1
  214. package/dist/agent/model-config.d.ts +2 -2
  215. package/dist/agent/model-config.d.ts.map +1 -1
  216. package/dist/agent/model-config.js +0 -2
  217. package/dist/agent/model-config.js.map +1 -1
  218. package/dist/agent/run-loop-with-resume.d.ts +13 -0
  219. package/dist/agent/run-loop-with-resume.d.ts.map +1 -1
  220. package/dist/agent/run-loop-with-resume.js +44 -0
  221. package/dist/agent/run-loop-with-resume.js.map +1 -1
  222. package/dist/changelog/parse.d.ts +70 -0
  223. package/dist/changelog/parse.d.ts.map +1 -0
  224. package/dist/changelog/parse.js +200 -0
  225. package/dist/changelog/parse.js.map +1 -0
  226. package/dist/cli/changelog.d.ts +4 -0
  227. package/dist/cli/changelog.d.ts.map +1 -0
  228. package/dist/cli/changelog.js +166 -0
  229. package/dist/cli/changelog.js.map +1 -0
  230. package/dist/cli/index.js +18 -0
  231. package/dist/cli/index.js.map +1 -1
  232. package/dist/cli/recap.d.ts.map +1 -1
  233. package/dist/cli/recap.js +22 -1
  234. package/dist/cli/recap.js.map +1 -1
  235. package/dist/cli/skills.d.ts +3 -3
  236. package/dist/cli/skills.d.ts.map +1 -1
  237. package/dist/cli/skills.js +74 -47
  238. package/dist/cli/skills.js.map +1 -1
  239. package/dist/client/AgentPanel.d.ts.map +1 -1
  240. package/dist/client/AgentPanel.js +5 -3
  241. package/dist/client/AgentPanel.js.map +1 -1
  242. package/dist/client/AssistantChat.d.ts +6 -1
  243. package/dist/client/AssistantChat.d.ts.map +1 -1
  244. package/dist/client/AssistantChat.js +2 -2
  245. package/dist/client/AssistantChat.js.map +1 -1
  246. package/dist/client/CommandMenu.d.ts +17 -1
  247. package/dist/client/CommandMenu.d.ts.map +1 -1
  248. package/dist/client/CommandMenu.js +49 -13
  249. package/dist/client/CommandMenu.js.map +1 -1
  250. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  251. package/dist/client/agent-chat-adapter.js +43 -6
  252. package/dist/client/agent-chat-adapter.js.map +1 -1
  253. package/dist/client/analytics.d.ts +18 -0
  254. package/dist/client/analytics.d.ts.map +1 -1
  255. package/dist/client/analytics.js +177 -0
  256. package/dist/client/analytics.js.map +1 -1
  257. package/dist/client/changelog/Changelog.d.ts +47 -0
  258. package/dist/client/changelog/Changelog.d.ts.map +1 -0
  259. package/dist/client/changelog/Changelog.js +138 -0
  260. package/dist/client/changelog/Changelog.js.map +1 -0
  261. package/dist/client/client-surface.d.ts +17 -0
  262. package/dist/client/client-surface.d.ts.map +1 -0
  263. package/dist/client/client-surface.js +24 -0
  264. package/dist/client/client-surface.js.map +1 -0
  265. package/dist/client/composer/TiptapComposer.d.ts +26 -1
  266. package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
  267. package/dist/client/composer/TiptapComposer.js +27 -5
  268. package/dist/client/composer/TiptapComposer.js.map +1 -1
  269. package/dist/client/feedback-context.d.ts +3 -0
  270. package/dist/client/feedback-context.d.ts.map +1 -1
  271. package/dist/client/feedback-context.js +2 -0
  272. package/dist/client/feedback-context.js.map +1 -1
  273. package/dist/client/index.d.ts +3 -1
  274. package/dist/client/index.d.ts.map +1 -1
  275. package/dist/client/index.js +3 -1
  276. package/dist/client/index.js.map +1 -1
  277. package/dist/client/mcp-apps/McpAppRenderer.d.ts +1 -0
  278. package/dist/client/mcp-apps/McpAppRenderer.d.ts.map +1 -1
  279. package/dist/client/mcp-apps/McpAppRenderer.js +42 -1
  280. package/dist/client/mcp-apps/McpAppRenderer.js.map +1 -1
  281. package/dist/client/settings/useBuilderStatus.d.ts +2 -0
  282. package/dist/client/settings/useBuilderStatus.d.ts.map +1 -1
  283. package/dist/client/settings/useBuilderStatus.js +58 -10
  284. package/dist/client/settings/useBuilderStatus.js.map +1 -1
  285. package/dist/client/sse-event-processor.d.ts.map +1 -1
  286. package/dist/client/sse-event-processor.js +7 -1
  287. package/dist/client/sse-event-processor.js.map +1 -1
  288. package/dist/mcp/build-server.d.ts +21 -0
  289. package/dist/mcp/build-server.d.ts.map +1 -1
  290. package/dist/mcp/build-server.js +43 -1
  291. package/dist/mcp/build-server.js.map +1 -1
  292. package/dist/mcp/embed-app.d.ts.map +1 -1
  293. package/dist/mcp/embed-app.js +197 -7
  294. package/dist/mcp/embed-app.js.map +1 -1
  295. package/dist/mcp/oauth-route.d.ts.map +1 -1
  296. package/dist/mcp/oauth-route.js +95 -12
  297. package/dist/mcp/oauth-route.js.map +1 -1
  298. package/dist/server/attribution.d.ts +83 -0
  299. package/dist/server/attribution.d.ts.map +1 -0
  300. package/dist/server/attribution.js +178 -0
  301. package/dist/server/attribution.js.map +1 -0
  302. package/dist/server/auth.d.ts.map +1 -1
  303. package/dist/server/auth.js +7 -0
  304. package/dist/server/auth.js.map +1 -1
  305. package/dist/server/better-auth-instance.d.ts +9 -1
  306. package/dist/server/better-auth-instance.d.ts.map +1 -1
  307. package/dist/server/better-auth-instance.js +32 -2
  308. package/dist/server/better-auth-instance.js.map +1 -1
  309. package/dist/server/builder-browser.d.ts +4 -0
  310. package/dist/server/builder-browser.d.ts.map +1 -1
  311. package/dist/server/builder-browser.js +20 -1
  312. package/dist/server/builder-browser.js.map +1 -1
  313. package/dist/server/core-routes-plugin.d.ts +25 -0
  314. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  315. package/dist/server/core-routes-plugin.js +101 -0
  316. package/dist/server/core-routes-plugin.js.map +1 -1
  317. package/dist/server/google-oauth.d.ts.map +1 -1
  318. package/dist/server/google-oauth.js +16 -2
  319. package/dist/server/google-oauth.js.map +1 -1
  320. package/dist/server/onboarding-html.d.ts.map +1 -1
  321. package/dist/server/onboarding-html.js +35 -0
  322. package/dist/server/onboarding-html.js.map +1 -1
  323. package/dist/shared/mcp-embed-headers.js +1 -1
  324. package/dist/shared/mcp-embed-headers.js.map +1 -1
  325. package/dist/styles/agent-conversation.css +21 -0
  326. package/dist/templates/workspace-core/.agents/skills/changelog/SKILL.md +111 -0
  327. package/dist/templates/workspace-core/.agents/skills/reliable-mutations/SKILL.md +72 -0
  328. package/dist/templates/workspace-core/.agents/skills/tracking/SKILL.md +46 -14
  329. package/docs/design/durable-agent-runs.md +217 -0
  330. package/package.json +1 -1
  331. package/src/templates/workspace-core/.agents/skills/changelog/SKILL.md +111 -0
  332. package/src/templates/workspace-core/.agents/skills/reliable-mutations/SKILL.md +72 -0
  333. package/src/templates/workspace-core/.agents/skills/tracking/SKILL.md +46 -14
  334. package/corpus/templates/analytics/app/pages/About.tsx +0 -108
  335. package/corpus/templates/analytics/app/routes/about.tsx +0 -9
  336. /package/corpus/templates/analytics/app/routes/{dashboards.tsx → dashboards._index.tsx} +0 -0
@@ -35,6 +35,8 @@ export default defineAction({
35
35
  id: r.id,
36
36
  submittedAt: r.submittedAt,
37
37
  submitterEmail: r.submitterEmail ?? null,
38
+ pageUrl: r.pageUrl ?? null,
39
+ clientSurface: r.clientSurface ?? null,
38
40
  ...JSON.parse(r.data),
39
41
  }));
40
42
  fs.writeFileSync(outputPath, JSON.stringify(data, null, 2));
@@ -43,6 +45,8 @@ export default defineAction({
43
45
  "ID",
44
46
  "Submitted At",
45
47
  "Submitter Email",
48
+ "Page URL",
49
+ "Source",
46
50
  ...fields.map((f: any) => f.label),
47
51
  ];
48
52
  const rows = responses.map((r) => {
@@ -51,6 +55,8 @@ export default defineAction({
51
55
  r.id,
52
56
  r.submittedAt,
53
57
  r.submitterEmail ?? "",
58
+ r.pageUrl ?? "",
59
+ r.clientSurface ?? "",
54
60
  ...fields.map((f: any) => {
55
61
  const val = data[f.id];
56
62
  if (Array.isArray(val)) return val.join("; ");
@@ -49,6 +49,8 @@ export default defineAction({
49
49
  data: JSON.parse(r.data),
50
50
  submittedAt: r.submittedAt,
51
51
  submitterEmail: r.submitterEmail ?? null,
52
+ pageUrl: r.pageUrl ?? null,
53
+ clientSurface: r.clientSurface ?? null,
52
54
  })) as FormResponse[],
53
55
  total: (total as any)?.count ?? 0,
54
56
  fields: JSON.parse(form.fields),
@@ -318,6 +318,8 @@ export default defineAction({
318
318
  submittedAt: schema.responses.submittedAt,
319
319
  ip: schema.responses.ip,
320
320
  submitterEmail: schema.responses.submitterEmail,
321
+ pageUrl: schema.responses.pageUrl,
322
+ clientSurface: schema.responses.clientSurface,
321
323
  })
322
324
  .from(schema.responses)
323
325
  .where(responseFilter)
@@ -29,6 +29,39 @@ function valueAsString(val: unknown): string {
29
29
  return String(val);
30
30
  }
31
31
 
32
+ /** Drop the protocol for a cleaner table cell; the full URL stays the link href. */
33
+ function formatPageUrl(url: string): string {
34
+ return url.replace(/^https?:\/\//, "");
35
+ }
36
+
37
+ /**
38
+ * Only http(s) URLs are safe to use as an anchor href. Page URLs arrive from
39
+ * client `_meta` and could be spoofed by a direct POST, so reject other schemes
40
+ * (e.g. `javascript:`) to avoid a self-XSS when the owner clicks the cell.
41
+ */
42
+ function safeHttpUrl(value: string): string | null {
43
+ try {
44
+ const u = new URL(value);
45
+ return u.protocol === "http:" || u.protocol === "https:" ? u.href : null;
46
+ } catch {
47
+ return null;
48
+ }
49
+ }
50
+
51
+ /** Friendly label for the client-surface token forwarded by feedback embeds. */
52
+ function formatClientSurface(surface: string): string {
53
+ switch (surface) {
54
+ case "electron":
55
+ return "Desktop (Electron)";
56
+ case "tauri":
57
+ return "Desktop (Tauri)";
58
+ case "web":
59
+ return "Web";
60
+ default:
61
+ return surface;
62
+ }
63
+ }
64
+
32
65
  function compareValues(a: unknown, b: unknown): number {
33
66
  // Empty values sort last regardless of direction.
34
67
  const aEmpty = a === undefined || a === null || a === "";
@@ -66,8 +99,21 @@ export function ResponsesPage() {
66
99
  () => responses.some((r: any) => valueAsString(r.submitterEmail).trim()),
67
100
  [responses],
68
101
  );
102
+ const hasPageUrl = useMemo(
103
+ () => responses.some((r: any) => valueAsString(r.pageUrl).trim()),
104
+ [responses],
105
+ );
106
+ const hasClientSurface = useMemo(
107
+ () => responses.some((r: any) => valueAsString(r.clientSurface).trim()),
108
+ [responses],
109
+ );
69
110
  const responseTableMinWidth =
70
- 64 + 160 + (hasSubmitterEmail ? 224 : 0) + Math.max(fields.length, 1) * 320;
111
+ 64 +
112
+ 160 +
113
+ (hasSubmitterEmail ? 224 : 0) +
114
+ (hasPageUrl ? 256 : 0) +
115
+ (hasClientSurface ? 160 : 0) +
116
+ Math.max(fields.length, 1) * 320;
71
117
 
72
118
  function toggleSort(key: SortKey) {
73
119
  if (sortKey === key) {
@@ -86,6 +132,16 @@ export function ResponsesPage() {
86
132
  if (valueAsString(r.submitterEmail).toLowerCase().includes(q)) {
87
133
  return true;
88
134
  }
135
+ if (valueAsString(r.pageUrl).toLowerCase().includes(q)) {
136
+ return true;
137
+ }
138
+ if (
139
+ formatClientSurface(valueAsString(r.clientSurface))
140
+ .toLowerCase()
141
+ .includes(q)
142
+ ) {
143
+ return true;
144
+ }
89
145
  for (const f of fields) {
90
146
  if (valueAsString(r.data[f.id]).toLowerCase().includes(q))
91
147
  return true;
@@ -100,6 +156,10 @@ export function ResponsesPage() {
100
156
  new Date(a.submittedAt).getTime() - new Date(b.submittedAt).getTime();
101
157
  } else if (sortKey === "_email") {
102
158
  cmp = compareValues(a.submitterEmail, b.submitterEmail);
159
+ } else if (sortKey === "_page") {
160
+ cmp = compareValues(a.pageUrl, b.pageUrl);
161
+ } else if (sortKey === "_source") {
162
+ cmp = compareValues(a.clientSurface, b.clientSurface);
103
163
  } else {
104
164
  cmp = compareValues(a.data[sortKey], b.data[sortKey]);
105
165
  }
@@ -113,11 +173,21 @@ export function ResponsesPage() {
113
173
  const headers = [
114
174
  "Submitted At",
115
175
  ...(hasSubmitterEmail ? ["Submitter Email"] : []),
176
+ ...(hasPageUrl ? ["Page URL"] : []),
177
+ ...(hasClientSurface ? ["Source"] : []),
116
178
  ...fields.map((f) => f.label),
117
179
  ];
118
180
  const rows = filteredSorted.map((r) => [
119
181
  r.submittedAt,
120
182
  ...(hasSubmitterEmail ? [valueAsString(r.submitterEmail)] : []),
183
+ ...(hasPageUrl ? [valueAsString(r.pageUrl)] : []),
184
+ ...(hasClientSurface
185
+ ? [
186
+ valueAsString(r.clientSurface)
187
+ ? formatClientSurface(valueAsString(r.clientSurface))
188
+ : "",
189
+ ]
190
+ : []),
121
191
  ...fields.map((f) => valueAsString(r.data[f.id])),
122
192
  ]);
123
193
 
@@ -282,6 +352,8 @@ export function ResponsesPage() {
282
352
  <col className="w-16" />
283
353
  <col className="w-40" />
284
354
  {hasSubmitterEmail ? <col className="w-56" /> : null}
355
+ {hasPageUrl ? <col className="w-64" /> : null}
356
+ {hasClientSurface ? <col className="w-40" /> : null}
285
357
  {fields.map((f) => (
286
358
  <col key={f.id} className="w-80" />
287
359
  ))}
@@ -308,6 +380,22 @@ export function ResponsesPage() {
308
380
  onClick={() => toggleSort("_email")}
309
381
  />
310
382
  ) : null}
383
+ {hasPageUrl ? (
384
+ <SortableHeader
385
+ label="Page"
386
+ active={sortKey === "_page"}
387
+ dir={sortDir}
388
+ onClick={() => toggleSort("_page")}
389
+ />
390
+ ) : null}
391
+ {hasClientSurface ? (
392
+ <SortableHeader
393
+ label="Source"
394
+ active={sortKey === "_source"}
395
+ dir={sortDir}
396
+ onClick={() => toggleSort("_source")}
397
+ />
398
+ ) : null}
311
399
  {fields.map((f) => (
312
400
  <SortableHeader
313
401
  key={f.id}
@@ -336,6 +424,55 @@ export function ResponsesPage() {
336
424
  {valueAsString(response.submitterEmail) || "-"}
337
425
  </td>
338
426
  ) : null}
427
+ {hasPageUrl ? (
428
+ <td className="w-64 px-4 py-3 align-top text-xs whitespace-normal break-words">
429
+ {(() => {
430
+ const raw = valueAsString(response.pageUrl);
431
+ if (!raw)
432
+ return (
433
+ <span className="text-muted-foreground">-</span>
434
+ );
435
+ const safe = safeHttpUrl(raw);
436
+ return safe ? (
437
+ <a
438
+ href={safe}
439
+ target="_blank"
440
+ rel="noopener noreferrer nofollow"
441
+ title={raw}
442
+ className="text-primary hover:underline"
443
+ >
444
+ {formatPageUrl(raw)}
445
+ </a>
446
+ ) : (
447
+ <span title={raw} className="text-muted-foreground">
448
+ {formatPageUrl(raw)}
449
+ </span>
450
+ );
451
+ })()}
452
+ </td>
453
+ ) : null}
454
+ {hasClientSurface ? (
455
+ <td className="w-40 px-4 py-3 align-top text-xs whitespace-nowrap">
456
+ {(() => {
457
+ const surface = valueAsString(response.clientSurface);
458
+ if (!surface)
459
+ return (
460
+ <span className="text-muted-foreground">-</span>
461
+ );
462
+ const label = formatClientSurface(surface);
463
+ // Make desktop submissions pop; web stays muted text.
464
+ return surface === "web" ? (
465
+ <span className="text-muted-foreground">
466
+ {label}
467
+ </span>
468
+ ) : (
469
+ <Badge variant="secondary" className="font-normal">
470
+ {label}
471
+ </Badge>
472
+ );
473
+ })()}
474
+ </td>
475
+ ) : null}
339
476
  {fields.map((f) => {
340
477
  const val = response.data[f.id];
341
478
  const display =
@@ -27,6 +27,7 @@ import {
27
27
  setClientAppState,
28
28
  } from "@agent-native/core/client";
29
29
  import type { LinksFunction } from "react-router";
30
+ import changelog from "../CHANGELOG.md?raw";
30
31
  import stylesheet from "./global.css?url";
31
32
  import { TAB_ID } from "@/lib/tab-id";
32
33
 
@@ -198,7 +199,12 @@ export default function Root() {
198
199
  <NavigationStateSync />
199
200
  <UrlStateSync />
200
201
  <OpenLinkInterceptor />
201
- <CommandMenu open={cmdkOpen} onOpenChange={setCmdkOpen}>
202
+ <CommandMenu
203
+ open={cmdkOpen}
204
+ onOpenChange={setCmdkOpen}
205
+ changelog={changelog}
206
+ changelogKey="forms"
207
+ >
202
208
  <CommandMenu.Group heading="Forms">
203
209
  <CommandMenu.Item onSelect={() => {}}>Search forms</CommandMenu.Item>
204
210
  </CommandMenu.Group>
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: added
3
+ date: 2026-06-23
4
+ ---
5
+
6
+ Response tables now show the page each submission came from, passed through from embedded feedback widgets
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: added
3
+ date: 2026-06-23
4
+ ---
5
+
6
+ Response tables now show whether feedback came from the web, the Electron desktop app, or a Tauri app
@@ -32,6 +32,13 @@ export const responses = table("responses", {
32
32
  submittedAt: text("submitted_at").notNull(),
33
33
  ip: text("ip"),
34
34
  submitterEmail: text("submitter_email"),
35
+ // URL of the page the respondent was on, forwarded by trusted embeds (e.g.
36
+ // the framework FeedbackButton) as a hidden pass-through field. Client-scrubbed
37
+ // of sensitive query params. NULL for direct fills that send no page context.
38
+ pageUrl: text("page_url"),
39
+ // Runtime shell the feedback was sent from: "web", "electron", or "tauri".
40
+ // Hidden pass-through field forwarded by trusted embeds. NULL when unknown.
41
+ clientSurface: text("client_surface"),
35
42
  });
36
43
 
37
44
  export const formShares = createSharesTable("form_shares");
@@ -49,6 +49,15 @@ function cleanSubmitterEmail(value: unknown): string | null {
49
49
  return trimmed;
50
50
  }
51
51
 
52
+ // Allowlist the client-surface hint so only known values are stored. Anything
53
+ // else (including spoofed direct POSTs) is dropped to NULL.
54
+ const KNOWN_CLIENT_SURFACES = new Set(["web", "electron", "tauri"]);
55
+ function cleanClientSurface(value: unknown): string | null {
56
+ if (typeof value !== "string") return null;
57
+ const normalized = value.trim().toLowerCase();
58
+ return KNOWN_CLIENT_SURFACES.has(normalized) ? normalized : null;
59
+ }
60
+
52
61
  function cleanChatSessionIds(value: unknown): string[] {
53
62
  const ids: string[] = [];
54
63
  const visit = (item: unknown) => {
@@ -219,6 +228,7 @@ export const submitForm = defineEventHandler(async (event: H3Event) => {
219
228
  chatSessionIds?: unknown;
220
229
  activeRunId?: unknown;
221
230
  pageUrl?: unknown;
231
+ clientSurface?: unknown;
222
232
  })
223
233
  : null;
224
234
  const session = await getSession(event).catch(() => null);
@@ -231,6 +241,7 @@ export const submitForm = defineEventHandler(async (event: H3Event) => {
231
241
  ]);
232
242
  const activeRunId = cleanMetaText(meta?.activeRunId);
233
243
  const pageUrl = cleanMetaText(meta?.pageUrl);
244
+ const clientSurface = cleanClientSurface(meta?.clientSurface);
234
245
 
235
246
  await db.insert(schema.responses).values({
236
247
  id: responseId,
@@ -239,6 +250,8 @@ export const submitForm = defineEventHandler(async (event: H3Event) => {
239
250
  submittedAt: now,
240
251
  ip,
241
252
  submitterEmail,
253
+ pageUrl,
254
+ clientSurface,
242
255
  });
243
256
 
244
257
  // Write submission notification to application state (SQL-backed)
@@ -257,8 +270,9 @@ export const submitForm = defineEventHandler(async (event: H3Event) => {
257
270
  // Fire integrations best-effort and never fail the submission. Keep this
258
271
  // awaited: serverless hosts can freeze fire-and-forget work as soon as the
259
272
  // HTTP response returns, which silently drops Slack/webhook delivery.
260
- // Feedback debug context is intentionally integration-only: it helps triage
261
- // Slack submissions without retaining page URLs or debug breadcrumbs in SQL.
273
+ // pageUrl and clientSurface are persisted on the response (above) so owners
274
+ // can see which screen and which app feedback came from; chat session ids and
275
+ // run ids remain integration-only debug breadcrumbs we don't retain in SQL.
262
276
  try {
263
277
  const integrations: FormIntegration[] = settings.integrations ?? [];
264
278
  if (integrations.length > 0) {
@@ -273,6 +287,7 @@ export const submitForm = defineEventHandler(async (event: H3Event) => {
273
287
  chatSessionIds,
274
288
  activeRunId,
275
289
  pageUrl,
290
+ clientSurface,
276
291
  });
277
292
  }
278
293
  } catch {
@@ -326,6 +341,8 @@ export const listResponses = defineEventHandler(async (event: H3Event) => {
326
341
  data: JSON.parse(r.data),
327
342
  submittedAt: r.submittedAt,
328
343
  submitterEmail: r.submitterEmail,
344
+ pageUrl: r.pageUrl ?? null,
345
+ clientSurface: r.clientSurface ?? null,
329
346
  })) as FormResponse[],
330
347
  total: total?.count ?? 0,
331
348
  fields: JSON.parse(access.resource.fields),
@@ -48,6 +48,64 @@ interface SubmissionPayload {
48
48
  activeRunId?: string | null;
49
49
  /** Page URL where the feedback was submitted, when available. */
50
50
  pageUrl?: string | null;
51
+ /** Client surface (web/electron/tauri) the feedback came from, when known. */
52
+ clientSurface?: string | null;
53
+ }
54
+
55
+ /** Human-readable label for a client-surface token. */
56
+ function clientSurfaceLabel(surface: string): string {
57
+ switch (surface) {
58
+ case "electron":
59
+ return "Desktop (Electron)";
60
+ case "tauri":
61
+ return "Desktop (Tauri)";
62
+ case "web":
63
+ return "Web";
64
+ default:
65
+ return surface;
66
+ }
67
+ }
68
+
69
+ /**
70
+ * Friendly app name derived from a feedback page URL, so a reviewer can tell at
71
+ * a glance which app the feedback came from. `plan.agent-native.com` → "Plan",
72
+ * `analytics.agent-native.com` → "Analytics". Returns null when the host isn't a
73
+ * recognizable per-app subdomain (the full URL still carries the page).
74
+ */
75
+ function appLabelFromUrl(pageUrl: string): string | null {
76
+ try {
77
+ const { hostname } = new URL(pageUrl);
78
+ const match = hostname.match(/^([a-z0-9-]+)\.agent-native\.com$/i);
79
+ const sub = match?.[1];
80
+ if (!sub || sub === "www") return null;
81
+ return sub
82
+ .split("-")
83
+ .map((part) => (part ? part[0].toUpperCase() + part.slice(1) : part))
84
+ .join(" ");
85
+ } catch {
86
+ return null;
87
+ }
88
+ }
89
+
90
+ /**
91
+ * Readable host+path label for a feedback page URL, used as the visible text of
92
+ * the Slack link so the app/page is legible inline instead of hidden behind a
93
+ * bare "open". The full (already client-scrubbed) URL stays the link target.
94
+ */
95
+ function pageLabelFromUrl(pageUrl: string): string {
96
+ let label = pageUrl;
97
+ try {
98
+ const url = new URL(pageUrl);
99
+ label = `${url.hostname}${url.pathname}`.replace(/\/$/, "") || url.hostname;
100
+ } catch {
101
+ // fall back to the raw string below
102
+ }
103
+ if (label.length > 80) label = `${label.slice(0, 79)}…`;
104
+ // Escape Slack mrkdwn link-text control characters.
105
+ return label
106
+ .replace(/&/g, "&amp;")
107
+ .replace(/</g, "&lt;")
108
+ .replace(/>/g, "&gt;");
51
109
  }
52
110
 
53
111
  // ---------------------------------------------------------------------------
@@ -82,13 +140,20 @@ function formatDebugContext(submission: SubmissionPayload): string[] {
82
140
  lines.push(`Run: \`${submission.activeRunId}\``);
83
141
  }
84
142
  if (submission.pageUrl) {
85
- lines.push(`Page: <${submission.pageUrl}|open>`);
143
+ const appLabel = appLabelFromUrl(submission.pageUrl);
144
+ if (appLabel) lines.push(`App: ${appLabel}`);
145
+ lines.push(
146
+ `Page: <${submission.pageUrl}|${pageLabelFromUrl(submission.pageUrl)}>`,
147
+ );
148
+ }
149
+ if (submission.clientSurface) {
150
+ lines.push(`Source: ${clientSurfaceLabel(submission.clientSurface)}`);
86
151
  }
87
152
  return lines;
88
153
  }
89
154
 
90
155
  /** Slack Block Kit message */
91
- function buildSlackPayload(submission: SubmissionPayload) {
156
+ export function buildSlackPayload(submission: SubmissionPayload) {
92
157
  const fieldLines = submission.fields
93
158
  .filter((f) => submission.data[f.id] !== undefined)
94
159
  .map((f) => {
@@ -170,6 +235,13 @@ function buildDiscordPayload(submission: SubmissionPayload) {
170
235
  inline: false,
171
236
  });
172
237
  }
238
+ if (submission.clientSurface) {
239
+ discordFields.push({
240
+ name: "Source",
241
+ value: clientSurfaceLabel(submission.clientSurface),
242
+ inline: true,
243
+ });
244
+ }
173
245
 
174
246
  return {
175
247
  embeds: [
@@ -192,6 +264,7 @@ function buildGoogleSheetsPayload(submission: SubmissionPayload) {
192
264
  chatSessionIds: (submission.chatSessionIds ?? []).join(", "),
193
265
  activeRunId: submission.activeRunId ?? "",
194
266
  pageUrl: submission.pageUrl ?? "",
267
+ clientSurface: submission.clientSurface ?? "",
195
268
  ...formatFields(submission.fields, submission.data),
196
269
  };
197
270
  }
@@ -208,6 +281,7 @@ function buildWebhookPayload(submission: SubmissionPayload) {
208
281
  chatSessionIds: submission.chatSessionIds ?? [],
209
282
  activeRunId: submission.activeRunId ?? null,
210
283
  pageUrl: submission.pageUrl ?? null,
284
+ clientSurface: submission.clientSurface ?? null,
211
285
  data: formatFields(submission.fields, submission.data),
212
286
  rawData: submission.data,
213
287
  };
@@ -102,6 +102,26 @@ CREATE TABLE IF NOT EXISTS form_shares (
102
102
  CREATE INDEX IF NOT EXISTS responses_form_id_idx ON responses (form_id, submitted_at);
103
103
  CREATE INDEX IF NOT EXISTS form_shares_resource_idx ON form_shares (resource_id, principal_type, principal_id)`,
104
104
  },
105
+ {
106
+ // Page URL the respondent was on, forwarded by trusted embeds (e.g. the
107
+ // framework FeedbackButton) as a hidden pass-through field so owners can
108
+ // see which screen feedback came from in the responses table.
109
+ version: 11,
110
+ sql: {
111
+ postgres: `ALTER TABLE responses ADD COLUMN IF NOT EXISTS page_url TEXT`,
112
+ sqlite: `ALTER TABLE responses ADD COLUMN IF NOT EXISTS page_url TEXT`,
113
+ },
114
+ },
115
+ {
116
+ // Client surface (web/electron/tauri) the respondent submitted from,
117
+ // forwarded by trusted embeds as a hidden pass-through field so owners can
118
+ // see whether feedback came from a desktop app or the browser.
119
+ version: 12,
120
+ sql: {
121
+ postgres: `ALTER TABLE responses ADD COLUMN IF NOT EXISTS client_surface TEXT`,
122
+ sqlite: `ALTER TABLE responses ADD COLUMN IF NOT EXISTS client_surface TEXT`,
123
+ },
124
+ },
105
125
  ],
106
126
  { table: "forms_migrations" },
107
127
  );
@@ -147,6 +147,18 @@ export interface FormResponse {
147
147
  submittedAt: string;
148
148
  /** Email of the submitter when known (claimed by the client; not verified). */
149
149
  submitterEmail?: string | null;
150
+ /**
151
+ * URL of the page the respondent was on, forwarded by trusted embeds (e.g.
152
+ * the framework FeedbackButton) as a hidden pass-through field. Null when the
153
+ * submission carried no page context (e.g. a direct fill on the public page).
154
+ */
155
+ pageUrl?: string | null;
156
+ /**
157
+ * Runtime shell the feedback was sent from — "web", "electron", or "tauri" —
158
+ * forwarded by trusted embeds as a hidden pass-through field. Null when
159
+ * unknown (e.g. a direct fill on the public page).
160
+ */
161
+ clientSurface?: string | null;
150
162
  }
151
163
 
152
164
  // ---------------------------------------------------------------------------
@@ -0,0 +1,10 @@
1
+ # Changelog
2
+
3
+ All notable user-facing changes to Agent-Native Macros are documented here. Open it any
4
+ time from the command menu (Cmd+K → "What's new").
5
+
6
+ ## 2026-06-23
7
+
8
+ ### Added
9
+
10
+ - See what's new right inside Agent-Native Macros — a changelog now lives in the command menu (Cmd+K).
@@ -17,6 +17,7 @@ import { IconSun, IconMoon } from "@tabler/icons-react";
17
17
  import { TAB_ID } from "@/lib/tab-id";
18
18
  import { AppLayout } from "@/components/layout/AppLayout";
19
19
  import type { LinksFunction } from "react-router";
20
+ import changelog from "../CHANGELOG.md?raw";
20
21
  import stylesheet from "./global.css?url";
21
22
 
22
23
  configureTracking({
@@ -124,7 +125,12 @@ export default function Root() {
124
125
  toaster={<Toaster richColors position="bottom-left" />}
125
126
  >
126
127
  <DbSyncSetup />
127
- <CommandMenu open={cmdkOpen} onOpenChange={setCmdkOpen}>
128
+ <CommandMenu
129
+ open={cmdkOpen}
130
+ onOpenChange={setCmdkOpen}
131
+ changelog={changelog}
132
+ changelogKey="macros"
133
+ >
128
134
  <CommandMenu.Group heading="Actions">
129
135
  <CommandMenu.Item onSelect={() => {}}>Search</CommandMenu.Item>
130
136
  </CommandMenu.Group>
@@ -0,0 +1,10 @@
1
+ # Changelog
2
+
3
+ All notable user-facing changes to Agent-Native Mail are documented here. Open it any
4
+ time from the command menu (Cmd+K → "What's new") or from Settings.
5
+
6
+ ## 2026-06-23
7
+
8
+ ### Added
9
+
10
+ - See what's new right inside Agent-Native Mail — a changelog now lives in the command menu (Cmd+K) and in Settings.
@@ -25,6 +25,7 @@ import { CommandMenu } from "@agent-native/core/client";
25
25
  import { useTheme } from "next-themes";
26
26
  import { useSettings, useUpdateSettings } from "@/hooks/use-emails";
27
27
  import { getResolvedTheme } from "@/lib/theme";
28
+ import changelog from "../../../CHANGELOG.md?raw";
28
29
 
29
30
  interface CommandPaletteProps {
30
31
  open: boolean;
@@ -91,6 +92,8 @@ export function CommandPalette({
91
92
  open={open}
92
93
  onOpenChange={onOpenChange}
93
94
  placeholder="Type a command or ask AI..."
95
+ changelog={changelog}
96
+ changelogKey="mail"
94
97
  >
95
98
  <CommandMenu.Group heading="Actions">
96
99
  <CommandMenu.Item
@@ -6,8 +6,10 @@ import {
6
6
  useActionMutation,
7
7
  useChatModels,
8
8
  useChangeVersions,
9
+ ChangelogSettingsCard,
9
10
  } from "@agent-native/core/client";
10
11
  import { appApiPath } from "@agent-native/core/client";
12
+ import changelog from "../../CHANGELOG.md?raw";
11
13
  import {
12
14
  IconUsers,
13
15
  IconPlus,
@@ -24,6 +26,7 @@ import {
24
26
  IconSignature,
25
27
  IconFilter,
26
28
  IconInfoCircle,
29
+ IconHistory,
27
30
  } from "@tabler/icons-react";
28
31
  import { cn } from "@/lib/utils";
29
32
  import { Button } from "@/components/ui/button";
@@ -1379,9 +1382,31 @@ function SlackIntakeSection() {
1379
1382
  );
1380
1383
  }
1381
1384
 
1385
+ // ─── What's New Section ──────────────────────────────────────────────────────
1386
+
1387
+ function WhatsNewSection() {
1388
+ return (
1389
+ <div className="flex-1 overflow-y-auto p-4 sm:p-8">
1390
+ <div className="mb-6">
1391
+ <h2 className="text-[16px] font-semibold text-foreground">
1392
+ What's new
1393
+ </h2>
1394
+ <p className="mt-0.5 text-[13px] text-muted-foreground">
1395
+ Recent user-facing changes to Agent-Native Mail.
1396
+ </p>
1397
+ </div>
1398
+
1399
+ <div className="max-w-2xl">
1400
+ <ChangelogSettingsCard markdown={changelog} />
1401
+ </div>
1402
+ </div>
1403
+ );
1404
+ }
1405
+
1382
1406
  // ─── Settings Page ────────────────────────────────────────────────────────────
1383
1407
 
1384
1408
  type SettingsSection =
1409
+ | "whats-new"
1385
1410
  | "drafting"
1386
1411
  | "automations"
1387
1412
  | "gmail-filters"
@@ -1395,6 +1420,7 @@ const navItems: {
1395
1420
  label: string;
1396
1421
  icon: React.ComponentType<{ className?: string }>;
1397
1422
  }[] = [
1423
+ { id: "whats-new", label: "What's new", icon: IconHistory },
1398
1424
  { id: "drafting", label: "Drafting", icon: IconSignature },
1399
1425
  { id: "automations", label: "Automations", icon: IconBolt },
1400
1426
  { id: "gmail-filters", label: "Gmail Filters", icon: IconFilter },
@@ -1464,6 +1490,7 @@ export function SettingsPage() {
1464
1490
 
1465
1491
  {/* Right content panel */}
1466
1492
  <div className="flex flex-1 overflow-hidden bg-background">
1493
+ {activeSection === "whats-new" && <WhatsNewSection />}
1467
1494
  {activeSection === "drafting" && <DraftingSection />}
1468
1495
  {activeSection === "automations" && <AutomationsSection />}
1469
1496
  {activeSection === "gmail-filters" && <GmailFiltersSection />}