@agent-native/core 0.98.17 → 0.99.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 (583) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +25 -0
  3. package/corpus/core/docs/content/actions.mdx +1 -1
  4. package/corpus/core/docs/content/authentication.mdx +1 -1
  5. package/corpus/core/docs/content/dispatch.mdx +3 -3
  6. package/corpus/core/docs/content/external-agents.mdx +30 -19
  7. package/corpus/core/docs/content/locales/ar-SA/actions.mdx +1 -1
  8. package/corpus/core/docs/content/locales/ar-SA/authentication.mdx +1 -1
  9. package/corpus/core/docs/content/locales/ar-SA/dispatch.mdx +3 -3
  10. package/corpus/core/docs/content/locales/ar-SA/external-agents.mdx +23 -19
  11. package/corpus/core/docs/content/locales/ar-SA/mcp-clients.mdx +31 -3
  12. package/corpus/core/docs/content/locales/ar-SA/mcp-protocol.mdx +29 -15
  13. package/corpus/core/docs/content/locales/ar-SA/plan-plugin.mdx +3 -3
  14. package/corpus/core/docs/content/locales/ar-SA/server.mdx +2 -2
  15. package/corpus/core/docs/content/locales/ar-SA/skills-guide.mdx +1 -1
  16. package/corpus/core/docs/content/locales/ar-SA/template-assets.mdx +1 -1
  17. package/corpus/core/docs/content/locales/ar-SA/template-dispatch.mdx +2 -2
  18. package/corpus/core/docs/content/locales/ar-SA/template-plan.mdx +3 -3
  19. package/corpus/core/docs/content/locales/de-DE/actions.mdx +1 -1
  20. package/corpus/core/docs/content/locales/de-DE/authentication.mdx +1 -1
  21. package/corpus/core/docs/content/locales/de-DE/dispatch.mdx +3 -3
  22. package/corpus/core/docs/content/locales/de-DE/external-agents.mdx +23 -19
  23. package/corpus/core/docs/content/locales/de-DE/mcp-clients.mdx +37 -3
  24. package/corpus/core/docs/content/locales/de-DE/mcp-protocol.mdx +29 -15
  25. package/corpus/core/docs/content/locales/de-DE/plan-plugin.mdx +3 -3
  26. package/corpus/core/docs/content/locales/de-DE/server.mdx +2 -2
  27. package/corpus/core/docs/content/locales/de-DE/skills-guide.mdx +1 -1
  28. package/corpus/core/docs/content/locales/de-DE/template-assets.mdx +1 -1
  29. package/corpus/core/docs/content/locales/de-DE/template-dispatch.mdx +2 -2
  30. package/corpus/core/docs/content/locales/de-DE/template-plan.mdx +3 -3
  31. package/corpus/core/docs/content/locales/es-ES/actions.mdx +1 -1
  32. package/corpus/core/docs/content/locales/es-ES/authentication.mdx +1 -1
  33. package/corpus/core/docs/content/locales/es-ES/dispatch.mdx +3 -3
  34. package/corpus/core/docs/content/locales/es-ES/external-agents.mdx +32 -19
  35. package/corpus/core/docs/content/locales/es-ES/mcp-clients.mdx +17 -3
  36. package/corpus/core/docs/content/locales/es-ES/mcp-protocol.mdx +38 -15
  37. package/corpus/core/docs/content/locales/es-ES/plan-plugin.mdx +3 -3
  38. package/corpus/core/docs/content/locales/es-ES/server.mdx +2 -2
  39. package/corpus/core/docs/content/locales/es-ES/skills-guide.mdx +1 -1
  40. package/corpus/core/docs/content/locales/es-ES/template-assets.mdx +1 -1
  41. package/corpus/core/docs/content/locales/es-ES/template-dispatch.mdx +2 -2
  42. package/corpus/core/docs/content/locales/es-ES/template-plan.mdx +3 -3
  43. package/corpus/core/docs/content/locales/fr-FR/actions.mdx +1 -1
  44. package/corpus/core/docs/content/locales/fr-FR/authentication.mdx +1 -1
  45. package/corpus/core/docs/content/locales/fr-FR/dispatch.mdx +3 -3
  46. package/corpus/core/docs/content/locales/fr-FR/external-agents.mdx +23 -19
  47. package/corpus/core/docs/content/locales/fr-FR/mcp-clients.mdx +17 -3
  48. package/corpus/core/docs/content/locales/fr-FR/mcp-protocol.mdx +29 -15
  49. package/corpus/core/docs/content/locales/fr-FR/plan-plugin.mdx +3 -3
  50. package/corpus/core/docs/content/locales/fr-FR/server.mdx +2 -2
  51. package/corpus/core/docs/content/locales/fr-FR/skills-guide.mdx +1 -1
  52. package/corpus/core/docs/content/locales/fr-FR/template-assets.mdx +1 -1
  53. package/corpus/core/docs/content/locales/fr-FR/template-dispatch.mdx +2 -2
  54. package/corpus/core/docs/content/locales/fr-FR/template-plan.mdx +3 -3
  55. package/corpus/core/docs/content/locales/hi-IN/actions.mdx +1 -1
  56. package/corpus/core/docs/content/locales/hi-IN/authentication.mdx +1 -1
  57. package/corpus/core/docs/content/locales/hi-IN/dispatch.mdx +3 -3
  58. package/corpus/core/docs/content/locales/hi-IN/external-agents.mdx +31 -19
  59. package/corpus/core/docs/content/locales/hi-IN/mcp-clients.mdx +17 -3
  60. package/corpus/core/docs/content/locales/hi-IN/mcp-protocol.mdx +36 -15
  61. package/corpus/core/docs/content/locales/hi-IN/plan-plugin.mdx +3 -3
  62. package/corpus/core/docs/content/locales/hi-IN/server.mdx +2 -2
  63. package/corpus/core/docs/content/locales/hi-IN/skills-guide.mdx +1 -1
  64. package/corpus/core/docs/content/locales/hi-IN/template-assets.mdx +1 -1
  65. package/corpus/core/docs/content/locales/hi-IN/template-dispatch.mdx +2 -2
  66. package/corpus/core/docs/content/locales/hi-IN/template-plan.mdx +3 -3
  67. package/corpus/core/docs/content/locales/ja-JP/actions.mdx +1 -1
  68. package/corpus/core/docs/content/locales/ja-JP/authentication.mdx +1 -1
  69. package/corpus/core/docs/content/locales/ja-JP/dispatch.mdx +3 -3
  70. package/corpus/core/docs/content/locales/ja-JP/external-agents.mdx +23 -19
  71. package/corpus/core/docs/content/locales/ja-JP/mcp-clients.mdx +34 -3
  72. package/corpus/core/docs/content/locales/ja-JP/mcp-protocol.mdx +29 -15
  73. package/corpus/core/docs/content/locales/ja-JP/plan-plugin.mdx +3 -3
  74. package/corpus/core/docs/content/locales/ja-JP/server.mdx +2 -2
  75. package/corpus/core/docs/content/locales/ja-JP/skills-guide.mdx +1 -1
  76. package/corpus/core/docs/content/locales/ja-JP/template-assets.mdx +1 -1
  77. package/corpus/core/docs/content/locales/ja-JP/template-dispatch.mdx +2 -2
  78. package/corpus/core/docs/content/locales/ja-JP/template-plan.mdx +3 -3
  79. package/corpus/core/docs/content/locales/ko-KR/actions.mdx +1 -1
  80. package/corpus/core/docs/content/locales/ko-KR/authentication.mdx +1 -1
  81. package/corpus/core/docs/content/locales/ko-KR/dispatch.mdx +3 -3
  82. package/corpus/core/docs/content/locales/ko-KR/external-agents.mdx +30 -19
  83. package/corpus/core/docs/content/locales/ko-KR/mcp-clients.mdx +33 -3
  84. package/corpus/core/docs/content/locales/ko-KR/mcp-protocol.mdx +36 -15
  85. package/corpus/core/docs/content/locales/ko-KR/plan-plugin.mdx +3 -3
  86. package/corpus/core/docs/content/locales/ko-KR/server.mdx +2 -2
  87. package/corpus/core/docs/content/locales/ko-KR/skills-guide.mdx +1 -1
  88. package/corpus/core/docs/content/locales/ko-KR/template-assets.mdx +1 -1
  89. package/corpus/core/docs/content/locales/ko-KR/template-dispatch.mdx +2 -2
  90. package/corpus/core/docs/content/locales/ko-KR/template-plan.mdx +3 -3
  91. package/corpus/core/docs/content/locales/pt-BR/actions.mdx +1 -1
  92. package/corpus/core/docs/content/locales/pt-BR/authentication.mdx +1 -1
  93. package/corpus/core/docs/content/locales/pt-BR/dispatch.mdx +3 -3
  94. package/corpus/core/docs/content/locales/pt-BR/external-agents.mdx +32 -19
  95. package/corpus/core/docs/content/locales/pt-BR/mcp-clients.mdx +34 -3
  96. package/corpus/core/docs/content/locales/pt-BR/mcp-protocol.mdx +32 -15
  97. package/corpus/core/docs/content/locales/pt-BR/plan-plugin.mdx +3 -3
  98. package/corpus/core/docs/content/locales/pt-BR/server.mdx +2 -2
  99. package/corpus/core/docs/content/locales/pt-BR/skills-guide.mdx +1 -1
  100. package/corpus/core/docs/content/locales/pt-BR/template-assets.mdx +1 -1
  101. package/corpus/core/docs/content/locales/pt-BR/template-dispatch.mdx +2 -2
  102. package/corpus/core/docs/content/locales/pt-BR/template-plan.mdx +3 -3
  103. package/corpus/core/docs/content/locales/zh-CN/actions.mdx +1 -1
  104. package/corpus/core/docs/content/locales/zh-CN/authentication.mdx +1 -1
  105. package/corpus/core/docs/content/locales/zh-CN/dispatch.mdx +3 -3
  106. package/corpus/core/docs/content/locales/zh-CN/external-agents.mdx +23 -19
  107. package/corpus/core/docs/content/locales/zh-CN/mcp-clients.mdx +28 -3
  108. package/corpus/core/docs/content/locales/zh-CN/mcp-protocol.mdx +29 -15
  109. package/corpus/core/docs/content/locales/zh-CN/plan-plugin.mdx +3 -3
  110. package/corpus/core/docs/content/locales/zh-CN/server.mdx +2 -2
  111. package/corpus/core/docs/content/locales/zh-CN/skills-guide.mdx +1 -1
  112. package/corpus/core/docs/content/locales/zh-CN/template-assets.mdx +1 -1
  113. package/corpus/core/docs/content/locales/zh-CN/template-dispatch.mdx +2 -2
  114. package/corpus/core/docs/content/locales/zh-CN/template-plan.mdx +3 -3
  115. package/corpus/core/docs/content/locales/zh-TW/actions.mdx +1 -1
  116. package/corpus/core/docs/content/locales/zh-TW/authentication.mdx +1 -1
  117. package/corpus/core/docs/content/locales/zh-TW/dispatch.mdx +3 -3
  118. package/corpus/core/docs/content/locales/zh-TW/external-agents.mdx +23 -19
  119. package/corpus/core/docs/content/locales/zh-TW/mcp-clients.mdx +28 -3
  120. package/corpus/core/docs/content/locales/zh-TW/mcp-protocol.mdx +30 -15
  121. package/corpus/core/docs/content/locales/zh-TW/plan-plugin.mdx +3 -3
  122. package/corpus/core/docs/content/locales/zh-TW/server.mdx +2 -2
  123. package/corpus/core/docs/content/locales/zh-TW/skills-guide.mdx +1 -1
  124. package/corpus/core/docs/content/locales/zh-TW/template-assets.mdx +1 -1
  125. package/corpus/core/docs/content/locales/zh-TW/template-dispatch.mdx +2 -2
  126. package/corpus/core/docs/content/locales/zh-TW/template-plan.mdx +3 -3
  127. package/corpus/core/docs/content/mcp-clients.mdx +31 -4
  128. package/corpus/core/docs/content/mcp-protocol.mdx +35 -15
  129. package/corpus/core/docs/content/plan-plugin.mdx +3 -3
  130. package/corpus/core/docs/content/server.mdx +2 -2
  131. package/corpus/core/docs/content/skills-guide.mdx +1 -1
  132. package/corpus/core/docs/content/template-assets.mdx +1 -1
  133. package/corpus/core/docs/content/template-dispatch.mdx +2 -2
  134. package/corpus/core/docs/content/template-plan.mdx +3 -3
  135. package/corpus/core/package.json +1 -1
  136. package/corpus/core/src/a2a/client.ts +3 -0
  137. package/corpus/core/src/a2a/handlers.ts +95 -7
  138. package/corpus/core/src/agent/production-agent.ts +21 -0
  139. package/corpus/core/src/cli/connect.ts +50 -24
  140. package/corpus/core/src/cli/mcp-config-writers.ts +10 -0
  141. package/corpus/core/src/cli/mcp.ts +5 -5
  142. package/corpus/core/src/cli/pr-visual-recap-workflow.ts +1 -1
  143. package/corpus/core/src/cli/skills-content/assets-skill.ts +1 -1
  144. package/corpus/core/src/cli/skills-content/design-exploration-skill.ts +1 -1
  145. package/corpus/core/src/cli/skills-content/help.ts +1 -1
  146. package/corpus/core/src/cli/skills-content/plan-setup-auth.ts +1 -1
  147. package/corpus/core/src/cli/skills-content/visual-plan-skill.ts +1 -1
  148. package/corpus/core/src/cli/skills-content/visual-recap-skill.ts +2 -2
  149. package/corpus/core/src/cli/skills.ts +23 -12
  150. package/corpus/core/src/client/analytics.ts +13 -0
  151. package/corpus/core/src/client/resources/McpIntegrationDialog.tsx +5 -0
  152. package/corpus/core/src/client/resources/mcp-integration-catalog.ts +17 -0
  153. package/corpus/core/src/embedding/agent.ts +8 -6
  154. package/corpus/core/src/localization/default-messages.ts +7 -0
  155. package/corpus/core/src/mcp/ask-app-inline-tasks.ts +125 -0
  156. package/corpus/core/src/mcp/build-server.ts +65 -3
  157. package/corpus/core/src/mcp/builtin-tools.ts +186 -22
  158. package/corpus/core/src/mcp/connect-route.ts +17 -14
  159. package/corpus/core/src/mcp/oauth-route.ts +38 -13
  160. package/corpus/core/src/mcp/route-paths.ts +34 -0
  161. package/corpus/core/src/mcp/server.ts +21 -10
  162. package/corpus/core/src/mcp/stdio.ts +34 -16
  163. package/corpus/core/src/mcp/workspace-resolve.ts +2 -1
  164. package/corpus/core/src/mcp-client/manager.ts +5 -3
  165. package/corpus/core/src/mcp-client/remote-store.ts +10 -10
  166. package/corpus/core/src/server/agent-chat/action-filters-a2a.ts +13 -0
  167. package/corpus/core/src/server/agent-chat-plugin.ts +42 -9
  168. package/corpus/core/src/server/agent-teams.ts +17 -0
  169. package/corpus/core/src/server/auth.ts +22 -6
  170. package/corpus/core/src/server/core-routes-plugin.ts +30 -24
  171. package/corpus/core/src/server/csrf.ts +17 -0
  172. package/corpus/core/src/server/framework-request-handler.ts +8 -2
  173. package/corpus/core/src/server/google-oauth.ts +12 -2
  174. package/corpus/core/src/server/http-response-telemetry.ts +6 -1
  175. package/corpus/core/src/server/recap-image-route.ts +3 -3
  176. package/corpus/core/src/server/security-headers.ts +7 -1
  177. package/corpus/core/src/server/ssr-handler.ts +2 -0
  178. package/corpus/core/src/shared/cache-control.ts +6 -4
  179. package/corpus/core/src/templates/default/.agents/skills/security/SKILL.md +10 -0
  180. package/corpus/core/src/templates/workspace-core/.agents/skills/authentication/SKILL.md +15 -6
  181. package/corpus/core/src/templates/workspace-core/.agents/skills/external-agents/SKILL.md +16 -10
  182. package/corpus/core/src/templates/workspace-core/.agents/skills/performance/SKILL.md +20 -1
  183. package/corpus/core/src/templates/workspace-core/.agents/skills/security/SKILL.md +10 -0
  184. package/corpus/templates/analytics/.agents/skills/security/SKILL.md +10 -0
  185. package/corpus/templates/analytics/app/assets/replay-cursor.svg +17 -0
  186. package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +4 -3
  187. package/corpus/templates/analytics/app/global.css +71 -4
  188. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/DashboardFilterBar.tsx +32 -4
  189. package/corpus/templates/analytics/app/pages/monitoring/errors/IssueDetail.tsx +1 -1
  190. package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +49 -11
  191. package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +4 -6
  192. package/corpus/templates/analytics/changelog/2026-07-13-all-time-dashboard-filters-no-longer-send-invalid-dates-to-b.md +6 -0
  193. package/corpus/templates/analytics/changelog/2026-07-13-daily-dashboard-emails-recover-more-reliably-from-serverless.md +6 -0
  194. package/corpus/templates/analytics/changelog/2026-07-13-error-issue-titles-wrap-instead-of-overflowing.md +6 -0
  195. package/corpus/templates/analytics/changelog/2026-07-13-saving-a-new-dashboard-view-no-longer-fails-with-a-server-er.md +6 -0
  196. package/corpus/templates/analytics/changelog/2026-07-13-session-replay-cursors-stay-hidden-until-their-first-recorde.md +6 -0
  197. package/corpus/templates/analytics/changelog/2026-07-13-session-replay-links-now-support-cmd-click-and-middle-click-.md +6 -0
  198. package/corpus/templates/analytics/changelog/2026-07-13-session-replays-keep-a-visible-mac-style-cursor-over-the-vis.md +6 -0
  199. package/corpus/templates/analytics/server/jobs/dashboard-report.ts +21 -2
  200. package/corpus/templates/analytics/server/lib/dashboard-report-subscriptions.ts +57 -1
  201. package/corpus/templates/analytics/server/lib/dashboard-report.ts +266 -28
  202. package/corpus/templates/analytics/server/lib/dashboards-store.ts +43 -4
  203. package/corpus/templates/analytics/server/lib/error-capture.ts +24 -5
  204. package/corpus/templates/analytics/server/plugins/agent-chat.ts +5 -0
  205. package/corpus/templates/analytics/server/routes/[...page].get.ts +0 -2
  206. package/corpus/templates/assets/.agents/skills/security/SKILL.md +10 -0
  207. package/corpus/templates/assets/agent-native.app-skill.json +1 -1
  208. package/corpus/templates/brain/.agents/skills/security/SKILL.md +10 -0
  209. package/corpus/templates/calendar/.agents/skills/security/SKILL.md +10 -0
  210. package/corpus/templates/calendar/AGENTS.md +2 -1
  211. package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +0 -19
  212. package/corpus/templates/calendar/app/lib/attendee-local-time.ts +14 -5
  213. package/corpus/templates/calendar/changelog/2026-07-13-your-local-time-now-follows-your-browser-timezone-and-event-.md +6 -0
  214. package/corpus/templates/chat/.agents/skills/security/SKILL.md +10 -0
  215. package/corpus/templates/clips/.agents/skills/recording/SKILL.md +7 -2
  216. package/corpus/templates/clips/.agents/skills/security/SKILL.md +10 -0
  217. package/corpus/templates/clips/AGENTS.md +7 -0
  218. package/corpus/templates/clips/actions/list-meetings.ts +19 -4
  219. package/corpus/templates/clips/actions/start-meeting-recording.ts +7 -1
  220. package/corpus/templates/clips/app/components/library/recording-card.tsx +27 -26
  221. package/corpus/templates/clips/app/components/library/space-card.tsx +4 -6
  222. package/corpus/templates/clips/app/components/meetings/transcript-bubbles.tsx +75 -78
  223. package/corpus/templates/clips/app/components/recorder/confetti-canvas.tsx +18 -1
  224. package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +6 -12
  225. package/corpus/templates/clips/app/components/recorder/recording-toolbar.tsx +19 -2
  226. package/corpus/templates/clips/app/lib/camera-composite.ts +70 -10
  227. package/corpus/templates/clips/app/routes/_app.meetings.$meetingId.tsx +1 -1
  228. package/corpus/templates/clips/app/routes/record.tsx +5 -25
  229. package/corpus/templates/clips/changelog/2026-07-13-clips-desktop-now-requires-macos-13-for-native-capture.md +6 -0
  230. package/corpus/templates/clips/changelog/2026-07-13-clips-hide-solo-meeting-reminders.md +6 -0
  231. package/corpus/templates/clips/changelog/2026-07-13-clips-now-removes-duplicate-mic-and-system-transcript-turns-.md +6 -0
  232. package/corpus/templates/clips/changelog/2026-07-13-desktop-recordings-run-cooler-and-quieter-live-transcription.md +6 -0
  233. package/corpus/templates/clips/changelog/2026-07-13-meeting-recordings-now-stop-after-a-calendar-meeting-ends-an.md +6 -0
  234. package/corpus/templates/clips/changelog/2026-07-13-meeting-transcripts-now-use-a-clean-agent-chat-style-reading.md +6 -0
  235. package/corpus/templates/clips/changelog/2026-07-13-screen-recordings-use-less-processing-power-during-longer-ca.md +6 -0
  236. package/corpus/templates/clips/chrome-extension/src/background.ts +5 -3
  237. package/corpus/templates/clips/chrome-extension/src/offscreen.ts +17 -14
  238. package/corpus/templates/clips/chrome-extension/src/overlay.ts +8 -2
  239. package/corpus/templates/clips/desktop/README.md +3 -0
  240. package/corpus/templates/clips/desktop/src/hooks/useMeetingTranscription.ts +9 -0
  241. package/corpus/templates/clips/desktop/src/lib/recorder.ts +8 -2
  242. package/corpus/templates/clips/desktop/src/lib/silence-events.ts +1 -0
  243. package/corpus/templates/clips/desktop/src/lib/transcription-capture.ts +5 -0
  244. package/corpus/templates/clips/desktop/src/lib/transcription-engine.ts +134 -5
  245. package/corpus/templates/clips/desktop/src/lib/upload-video-stream.ts +28 -0
  246. package/corpus/templates/clips/desktop/src/overlays/flow-bar.tsx +10 -3
  247. package/corpus/templates/clips/desktop/src/overlays/meeting-notification.tsx +42 -9
  248. package/corpus/templates/clips/desktop/src/overlays/recording-pill.tsx +10 -2
  249. package/corpus/templates/clips/desktop/src-tauri/Cargo.toml +6 -1
  250. package/corpus/templates/clips/desktop/src-tauri/src/notifications.rs +77 -1
  251. package/corpus/templates/clips/desktop/src-tauri/src/silence_detector.rs +104 -25
  252. package/corpus/templates/clips/desktop/src-tauri/src/system_audio.rs +4 -0
  253. package/corpus/templates/clips/desktop/src-tauri/src/whisper_speech.rs +287 -32
  254. package/corpus/templates/clips/desktop/src-tauri/tauri.conf.json +1 -1
  255. package/corpus/templates/clips/server/lib/calendar-event-classification.ts +28 -0
  256. package/corpus/templates/clips/server/routes/[...page].get.ts +1 -59
  257. package/corpus/templates/clips/shared/recording-capture.ts +37 -0
  258. package/corpus/templates/content/.agents/skills/security/SKILL.md +10 -0
  259. package/corpus/templates/content/agent-native.app-skill.json +1 -1
  260. package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +18 -16
  261. package/corpus/templates/content/server/routes/[...page].get.ts +1 -15
  262. package/corpus/templates/design/.agents/skills/security/SKILL.md +10 -0
  263. package/corpus/templates/design/actions/connect-assets-mcp.ts +1 -1
  264. package/corpus/templates/design/agent-native.app-skill.json +1 -1
  265. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +88 -12
  266. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +1 -0
  267. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +4 -1
  268. package/corpus/templates/design/changelog/2026-07-13-adding-shapes-preserves-canvas-background.md +6 -0
  269. package/corpus/templates/design/changelog/2026-07-13-clicking-quickly-between-overlapping-elements-no-longer-bounc.md +6 -0
  270. package/corpus/templates/design/server/plugins/assets-mcp.ts +1 -1
  271. package/corpus/templates/design/server/routes/[...page].get.ts +0 -2
  272. package/corpus/templates/dispatch/.agents/skills/security/SKILL.md +10 -0
  273. package/corpus/templates/forms/.agents/skills/security/SKILL.md +10 -0
  274. package/corpus/templates/macros/.agents/skills/security/SKILL.md +10 -0
  275. package/corpus/templates/mail/.agents/skills/security/SKILL.md +10 -0
  276. package/corpus/templates/plan/.agents/skills/security/SKILL.md +10 -0
  277. package/corpus/templates/plan/.agents/skills/visual-plan/SKILL.md +1 -1
  278. package/corpus/templates/plan/.agents/skills/visual-recap/SKILL.md +2 -2
  279. package/corpus/templates/plan/agent-native.app-skill.json +1 -1
  280. package/corpus/templates/plan/app/lib/plan-local-bridge.ts +6 -1
  281. package/corpus/templates/plan/app/pages/PlansPage.tsx +15 -6
  282. package/corpus/templates/plan/changelog/2026-07-13-fixed-local-plan-previews-continuously-flickering-while-revi.md +6 -0
  283. package/corpus/templates/plan/server/routes/[...page].get.ts +0 -2
  284. package/corpus/templates/slides/.agents/skills/security/SKILL.md +10 -0
  285. package/dist/a2a/client.d.ts +2 -0
  286. package/dist/a2a/client.d.ts.map +1 -1
  287. package/dist/a2a/client.js +2 -0
  288. package/dist/a2a/client.js.map +1 -1
  289. package/dist/a2a/handlers.d.ts.map +1 -1
  290. package/dist/a2a/handlers.js +84 -7
  291. package/dist/a2a/handlers.js.map +1 -1
  292. package/dist/agent/production-agent.d.ts +9 -0
  293. package/dist/agent/production-agent.d.ts.map +1 -1
  294. package/dist/agent/production-agent.js +12 -0
  295. package/dist/agent/production-agent.js.map +1 -1
  296. package/dist/cli/connect.d.ts +2 -2
  297. package/dist/cli/connect.d.ts.map +1 -1
  298. package/dist/cli/connect.js +45 -23
  299. package/dist/cli/connect.js.map +1 -1
  300. package/dist/cli/mcp-config-writers.d.ts.map +1 -1
  301. package/dist/cli/mcp-config-writers.js +7 -0
  302. package/dist/cli/mcp-config-writers.js.map +1 -1
  303. package/dist/cli/mcp.js +5 -3
  304. package/dist/cli/mcp.js.map +1 -1
  305. package/dist/cli/pr-visual-recap-workflow.d.ts +1 -1
  306. package/dist/cli/pr-visual-recap-workflow.d.ts.map +1 -1
  307. package/dist/cli/pr-visual-recap-workflow.js +1 -1
  308. package/dist/cli/pr-visual-recap-workflow.js.map +1 -1
  309. package/dist/cli/skills-content/assets-skill.d.ts +1 -1
  310. package/dist/cli/skills-content/assets-skill.d.ts.map +1 -1
  311. package/dist/cli/skills-content/assets-skill.js +1 -1
  312. package/dist/cli/skills-content/assets-skill.js.map +1 -1
  313. package/dist/cli/skills-content/design-exploration-skill.d.ts +1 -1
  314. package/dist/cli/skills-content/design-exploration-skill.d.ts.map +1 -1
  315. package/dist/cli/skills-content/design-exploration-skill.js +1 -1
  316. package/dist/cli/skills-content/design-exploration-skill.js.map +1 -1
  317. package/dist/cli/skills-content/help.d.ts +1 -1
  318. package/dist/cli/skills-content/help.d.ts.map +1 -1
  319. package/dist/cli/skills-content/help.js +1 -1
  320. package/dist/cli/skills-content/help.js.map +1 -1
  321. package/dist/cli/skills-content/plan-setup-auth.d.ts +1 -1
  322. package/dist/cli/skills-content/plan-setup-auth.d.ts.map +1 -1
  323. package/dist/cli/skills-content/plan-setup-auth.js +1 -1
  324. package/dist/cli/skills-content/plan-setup-auth.js.map +1 -1
  325. package/dist/cli/skills-content/visual-plan-skill.d.ts +1 -1
  326. package/dist/cli/skills-content/visual-plan-skill.d.ts.map +1 -1
  327. package/dist/cli/skills-content/visual-plan-skill.js +1 -1
  328. package/dist/cli/skills-content/visual-plan-skill.js.map +1 -1
  329. package/dist/cli/skills-content/visual-recap-skill.d.ts +1 -1
  330. package/dist/cli/skills-content/visual-recap-skill.d.ts.map +1 -1
  331. package/dist/cli/skills-content/visual-recap-skill.js +2 -2
  332. package/dist/cli/skills-content/visual-recap-skill.js.map +1 -1
  333. package/dist/cli/skills.d.ts.map +1 -1
  334. package/dist/cli/skills.js +16 -12
  335. package/dist/cli/skills.js.map +1 -1
  336. package/dist/client/analytics.d.ts.map +1 -1
  337. package/dist/client/analytics.js +11 -0
  338. package/dist/client/analytics.js.map +1 -1
  339. package/dist/client/resources/McpIntegrationDialog.d.ts.map +1 -1
  340. package/dist/client/resources/McpIntegrationDialog.js +1 -1
  341. package/dist/client/resources/McpIntegrationDialog.js.map +1 -1
  342. package/dist/client/resources/mcp-integration-catalog.d.ts +1 -0
  343. package/dist/client/resources/mcp-integration-catalog.d.ts.map +1 -1
  344. package/dist/client/resources/mcp-integration-catalog.js +14 -0
  345. package/dist/client/resources/mcp-integration-catalog.js.map +1 -1
  346. package/dist/collab/awareness.d.ts +2 -2
  347. package/dist/collab/awareness.d.ts.map +1 -1
  348. package/dist/collab/routes.d.ts +1 -1
  349. package/dist/collab/struct-routes.d.ts +1 -1
  350. package/dist/embedding/agent.d.ts.map +1 -1
  351. package/dist/embedding/agent.js +6 -7
  352. package/dist/embedding/agent.js.map +1 -1
  353. package/dist/localization/default-messages.d.ts +5 -0
  354. package/dist/localization/default-messages.d.ts.map +1 -1
  355. package/dist/localization/default-messages.js +5 -0
  356. package/dist/localization/default-messages.js.map +1 -1
  357. package/dist/mcp/ask-app-inline-tasks.d.ts +47 -0
  358. package/dist/mcp/ask-app-inline-tasks.d.ts.map +1 -0
  359. package/dist/mcp/ask-app-inline-tasks.js +87 -0
  360. package/dist/mcp/ask-app-inline-tasks.js.map +1 -0
  361. package/dist/mcp/build-server.d.ts.map +1 -1
  362. package/dist/mcp/build-server.js +56 -3
  363. package/dist/mcp/build-server.js.map +1 -1
  364. package/dist/mcp/builtin-tools.d.ts.map +1 -1
  365. package/dist/mcp/builtin-tools.js +141 -21
  366. package/dist/mcp/builtin-tools.js.map +1 -1
  367. package/dist/mcp/connect-route.d.ts +12 -10
  368. package/dist/mcp/connect-route.d.ts.map +1 -1
  369. package/dist/mcp/connect-route.js +17 -14
  370. package/dist/mcp/connect-route.js.map +1 -1
  371. package/dist/mcp/oauth-route.d.ts +1 -1
  372. package/dist/mcp/oauth-route.d.ts.map +1 -1
  373. package/dist/mcp/oauth-route.js +30 -13
  374. package/dist/mcp/oauth-route.js.map +1 -1
  375. package/dist/mcp/route-paths.d.ts +13 -0
  376. package/dist/mcp/route-paths.d.ts.map +1 -0
  377. package/dist/mcp/route-paths.js +27 -0
  378. package/dist/mcp/route-paths.js.map +1 -0
  379. package/dist/mcp/server.d.ts +2 -1
  380. package/dist/mcp/server.d.ts.map +1 -1
  381. package/dist/mcp/server.js +14 -6
  382. package/dist/mcp/server.js.map +1 -1
  383. package/dist/mcp/stdio.d.ts +7 -6
  384. package/dist/mcp/stdio.d.ts.map +1 -1
  385. package/dist/mcp/stdio.js +27 -15
  386. package/dist/mcp/stdio.js.map +1 -1
  387. package/dist/mcp/workspace-resolve.d.ts +2 -1
  388. package/dist/mcp/workspace-resolve.d.ts.map +1 -1
  389. package/dist/mcp/workspace-resolve.js +2 -1
  390. package/dist/mcp/workspace-resolve.js.map +1 -1
  391. package/dist/mcp-client/manager.js +6 -3
  392. package/dist/mcp-client/manager.js.map +1 -1
  393. package/dist/mcp-client/remote-store.js +5 -7
  394. package/dist/mcp-client/remote-store.js.map +1 -1
  395. package/dist/notifications/routes.d.ts +3 -3
  396. package/dist/progress/routes.d.ts +1 -1
  397. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  398. package/dist/resources/handlers.d.ts +2 -2
  399. package/dist/server/agent-chat/action-filters-a2a.d.ts.map +1 -1
  400. package/dist/server/agent-chat/action-filters-a2a.js +11 -1
  401. package/dist/server/agent-chat/action-filters-a2a.js.map +1 -1
  402. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  403. package/dist/server/agent-chat-plugin.js +43 -11
  404. package/dist/server/agent-chat-plugin.js.map +1 -1
  405. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  406. package/dist/server/agent-teams.d.ts.map +1 -1
  407. package/dist/server/agent-teams.js +15 -1
  408. package/dist/server/agent-teams.js.map +1 -1
  409. package/dist/server/auth.d.ts.map +1 -1
  410. package/dist/server/auth.js +16 -6
  411. package/dist/server/auth.js.map +1 -1
  412. package/dist/server/core-routes-plugin.d.ts +4 -3
  413. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  414. package/dist/server/core-routes-plugin.js +20 -15
  415. package/dist/server/core-routes-plugin.js.map +1 -1
  416. package/dist/server/csrf.d.ts.map +1 -1
  417. package/dist/server/csrf.js +11 -0
  418. package/dist/server/csrf.js.map +1 -1
  419. package/dist/server/framework-request-handler.d.ts.map +1 -1
  420. package/dist/server/framework-request-handler.js +8 -2
  421. package/dist/server/framework-request-handler.js.map +1 -1
  422. package/dist/server/google-oauth.d.ts +2 -0
  423. package/dist/server/google-oauth.d.ts.map +1 -1
  424. package/dist/server/google-oauth.js +11 -2
  425. package/dist/server/google-oauth.js.map +1 -1
  426. package/dist/server/http-response-telemetry.d.ts.map +1 -1
  427. package/dist/server/http-response-telemetry.js +4 -1
  428. package/dist/server/http-response-telemetry.js.map +1 -1
  429. package/dist/server/recap-image-route.js +3 -3
  430. package/dist/server/recap-image-route.js.map +1 -1
  431. package/dist/server/security-headers.d.ts.map +1 -1
  432. package/dist/server/security-headers.js +4 -1
  433. package/dist/server/security-headers.js.map +1 -1
  434. package/dist/server/ssr-handler.d.ts.map +1 -1
  435. package/dist/server/ssr-handler.js +3 -1
  436. package/dist/server/ssr-handler.js.map +1 -1
  437. package/dist/shared/cache-control.d.ts +7 -6
  438. package/dist/shared/cache-control.d.ts.map +1 -1
  439. package/dist/shared/cache-control.js +4 -3
  440. package/dist/shared/cache-control.js.map +1 -1
  441. package/dist/templates/default/.agents/skills/security/SKILL.md +10 -0
  442. package/dist/templates/workspace-core/.agents/skills/authentication/SKILL.md +15 -6
  443. package/dist/templates/workspace-core/.agents/skills/external-agents/SKILL.md +16 -10
  444. package/dist/templates/workspace-core/.agents/skills/performance/SKILL.md +20 -1
  445. package/dist/templates/workspace-core/.agents/skills/security/SKILL.md +10 -0
  446. package/docs/content/actions.mdx +1 -1
  447. package/docs/content/authentication.mdx +1 -1
  448. package/docs/content/dispatch.mdx +3 -3
  449. package/docs/content/external-agents.mdx +30 -19
  450. package/docs/content/locales/ar-SA/actions.mdx +1 -1
  451. package/docs/content/locales/ar-SA/authentication.mdx +1 -1
  452. package/docs/content/locales/ar-SA/dispatch.mdx +3 -3
  453. package/docs/content/locales/ar-SA/external-agents.mdx +23 -19
  454. package/docs/content/locales/ar-SA/mcp-clients.mdx +31 -3
  455. package/docs/content/locales/ar-SA/mcp-protocol.mdx +29 -15
  456. package/docs/content/locales/ar-SA/plan-plugin.mdx +3 -3
  457. package/docs/content/locales/ar-SA/server.mdx +2 -2
  458. package/docs/content/locales/ar-SA/skills-guide.mdx +1 -1
  459. package/docs/content/locales/ar-SA/template-assets.mdx +1 -1
  460. package/docs/content/locales/ar-SA/template-dispatch.mdx +2 -2
  461. package/docs/content/locales/ar-SA/template-plan.mdx +3 -3
  462. package/docs/content/locales/de-DE/actions.mdx +1 -1
  463. package/docs/content/locales/de-DE/authentication.mdx +1 -1
  464. package/docs/content/locales/de-DE/dispatch.mdx +3 -3
  465. package/docs/content/locales/de-DE/external-agents.mdx +23 -19
  466. package/docs/content/locales/de-DE/mcp-clients.mdx +37 -3
  467. package/docs/content/locales/de-DE/mcp-protocol.mdx +29 -15
  468. package/docs/content/locales/de-DE/plan-plugin.mdx +3 -3
  469. package/docs/content/locales/de-DE/server.mdx +2 -2
  470. package/docs/content/locales/de-DE/skills-guide.mdx +1 -1
  471. package/docs/content/locales/de-DE/template-assets.mdx +1 -1
  472. package/docs/content/locales/de-DE/template-dispatch.mdx +2 -2
  473. package/docs/content/locales/de-DE/template-plan.mdx +3 -3
  474. package/docs/content/locales/es-ES/actions.mdx +1 -1
  475. package/docs/content/locales/es-ES/authentication.mdx +1 -1
  476. package/docs/content/locales/es-ES/dispatch.mdx +3 -3
  477. package/docs/content/locales/es-ES/external-agents.mdx +32 -19
  478. package/docs/content/locales/es-ES/mcp-clients.mdx +17 -3
  479. package/docs/content/locales/es-ES/mcp-protocol.mdx +38 -15
  480. package/docs/content/locales/es-ES/plan-plugin.mdx +3 -3
  481. package/docs/content/locales/es-ES/server.mdx +2 -2
  482. package/docs/content/locales/es-ES/skills-guide.mdx +1 -1
  483. package/docs/content/locales/es-ES/template-assets.mdx +1 -1
  484. package/docs/content/locales/es-ES/template-dispatch.mdx +2 -2
  485. package/docs/content/locales/es-ES/template-plan.mdx +3 -3
  486. package/docs/content/locales/fr-FR/actions.mdx +1 -1
  487. package/docs/content/locales/fr-FR/authentication.mdx +1 -1
  488. package/docs/content/locales/fr-FR/dispatch.mdx +3 -3
  489. package/docs/content/locales/fr-FR/external-agents.mdx +23 -19
  490. package/docs/content/locales/fr-FR/mcp-clients.mdx +17 -3
  491. package/docs/content/locales/fr-FR/mcp-protocol.mdx +29 -15
  492. package/docs/content/locales/fr-FR/plan-plugin.mdx +3 -3
  493. package/docs/content/locales/fr-FR/server.mdx +2 -2
  494. package/docs/content/locales/fr-FR/skills-guide.mdx +1 -1
  495. package/docs/content/locales/fr-FR/template-assets.mdx +1 -1
  496. package/docs/content/locales/fr-FR/template-dispatch.mdx +2 -2
  497. package/docs/content/locales/fr-FR/template-plan.mdx +3 -3
  498. package/docs/content/locales/hi-IN/actions.mdx +1 -1
  499. package/docs/content/locales/hi-IN/authentication.mdx +1 -1
  500. package/docs/content/locales/hi-IN/dispatch.mdx +3 -3
  501. package/docs/content/locales/hi-IN/external-agents.mdx +31 -19
  502. package/docs/content/locales/hi-IN/mcp-clients.mdx +17 -3
  503. package/docs/content/locales/hi-IN/mcp-protocol.mdx +36 -15
  504. package/docs/content/locales/hi-IN/plan-plugin.mdx +3 -3
  505. package/docs/content/locales/hi-IN/server.mdx +2 -2
  506. package/docs/content/locales/hi-IN/skills-guide.mdx +1 -1
  507. package/docs/content/locales/hi-IN/template-assets.mdx +1 -1
  508. package/docs/content/locales/hi-IN/template-dispatch.mdx +2 -2
  509. package/docs/content/locales/hi-IN/template-plan.mdx +3 -3
  510. package/docs/content/locales/ja-JP/actions.mdx +1 -1
  511. package/docs/content/locales/ja-JP/authentication.mdx +1 -1
  512. package/docs/content/locales/ja-JP/dispatch.mdx +3 -3
  513. package/docs/content/locales/ja-JP/external-agents.mdx +23 -19
  514. package/docs/content/locales/ja-JP/mcp-clients.mdx +34 -3
  515. package/docs/content/locales/ja-JP/mcp-protocol.mdx +29 -15
  516. package/docs/content/locales/ja-JP/plan-plugin.mdx +3 -3
  517. package/docs/content/locales/ja-JP/server.mdx +2 -2
  518. package/docs/content/locales/ja-JP/skills-guide.mdx +1 -1
  519. package/docs/content/locales/ja-JP/template-assets.mdx +1 -1
  520. package/docs/content/locales/ja-JP/template-dispatch.mdx +2 -2
  521. package/docs/content/locales/ja-JP/template-plan.mdx +3 -3
  522. package/docs/content/locales/ko-KR/actions.mdx +1 -1
  523. package/docs/content/locales/ko-KR/authentication.mdx +1 -1
  524. package/docs/content/locales/ko-KR/dispatch.mdx +3 -3
  525. package/docs/content/locales/ko-KR/external-agents.mdx +30 -19
  526. package/docs/content/locales/ko-KR/mcp-clients.mdx +33 -3
  527. package/docs/content/locales/ko-KR/mcp-protocol.mdx +36 -15
  528. package/docs/content/locales/ko-KR/plan-plugin.mdx +3 -3
  529. package/docs/content/locales/ko-KR/server.mdx +2 -2
  530. package/docs/content/locales/ko-KR/skills-guide.mdx +1 -1
  531. package/docs/content/locales/ko-KR/template-assets.mdx +1 -1
  532. package/docs/content/locales/ko-KR/template-dispatch.mdx +2 -2
  533. package/docs/content/locales/ko-KR/template-plan.mdx +3 -3
  534. package/docs/content/locales/pt-BR/actions.mdx +1 -1
  535. package/docs/content/locales/pt-BR/authentication.mdx +1 -1
  536. package/docs/content/locales/pt-BR/dispatch.mdx +3 -3
  537. package/docs/content/locales/pt-BR/external-agents.mdx +32 -19
  538. package/docs/content/locales/pt-BR/mcp-clients.mdx +34 -3
  539. package/docs/content/locales/pt-BR/mcp-protocol.mdx +32 -15
  540. package/docs/content/locales/pt-BR/plan-plugin.mdx +3 -3
  541. package/docs/content/locales/pt-BR/server.mdx +2 -2
  542. package/docs/content/locales/pt-BR/skills-guide.mdx +1 -1
  543. package/docs/content/locales/pt-BR/template-assets.mdx +1 -1
  544. package/docs/content/locales/pt-BR/template-dispatch.mdx +2 -2
  545. package/docs/content/locales/pt-BR/template-plan.mdx +3 -3
  546. package/docs/content/locales/zh-CN/actions.mdx +1 -1
  547. package/docs/content/locales/zh-CN/authentication.mdx +1 -1
  548. package/docs/content/locales/zh-CN/dispatch.mdx +3 -3
  549. package/docs/content/locales/zh-CN/external-agents.mdx +23 -19
  550. package/docs/content/locales/zh-CN/mcp-clients.mdx +28 -3
  551. package/docs/content/locales/zh-CN/mcp-protocol.mdx +29 -15
  552. package/docs/content/locales/zh-CN/plan-plugin.mdx +3 -3
  553. package/docs/content/locales/zh-CN/server.mdx +2 -2
  554. package/docs/content/locales/zh-CN/skills-guide.mdx +1 -1
  555. package/docs/content/locales/zh-CN/template-assets.mdx +1 -1
  556. package/docs/content/locales/zh-CN/template-dispatch.mdx +2 -2
  557. package/docs/content/locales/zh-CN/template-plan.mdx +3 -3
  558. package/docs/content/locales/zh-TW/actions.mdx +1 -1
  559. package/docs/content/locales/zh-TW/authentication.mdx +1 -1
  560. package/docs/content/locales/zh-TW/dispatch.mdx +3 -3
  561. package/docs/content/locales/zh-TW/external-agents.mdx +23 -19
  562. package/docs/content/locales/zh-TW/mcp-clients.mdx +28 -3
  563. package/docs/content/locales/zh-TW/mcp-protocol.mdx +30 -15
  564. package/docs/content/locales/zh-TW/plan-plugin.mdx +3 -3
  565. package/docs/content/locales/zh-TW/server.mdx +2 -2
  566. package/docs/content/locales/zh-TW/skills-guide.mdx +1 -1
  567. package/docs/content/locales/zh-TW/template-assets.mdx +1 -1
  568. package/docs/content/locales/zh-TW/template-dispatch.mdx +2 -2
  569. package/docs/content/locales/zh-TW/template-plan.mdx +3 -3
  570. package/docs/content/mcp-clients.mdx +31 -4
  571. package/docs/content/mcp-protocol.mdx +35 -15
  572. package/docs/content/plan-plugin.mdx +3 -3
  573. package/docs/content/server.mdx +2 -2
  574. package/docs/content/skills-guide.mdx +1 -1
  575. package/docs/content/template-assets.mdx +1 -1
  576. package/docs/content/template-dispatch.mdx +2 -2
  577. package/docs/content/template-plan.mdx +3 -3
  578. package/package.json +2 -2
  579. package/src/templates/default/.agents/skills/security/SKILL.md +10 -0
  580. package/src/templates/workspace-core/.agents/skills/authentication/SKILL.md +15 -6
  581. package/src/templates/workspace-core/.agents/skills/external-agents/SKILL.md +16 -10
  582. package/src/templates/workspace-core/.agents/skills/performance/SKILL.md +20 -1
  583. package/src/templates/workspace-core/.agents/skills/security/SKILL.md +10 -0
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-07-13
4
+ ---
5
+
6
+ Session replay links now support Cmd-click and middle-click to open in a new tab
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-13
4
+ ---
5
+
6
+ Session replays keep a visible Mac-style cursor over the visitor's recorded actions
@@ -3,6 +3,7 @@ import { runWithRequestContext } from "@agent-native/core/server/request-context
3
3
  import { sendDashboardReportSubscription } from "../lib/dashboard-report";
4
4
  import {
5
5
  claimDueDashboardReportSubscriptions,
6
+ dashboardReportRetryAt,
6
7
  markDashboardReportResult,
7
8
  } from "../lib/dashboard-report-subscriptions";
8
9
 
@@ -41,22 +42,40 @@ export async function runDashboardReportsOnce(): Promise<{
41
42
  for (const sub of batch) {
42
43
  processed++;
43
44
  try {
45
+ const retryAt = dashboardReportRetryAt(sub);
44
46
  const result = await runWithRequestContext(
45
47
  {
46
48
  userEmail: sub.ownerEmail,
47
49
  orgId: sub.orgId ?? undefined,
48
50
  },
49
- () => sendDashboardReportSubscription(sub),
51
+ () =>
52
+ sendDashboardReportSubscription(sub, {
53
+ skipEmailWithoutScreenshot: retryAt !== null,
54
+ }),
50
55
  );
51
56
  if (!result.screenshotAttached) {
52
57
  const message = result.screenshotError
53
58
  ? `Dashboard screenshot unavailable: ${result.screenshotError}`
54
59
  : "Dashboard screenshot unavailable";
60
+ if (retryAt && !result.emailsSent) {
61
+ console.error(
62
+ `[dashboard-report] Subscription ${sub.id} skipped sending without a screenshot, will retry:`,
63
+ message,
64
+ );
65
+ await markDashboardReportResult(
66
+ sub,
67
+ "error",
68
+ `${message} (retry scheduled)`,
69
+ { nextRunAt: retryAt },
70
+ );
71
+ continue;
72
+ }
73
+ failed++;
55
74
  console.error(
56
75
  `[dashboard-report] Subscription ${sub.id} sent without a screenshot:`,
57
76
  message,
58
77
  );
59
- await markDashboardReportResult(sub, "success", message);
78
+ await markDashboardReportResult(sub, "error", message);
60
79
  continue;
61
80
  }
62
81
  await markDashboardReportResult(sub, "success");
@@ -158,6 +158,19 @@ function calendarDayAfter(parts: { year: number; month: number; day: number }) {
158
158
  };
159
159
  }
160
160
 
161
+ function calendarDayBefore(parts: {
162
+ year: number;
163
+ month: number;
164
+ day: number;
165
+ }) {
166
+ const prev = new Date(Date.UTC(parts.year, parts.month - 1, parts.day - 1));
167
+ return {
168
+ year: prev.getUTCFullYear(),
169
+ month: prev.getUTCMonth() + 1,
170
+ day: prev.getUTCDate(),
171
+ };
172
+ }
173
+
161
174
  function zonedTimeToUtc(
162
175
  parts: { year: number; month: number; day: number },
163
176
  hour: number,
@@ -205,6 +218,47 @@ export function nextDailyRunAt(
205
218
  return candidate.toISOString();
206
219
  }
207
220
 
221
+ export function lastDailyRunAt(
222
+ timeOfDay: string,
223
+ timeZone: string,
224
+ from: Date = new Date(),
225
+ ): string {
226
+ const [hour, minute] = assertTimeOfDay(timeOfDay).split(":").map(Number);
227
+ const timezone = assertTimezone(timeZone);
228
+ const today = getZonedParts(from, timezone);
229
+ let candidate = zonedTimeToUtc(today, hour, minute, timezone);
230
+ if (candidate.getTime() > from.getTime()) {
231
+ candidate = zonedTimeToUtc(
232
+ calendarDayBefore(today),
233
+ hour,
234
+ minute,
235
+ timezone,
236
+ );
237
+ }
238
+ return candidate.toISOString();
239
+ }
240
+
241
+ const DASHBOARD_REPORT_RETRY_WINDOW_MS = 60 * 60 * 1000;
242
+ const DASHBOARD_REPORT_RETRY_DELAY_MS = 10 * 60 * 1000;
243
+
244
+ export function dashboardReportRetryAt(
245
+ sub: DashboardReportSubscription,
246
+ now: Date = new Date(),
247
+ ): string | null {
248
+ if (!sub.enabled) return null;
249
+ try {
250
+ const anchor = Date.parse(lastDailyRunAt(sub.timeOfDay, sub.timezone, now));
251
+ if (now.getTime() - anchor < DASHBOARD_REPORT_RETRY_WINDOW_MS) {
252
+ return new Date(
253
+ now.getTime() + DASHBOARD_REPORT_RETRY_DELAY_MS,
254
+ ).toISOString();
255
+ }
256
+ return null;
257
+ } catch {
258
+ return null;
259
+ }
260
+ }
261
+
208
262
  function rowToSubscription(row: any): DashboardReportSubscription {
209
263
  return {
210
264
  id: row.id,
@@ -477,6 +531,7 @@ export async function markDashboardReportResult(
477
531
  sub: DashboardReportSubscription,
478
532
  status: "success" | "error",
479
533
  error?: string,
534
+ options?: { nextRunAt?: string },
480
535
  ): Promise<void> {
481
536
  const now = nowIso();
482
537
  const db = getDb() as any;
@@ -486,7 +541,8 @@ export async function markDashboardReportResult(
486
541
  lastStatus: status,
487
542
  lastError: error ? error.slice(0, 500) : null,
488
543
  nextRunAt: sub.enabled
489
- ? nextDailyRunAt(sub.timeOfDay, sub.timezone, new Date())
544
+ ? (options?.nextRunAt ??
545
+ nextDailyRunAt(sub.timeOfDay, sub.timezone, new Date()))
490
546
  : null,
491
547
  updatedAt: now,
492
548
  })
@@ -1,4 +1,8 @@
1
+ import { randomUUID } from "node:crypto";
1
2
  import { existsSync } from "node:fs";
3
+ import { readdir, rm, stat } from "node:fs/promises";
4
+ import { tmpdir } from "node:os";
5
+ import { join } from "node:path";
2
6
 
3
7
  import {
4
8
  getAppProductionUrl,
@@ -44,11 +48,24 @@ const DASHBOARD_REPORT_CID = "dashboard-report-snapshot";
44
48
  const LOCAL_SCREENSHOT_TIMEOUT_MS = 90_000;
45
49
  const SERVERLESS_SCREENSHOT_TIMEOUT_MS = 90_000;
46
50
  const SERVERLESS_SECOND_READY_TIMEOUT_MS = 45_000;
51
+ const SERVERLESS_FULL_ATTEMPT_TIMEOUT_MS = 125_000;
52
+ const SERVERLESS_LIGHTWEIGHT_ATTEMPT_TIMEOUT_MS = 85_000;
53
+ const BROWSER_CLEANUP_TIMEOUT_MS = 10_000;
47
54
  const SCREENSHOT_VIEWPORT_PADDING = 64;
48
55
 
49
56
  type DashboardScreenshotAttempt = {
50
57
  label: "full" | "full-lightweight";
51
58
  viewport: { width: number; height: number };
59
+ captureScale?: number;
60
+ readyTimeout?: number;
61
+ secondReadyTimeout?: number;
62
+ totalTimeout?: number;
63
+ };
64
+
65
+ type LaunchedScreenshotBrowser = {
66
+ browser: any;
67
+ cleanup: () => Promise<void>;
68
+ newPage: () => Promise<any>;
52
69
  };
53
70
 
54
71
  function daysAgo(n: number): string {
@@ -216,14 +233,44 @@ function localChromiumExecutablePath(): string | null {
216
233
  return candidates.find((candidate) => existsSync(candidate)) ?? null;
217
234
  }
218
235
 
219
- async function launchScreenshotBrowser() {
236
+ async function sweepStaleScreenshotProfiles(): Promise<void> {
237
+ let entries: string[];
238
+ try {
239
+ entries = await readdir(tmpdir());
240
+ } catch {
241
+ return;
242
+ }
243
+ const stale = entries
244
+ .filter((entry) => entry.startsWith("dashboard-report-playwright-"))
245
+ .slice(0, 8);
246
+ for (const entry of stale) {
247
+ const full = join(tmpdir(), entry);
248
+ const info = await stat(full).catch(() => null);
249
+ if (info && Date.now() - info.mtimeMs > 30 * 60_000) {
250
+ await rm(full, { recursive: true, force: true }).catch(() => {});
251
+ }
252
+ }
253
+ }
254
+
255
+ async function launchScreenshotBrowser(
256
+ viewport: DashboardScreenshotAttempt["viewport"],
257
+ ): Promise<LaunchedScreenshotBrowser> {
220
258
  const { chromium: playwright } = await import("playwright-core");
221
259
  const localExecutablePath = localChromiumExecutablePath();
222
260
  if (localExecutablePath) {
223
- return playwright.launch({
261
+ const browser = await playwright.launch({
224
262
  executablePath: localExecutablePath,
225
263
  headless: true,
226
264
  });
265
+ return {
266
+ browser,
267
+ cleanup: async () => {},
268
+ newPage: () =>
269
+ browser.newPage({
270
+ viewport,
271
+ deviceScaleFactor: 1,
272
+ }),
273
+ };
227
274
  }
228
275
 
229
276
  if (isServerlessBrowserRuntime()) {
@@ -232,19 +279,54 @@ async function launchScreenshotBrowser() {
232
279
  const packUrl =
233
280
  process.env.DASHBOARD_REPORT_CHROMIUM_PACK_URL?.trim() ||
234
281
  DEFAULT_SERVERLESS_CHROMIUM_PACK_URL;
235
- return playwright.launch({
236
- args: [
237
- ...chromium.args,
238
- "--disable-dev-shm-usage",
239
- "--disable-gpu",
240
- "--hide-scrollbars",
241
- ],
242
- executablePath: await chromium.executablePath(packUrl),
243
- headless: true,
244
- });
282
+ await sweepStaleScreenshotProfiles();
283
+ const userDataDir = join(
284
+ tmpdir(),
285
+ `dashboard-report-playwright-${randomUUID()}`,
286
+ );
287
+ const cleanup = async () => {
288
+ await rm(userDataDir, { recursive: true, force: true }).catch((err) => {
289
+ console.error(
290
+ "[dashboard-report] Failed to clean Chromium profile:",
291
+ errorMessage(err),
292
+ );
293
+ });
294
+ };
295
+
296
+ try {
297
+ const browser = await playwright.launchPersistentContext(userDataDir, {
298
+ args: [
299
+ ...chromium.args,
300
+ "--disable-dev-shm-usage",
301
+ "--disable-gpu",
302
+ "--hide-scrollbars",
303
+ ],
304
+ deviceScaleFactor: 1,
305
+ executablePath: await chromium.executablePath(packUrl),
306
+ headless: true,
307
+ viewport,
308
+ });
309
+ return {
310
+ browser,
311
+ cleanup,
312
+ newPage: () => browser.newPage(),
313
+ };
314
+ } catch (err) {
315
+ await cleanup();
316
+ throw err;
317
+ }
245
318
  }
246
319
 
247
- return playwright.launch({ headless: true });
320
+ const browser = await playwright.launch({ headless: true });
321
+ return {
322
+ browser,
323
+ cleanup: async () => {},
324
+ newPage: () =>
325
+ browser.newPage({
326
+ viewport,
327
+ deviceScaleFactor: 1,
328
+ }),
329
+ };
248
330
  }
249
331
 
250
332
  function screenshotTimeoutMs(): number {
@@ -350,6 +432,40 @@ async function fitViewportWidthToDashboardCapture(
350
432
  await page.waitForTimeout(250);
351
433
  }
352
434
 
435
+ async function scaleDashboardCapture(
436
+ page: any,
437
+ scale: number | undefined,
438
+ ): Promise<void> {
439
+ if (!scale || scale >= 1) return;
440
+ await page.evaluate(`(() => {
441
+ const root = document.querySelector("[data-dashboard-report-capture]");
442
+ if (root instanceof HTMLElement) root.style.zoom = "${scale}";
443
+ })()`);
444
+ await page.waitForTimeout(250);
445
+ }
446
+
447
+ async function runBoundedBrowserCleanup(
448
+ label: string,
449
+ operation: () => Promise<void>,
450
+ ): Promise<void> {
451
+ let timeout: ReturnType<typeof setTimeout> | undefined;
452
+ try {
453
+ await Promise.race([
454
+ operation(),
455
+ new Promise<never>((_, reject) => {
456
+ timeout = setTimeout(
457
+ () => reject(new Error(`${label} exceeded cleanup timeout`)),
458
+ BROWSER_CLEANUP_TIMEOUT_MS,
459
+ );
460
+ }),
461
+ ]);
462
+ } catch (err) {
463
+ console.error(`[dashboard-report] ${label}:`, errorMessage(err));
464
+ } finally {
465
+ if (timeout) clearTimeout(timeout);
466
+ }
467
+ }
468
+
353
469
  async function captureDashboardPng(
354
470
  sub: DashboardReportSubscription,
355
471
  snapshot: ReportSnapshot,
@@ -373,13 +489,47 @@ async function captureDashboardPng(
373
489
  screenshotUrl.searchParams.set(EMBED_MODE_QUERY_PARAM, "1");
374
490
  screenshotUrl.searchParams.set(EMBED_TOKEN_QUERY_PARAM, token);
375
491
 
376
- const browser = await launchScreenshotBrowser();
492
+ let browser: any;
493
+ let cleanup = async () => {};
494
+ let newPage = async (): Promise<any> => {
495
+ throw new Error("Screenshot browser did not provide a page factory");
496
+ };
497
+ let launchPromise: Promise<LaunchedScreenshotBrowser> | undefined;
498
+ let launchTimeout: ReturnType<typeof setTimeout> | undefined;
499
+ let captureStage = "launching the screenshot browser";
500
+ let attemptTimedOut = false;
501
+ const attemptTimeout = attempt.totalTimeout
502
+ ? setTimeout(() => {
503
+ attemptTimedOut = true;
504
+ if (browser) void browser.close().catch(() => {});
505
+ }, attempt.totalTimeout)
506
+ : null;
377
507
  try {
508
+ launchPromise = launchScreenshotBrowser(attempt.viewport);
509
+ const launched = attempt.totalTimeout
510
+ ? await Promise.race([
511
+ launchPromise,
512
+ new Promise<never>((_, reject) => {
513
+ launchTimeout = setTimeout(() => {
514
+ attemptTimedOut = true;
515
+ reject(
516
+ new Error(
517
+ `${attempt.label} browser launch exceeded ${attempt.totalTimeout}ms`,
518
+ ),
519
+ );
520
+ }, attempt.totalTimeout);
521
+ }),
522
+ ])
523
+ : await launchPromise;
524
+ browser = launched.browser;
525
+ cleanup = launched.cleanup;
526
+ newPage = launched.newPage;
527
+ if (attemptTimedOut) {
528
+ throw new Error("Screenshot browser launch exceeded attempt timeout");
529
+ }
530
+
378
531
  const timeout = screenshotTimeoutMs();
379
- const page = await browser.newPage({
380
- viewport: attempt.viewport,
381
- deviceScaleFactor: 1,
382
- });
532
+ const page = await newPage();
383
533
  page.setDefaultTimeout(timeout);
384
534
  await page.emulateMedia({ media: "screen", colorScheme: "light" });
385
535
  await page.addInitScript(() => {
@@ -390,14 +540,24 @@ async function captureDashboardPng(
390
540
  timeout,
391
541
  });
392
542
 
543
+ captureStage = "waiting for the report surface";
393
544
  const capture = page.locator("[data-dashboard-report-capture]");
394
545
  await capture.waitFor({ state: "visible", timeout });
395
- await waitForDashboardReportReady(page, timeout);
546
+ captureStage = "waiting for dashboard queries";
547
+ await waitForDashboardReportReady(page, attempt.readyTimeout ?? timeout);
548
+ captureStage = "rendering lazy dashboard panels";
396
549
  await scrollDashboardForLazyRendering(page);
397
- await waitForDashboardReportReady(page, secondReadyTimeoutMs());
550
+ captureStage = "waiting for lazy dashboard panels";
551
+ await waitForDashboardReportReady(
552
+ page,
553
+ attempt.secondReadyTimeout ?? secondReadyTimeoutMs(),
554
+ );
398
555
 
556
+ captureStage = "sizing the dashboard capture";
399
557
  await fitViewportWidthToDashboardCapture(page, capture, attempt.viewport);
558
+ await scaleDashboardCapture(page, attempt.captureScale);
400
559
  await capture.scrollIntoViewIfNeeded();
560
+ captureStage = "rasterizing the dashboard PNG";
401
561
  const image = await capture.screenshot({
402
562
  type: "png",
403
563
  animations: "disabled",
@@ -406,8 +566,39 @@ async function captureDashboardPng(
406
566
  throw new Error("Dashboard screenshot was empty");
407
567
  }
408
568
  return Buffer.from(image);
569
+ } catch (err) {
570
+ if (attemptTimedOut) {
571
+ throw new Error(
572
+ `${attempt.label} capture exceeded ${attempt.totalTimeout}ms while ${captureStage}`,
573
+ );
574
+ }
575
+ throw new Error(`${captureStage}: ${errorMessage(err)}`);
409
576
  } finally {
410
- await browser.close();
577
+ if (attemptTimeout) clearTimeout(attemptTimeout);
578
+ if (launchTimeout) clearTimeout(launchTimeout);
579
+ if (!browser && launchPromise) {
580
+ void launchPromise.then(
581
+ async (lateBrowser) => {
582
+ await runBoundedBrowserCleanup(
583
+ "Failed to close late screenshot browser",
584
+ () => lateBrowser.browser.close(),
585
+ );
586
+ await runBoundedBrowserCleanup(
587
+ "Failed to clean late Chromium profile",
588
+ lateBrowser.cleanup,
589
+ );
590
+ },
591
+ () => {
592
+ // The launch rejection was already handled by the race above.
593
+ },
594
+ );
595
+ }
596
+ if (browser) {
597
+ await runBoundedBrowserCleanup("Failed to close screenshot browser", () =>
598
+ browser.close(),
599
+ );
600
+ }
601
+ await runBoundedBrowserCleanup("Failed to clean Chromium profile", cleanup);
411
602
  }
412
603
  }
413
604
 
@@ -419,6 +610,11 @@ function errorMessage(err: unknown): string {
419
610
  );
420
611
  }
421
612
 
613
+ function storedAttemptError(message: string): string {
614
+ const normalized = message.replace(/\s+/g, " ").trim();
615
+ return normalized.length > 220 ? `${normalized.slice(0, 219)}…` : normalized;
616
+ }
617
+
422
618
  async function captureDashboardPngWithFallback(
423
619
  sub: DashboardReportSubscription,
424
620
  snapshot: ReportSnapshot,
@@ -427,11 +623,33 @@ async function captureDashboardPngWithFallback(
427
623
  mode: "full" | "full-lightweight" | "none";
428
624
  error?: string;
429
625
  }> {
626
+ const serverless = isServerlessBrowserRuntime();
430
627
  const attempts: DashboardScreenshotAttempt[] = [
431
- { label: "full", viewport: { width: 1440, height: 1800 } },
432
- { label: "full-lightweight", viewport: { width: 1200, height: 1400 } },
628
+ {
629
+ label: "full",
630
+ viewport: { width: 1440, height: 1800 },
631
+ captureScale: 0.85,
632
+ ...(serverless
633
+ ? {
634
+ secondReadyTimeout: 25_000,
635
+ totalTimeout: SERVERLESS_FULL_ATTEMPT_TIMEOUT_MS,
636
+ }
637
+ : {}),
638
+ },
639
+ {
640
+ label: "full-lightweight",
641
+ viewport: { width: 1200, height: 1400 },
642
+ captureScale: 0.7,
643
+ ...(serverless
644
+ ? {
645
+ readyTimeout: 55_000,
646
+ secondReadyTimeout: 15_000,
647
+ totalTimeout: SERVERLESS_LIGHTWEIGHT_ATTEMPT_TIMEOUT_MS,
648
+ }
649
+ : {}),
650
+ },
433
651
  ];
434
- let lastError: string | undefined;
652
+ const errors: string[] = [];
435
653
 
436
654
  for (const attempt of attempts) {
437
655
  try {
@@ -440,15 +658,20 @@ async function captureDashboardPngWithFallback(
440
658
  mode: attempt.label,
441
659
  };
442
660
  } catch (err) {
443
- lastError = errorMessage(err);
661
+ const attemptError = errorMessage(err);
662
+ errors.push(`${attempt.label}: ${storedAttemptError(attemptError)}`);
444
663
  console.error(
445
664
  `[dashboard-report] ${attempt.label} screenshot failed for subscription ${sub.id}:`,
446
- lastError,
665
+ attemptError,
447
666
  );
448
667
  }
449
668
  }
450
669
 
451
- return { png: null, mode: "none", error: lastError };
670
+ return {
671
+ png: null,
672
+ mode: "none",
673
+ ...(errors.length ? { error: errors.join(" | ") } : {}),
674
+ };
452
675
  }
453
676
 
454
677
  function reportDate(snapshot: ReportSnapshot): string {
@@ -518,13 +741,17 @@ function reportFilename(title: string): string {
518
741
 
519
742
  export async function sendDashboardReportSubscription(
520
743
  sub: DashboardReportSubscription,
521
- options: { requireScreenshot?: boolean } = {},
744
+ options: {
745
+ requireScreenshot?: boolean;
746
+ skipEmailWithoutScreenshot?: boolean;
747
+ } = {},
522
748
  ): Promise<{
523
749
  dashboardUrl: string;
524
750
  recipientCount: number;
525
751
  screenshotAttached: boolean;
526
752
  screenshotMode: "full" | "full-lightweight" | "none";
527
753
  screenshotError?: string;
754
+ emailsSent: boolean;
528
755
  }> {
529
756
  const snapshot = await collectReportSnapshot(sub);
530
757
  const capture = await captureDashboardPngWithFallback(sub, snapshot);
@@ -535,6 +762,16 @@ export async function sendDashboardReportSubscription(
535
762
  : "Dashboard screenshot unavailable",
536
763
  );
537
764
  }
765
+ if (!capture.png && options.skipEmailWithoutScreenshot) {
766
+ return {
767
+ dashboardUrl: snapshot.dashboardUrl,
768
+ recipientCount: sub.recipients.length,
769
+ screenshotAttached: false,
770
+ screenshotMode: capture.mode,
771
+ emailsSent: false,
772
+ ...(capture.error ? { screenshotError: capture.error } : {}),
773
+ };
774
+ }
538
775
  const screenshotAttached = Boolean(capture.png);
539
776
  const html = renderReportEmailHtml(snapshot, { screenshotAttached });
540
777
  const text = renderReportText(snapshot, { screenshotAttached });
@@ -565,6 +802,7 @@ export async function sendDashboardReportSubscription(
565
802
  recipientCount: sub.recipients.length,
566
803
  screenshotAttached,
567
804
  screenshotMode: capture.mode,
805
+ emailsSent: true,
568
806
  ...(capture.error ? { screenshotError: capture.error } : {}),
569
807
  };
570
808
  }
@@ -1846,12 +1846,38 @@ export async function saveDashboardView(
1846
1846
  orgId: ctx.orgId ?? undefined,
1847
1847
  });
1848
1848
  const db = getDb() as any;
1849
- const id = view.id ?? nanoidFallback();
1849
+ let id = view.id ?? nanoidFallback();
1850
+ let existing = false;
1850
1851
  if (view.id) {
1852
+ const [existingRow] = await db
1853
+ .select({
1854
+ id: schema.dashboardViews.id,
1855
+ dashboardId: schema.dashboardViews.dashboardId,
1856
+ })
1857
+ .from(schema.dashboardViews)
1858
+ .where(eq(schema.dashboardViews.id, view.id))
1859
+ .limit(1);
1860
+ if (existingRow?.dashboardId === dashboardId) {
1861
+ existing = true;
1862
+ } else if (existingRow) {
1863
+ // View ids are generated from names in the browser but remain globally
1864
+ // primary-keyed for backwards compatibility. Avoid colliding with a
1865
+ // same-named view on another dashboard instead of updating that row or
1866
+ // returning a raw unique-constraint 500.
1867
+ id = nanoidFallback();
1868
+ }
1869
+ }
1870
+
1871
+ if (existing) {
1851
1872
  await db
1852
1873
  .update(schema.dashboardViews)
1853
1874
  .set({ name: view.name, filters: JSON.stringify(view.filters) })
1854
- .where(eq(schema.dashboardViews.id, id));
1875
+ .where(
1876
+ and(
1877
+ eq(schema.dashboardViews.id, id),
1878
+ eq(schema.dashboardViews.dashboardId, dashboardId),
1879
+ ),
1880
+ );
1855
1881
  } else {
1856
1882
  await db.insert(schema.dashboardViews).values({
1857
1883
  id,
@@ -1864,7 +1890,15 @@ export async function saveDashboardView(
1864
1890
  const [row] = await db
1865
1891
  .select()
1866
1892
  .from(schema.dashboardViews)
1867
- .where(eq(schema.dashboardViews.id, id));
1893
+ .where(
1894
+ and(
1895
+ eq(schema.dashboardViews.id, id),
1896
+ eq(schema.dashboardViews.dashboardId, dashboardId),
1897
+ ),
1898
+ );
1899
+ if (!row) {
1900
+ throw new Error("Dashboard view was not persisted");
1901
+ }
1868
1902
  const dash = await getDashboard(dashboardId, ctx);
1869
1903
  if (dash) {
1870
1904
  recordScopedChange(
@@ -1891,7 +1925,12 @@ export async function deleteDashboardView(
1891
1925
  const db = getDb() as any;
1892
1926
  await db
1893
1927
  .delete(schema.dashboardViews)
1894
- .where(eq(schema.dashboardViews.id, viewId));
1928
+ .where(
1929
+ and(
1930
+ eq(schema.dashboardViews.id, viewId),
1931
+ eq(schema.dashboardViews.dashboardId, dashboardId),
1932
+ ),
1933
+ );
1895
1934
  const dash = await getDashboard(dashboardId, ctx);
1896
1935
  if (dash) {
1897
1936
  recordScopedChange(
@@ -547,6 +547,27 @@ export interface RawExceptionInput {
547
547
  breadcrumbs: unknown[];
548
548
  }
549
549
 
550
+ /**
551
+ * Browser request cancellation is expected during navigation and query
552
+ * invalidation. Keep the first-party issue store aligned with the client
553
+ * Sentry filter, while preserving other AbortError failures for triage.
554
+ */
555
+ export function isBenignBrowserAbortException(
556
+ input: Pick<RawExceptionInput, "type" | "message">,
557
+ ): boolean {
558
+ const exceptionType = input.type.trim().toLowerCase();
559
+ const exceptionValue = input.message.trim().toLowerCase();
560
+ return (
561
+ exceptionValue === "the user aborted a request." ||
562
+ exceptionValue === "signal is aborted without reason" ||
563
+ exceptionValue === "aborterror: the user aborted a request." ||
564
+ exceptionValue === "aborterror: signal is aborted without reason" ||
565
+ (exceptionType === "aborterror" &&
566
+ (exceptionValue.includes("the user aborted a request") ||
567
+ exceptionValue.includes("signal is aborted without reason")))
568
+ );
569
+ }
570
+
550
571
  function nowIso(): string {
551
572
  return new Date().toISOString();
552
573
  }
@@ -930,11 +951,9 @@ export async function ingestAnalyticsExceptionEvents(
930
951
  let ingested = 0;
931
952
  for (const item of events) {
932
953
  try {
933
- await ingestException(
934
- scope,
935
- extractExceptionInput(item.properties),
936
- item.derived,
937
- );
954
+ const raw = extractExceptionInput(item.properties);
955
+ if (isBenignBrowserAbortException(raw)) continue;
956
+ await ingestException(scope, raw, item.derived);
938
957
  ingested += 1;
939
958
  } catch (error) {
940
959
  console.warn("[error-capture] Failed to ingest exception event:", error);
@@ -239,6 +239,11 @@ export function realDataFinalGuard(
239
239
  // actual query. One corrective turn was enough for Sonnet but caused Luna
240
240
  // to hit the fallback before it reached the query.
241
241
  maxRetries: 2,
242
+ // The first request may use the compact starter catalog. A corrective
243
+ // retry must be able to reach the real source action directly; otherwise
244
+ // some model families spend the retry on tool-search narration and hit
245
+ // the canned fallback without ever running a query.
246
+ expandToolSurface: true,
242
247
  };
243
248
  }
244
249