@agent-native/core 0.98.17 → 0.99.0

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 (545) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +15 -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/cli/connect.ts +50 -24
  139. package/corpus/core/src/cli/mcp-config-writers.ts +10 -0
  140. package/corpus/core/src/cli/mcp.ts +5 -5
  141. package/corpus/core/src/cli/skills-content/assets-skill.ts +1 -1
  142. package/corpus/core/src/cli/skills-content/design-exploration-skill.ts +1 -1
  143. package/corpus/core/src/cli/skills-content/help.ts +1 -1
  144. package/corpus/core/src/cli/skills-content/plan-setup-auth.ts +1 -1
  145. package/corpus/core/src/cli/skills-content/visual-plan-skill.ts +1 -1
  146. package/corpus/core/src/cli/skills-content/visual-recap-skill.ts +2 -2
  147. package/corpus/core/src/cli/skills.ts +23 -12
  148. package/corpus/core/src/client/resources/McpIntegrationDialog.tsx +5 -0
  149. package/corpus/core/src/client/resources/mcp-integration-catalog.ts +17 -0
  150. package/corpus/core/src/embedding/agent.ts +8 -6
  151. package/corpus/core/src/localization/default-messages.ts +7 -0
  152. package/corpus/core/src/mcp/ask-app-inline-tasks.ts +125 -0
  153. package/corpus/core/src/mcp/build-server.ts +65 -3
  154. package/corpus/core/src/mcp/builtin-tools.ts +186 -22
  155. package/corpus/core/src/mcp/connect-route.ts +17 -14
  156. package/corpus/core/src/mcp/oauth-route.ts +38 -13
  157. package/corpus/core/src/mcp/route-paths.ts +34 -0
  158. package/corpus/core/src/mcp/server.ts +21 -10
  159. package/corpus/core/src/mcp/stdio.ts +34 -16
  160. package/corpus/core/src/mcp/workspace-resolve.ts +2 -1
  161. package/corpus/core/src/mcp-client/manager.ts +5 -3
  162. package/corpus/core/src/mcp-client/remote-store.ts +10 -10
  163. package/corpus/core/src/server/agent-chat-plugin.ts +42 -9
  164. package/corpus/core/src/server/auth.ts +22 -6
  165. package/corpus/core/src/server/core-routes-plugin.ts +30 -24
  166. package/corpus/core/src/server/csrf.ts +17 -0
  167. package/corpus/core/src/server/framework-request-handler.ts +8 -2
  168. package/corpus/core/src/server/google-oauth.ts +6 -0
  169. package/corpus/core/src/server/http-response-telemetry.ts +6 -1
  170. package/corpus/core/src/server/recap-image-route.ts +3 -3
  171. package/corpus/core/src/server/security-headers.ts +7 -1
  172. package/corpus/core/src/server/ssr-handler.ts +2 -0
  173. package/corpus/core/src/shared/cache-control.ts +6 -4
  174. package/corpus/core/src/templates/default/.agents/skills/security/SKILL.md +10 -0
  175. package/corpus/core/src/templates/workspace-core/.agents/skills/authentication/SKILL.md +15 -6
  176. package/corpus/core/src/templates/workspace-core/.agents/skills/external-agents/SKILL.md +16 -10
  177. package/corpus/core/src/templates/workspace-core/.agents/skills/performance/SKILL.md +20 -1
  178. package/corpus/core/src/templates/workspace-core/.agents/skills/security/SKILL.md +10 -0
  179. package/corpus/templates/analytics/.agents/skills/security/SKILL.md +10 -0
  180. package/corpus/templates/analytics/app/global.css +63 -4
  181. package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +8 -5
  182. package/corpus/templates/analytics/changelog/2026-07-13-daily-dashboard-emails-recover-more-reliably-from-serverless.md +6 -0
  183. package/corpus/templates/analytics/changelog/2026-07-13-session-replays-keep-a-visible-mac-style-cursor-over-the-vis.md +6 -0
  184. package/corpus/templates/analytics/server/jobs/dashboard-report.ts +21 -2
  185. package/corpus/templates/analytics/server/lib/dashboard-report-subscriptions.ts +57 -1
  186. package/corpus/templates/analytics/server/lib/dashboard-report.ts +266 -28
  187. package/corpus/templates/analytics/server/routes/[...page].get.ts +0 -2
  188. package/corpus/templates/assets/.agents/skills/security/SKILL.md +10 -0
  189. package/corpus/templates/assets/agent-native.app-skill.json +1 -1
  190. package/corpus/templates/brain/.agents/skills/security/SKILL.md +10 -0
  191. package/corpus/templates/calendar/.agents/skills/security/SKILL.md +10 -0
  192. package/corpus/templates/calendar/AGENTS.md +2 -1
  193. package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +0 -19
  194. package/corpus/templates/calendar/app/lib/attendee-local-time.ts +14 -5
  195. package/corpus/templates/calendar/changelog/2026-07-13-your-local-time-now-follows-your-browser-timezone-and-event-.md +6 -0
  196. package/corpus/templates/chat/.agents/skills/security/SKILL.md +10 -0
  197. package/corpus/templates/clips/.agents/skills/recording/SKILL.md +7 -2
  198. package/corpus/templates/clips/.agents/skills/security/SKILL.md +10 -0
  199. package/corpus/templates/clips/AGENTS.md +7 -0
  200. package/corpus/templates/clips/actions/list-meetings.ts +19 -4
  201. package/corpus/templates/clips/actions/start-meeting-recording.ts +7 -1
  202. package/corpus/templates/clips/app/components/meetings/transcript-bubbles.tsx +75 -78
  203. package/corpus/templates/clips/app/components/recorder/confetti-canvas.tsx +18 -1
  204. package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +6 -12
  205. package/corpus/templates/clips/app/components/recorder/recording-toolbar.tsx +19 -2
  206. package/corpus/templates/clips/app/lib/camera-composite.ts +70 -10
  207. package/corpus/templates/clips/app/routes/_app.meetings.$meetingId.tsx +1 -1
  208. package/corpus/templates/clips/app/routes/record.tsx +5 -25
  209. package/corpus/templates/clips/changelog/2026-07-13-clips-desktop-now-requires-macos-13-for-native-capture.md +6 -0
  210. package/corpus/templates/clips/changelog/2026-07-13-clips-hide-solo-meeting-reminders.md +6 -0
  211. package/corpus/templates/clips/changelog/2026-07-13-clips-now-removes-duplicate-mic-and-system-transcript-turns-.md +6 -0
  212. package/corpus/templates/clips/changelog/2026-07-13-desktop-recordings-run-cooler-and-quieter-live-transcription.md +6 -0
  213. package/corpus/templates/clips/changelog/2026-07-13-meeting-recordings-now-stop-after-a-calendar-meeting-ends-an.md +6 -0
  214. package/corpus/templates/clips/changelog/2026-07-13-meeting-transcripts-now-use-a-clean-agent-chat-style-reading.md +6 -0
  215. package/corpus/templates/clips/changelog/2026-07-13-screen-recordings-use-less-processing-power-during-longer-ca.md +6 -0
  216. package/corpus/templates/clips/chrome-extension/src/background.ts +5 -3
  217. package/corpus/templates/clips/chrome-extension/src/offscreen.ts +17 -14
  218. package/corpus/templates/clips/chrome-extension/src/overlay.ts +8 -2
  219. package/corpus/templates/clips/desktop/README.md +3 -0
  220. package/corpus/templates/clips/desktop/src/hooks/useMeetingTranscription.ts +9 -0
  221. package/corpus/templates/clips/desktop/src/lib/recorder.ts +8 -2
  222. package/corpus/templates/clips/desktop/src/lib/silence-events.ts +1 -0
  223. package/corpus/templates/clips/desktop/src/lib/transcription-capture.ts +5 -0
  224. package/corpus/templates/clips/desktop/src/lib/transcription-engine.ts +134 -5
  225. package/corpus/templates/clips/desktop/src/lib/upload-video-stream.ts +28 -0
  226. package/corpus/templates/clips/desktop/src/overlays/flow-bar.tsx +10 -3
  227. package/corpus/templates/clips/desktop/src/overlays/meeting-notification.tsx +42 -9
  228. package/corpus/templates/clips/desktop/src/overlays/recording-pill.tsx +10 -2
  229. package/corpus/templates/clips/desktop/src-tauri/Cargo.toml +6 -1
  230. package/corpus/templates/clips/desktop/src-tauri/src/notifications.rs +77 -1
  231. package/corpus/templates/clips/desktop/src-tauri/src/silence_detector.rs +104 -25
  232. package/corpus/templates/clips/desktop/src-tauri/src/system_audio.rs +4 -0
  233. package/corpus/templates/clips/desktop/src-tauri/src/whisper_speech.rs +287 -32
  234. package/corpus/templates/clips/desktop/src-tauri/tauri.conf.json +1 -1
  235. package/corpus/templates/clips/server/lib/calendar-event-classification.ts +28 -0
  236. package/corpus/templates/clips/server/routes/[...page].get.ts +1 -59
  237. package/corpus/templates/clips/shared/recording-capture.ts +37 -0
  238. package/corpus/templates/content/.agents/skills/security/SKILL.md +10 -0
  239. package/corpus/templates/content/agent-native.app-skill.json +1 -1
  240. package/corpus/templates/content/server/routes/[...page].get.ts +1 -15
  241. package/corpus/templates/design/.agents/skills/security/SKILL.md +10 -0
  242. package/corpus/templates/design/actions/connect-assets-mcp.ts +1 -1
  243. package/corpus/templates/design/agent-native.app-skill.json +1 -1
  244. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +88 -12
  245. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +1 -0
  246. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +4 -1
  247. package/corpus/templates/design/changelog/2026-07-13-adding-shapes-preserves-canvas-background.md +6 -0
  248. package/corpus/templates/design/changelog/2026-07-13-clicking-quickly-between-overlapping-elements-no-longer-bounc.md +6 -0
  249. package/corpus/templates/design/server/plugins/assets-mcp.ts +1 -1
  250. package/corpus/templates/design/server/routes/[...page].get.ts +0 -2
  251. package/corpus/templates/dispatch/.agents/skills/security/SKILL.md +10 -0
  252. package/corpus/templates/forms/.agents/skills/security/SKILL.md +10 -0
  253. package/corpus/templates/macros/.agents/skills/security/SKILL.md +10 -0
  254. package/corpus/templates/mail/.agents/skills/security/SKILL.md +10 -0
  255. package/corpus/templates/plan/.agents/skills/security/SKILL.md +10 -0
  256. package/corpus/templates/plan/.agents/skills/visual-plan/SKILL.md +1 -1
  257. package/corpus/templates/plan/.agents/skills/visual-recap/SKILL.md +2 -2
  258. package/corpus/templates/plan/agent-native.app-skill.json +1 -1
  259. package/corpus/templates/plan/app/lib/plan-local-bridge.ts +6 -1
  260. package/corpus/templates/plan/app/pages/PlansPage.tsx +15 -6
  261. package/corpus/templates/plan/changelog/2026-07-13-fixed-local-plan-previews-continuously-flickering-while-revi.md +6 -0
  262. package/corpus/templates/plan/server/routes/[...page].get.ts +0 -2
  263. package/corpus/templates/slides/.agents/skills/security/SKILL.md +10 -0
  264. package/dist/a2a/client.d.ts +2 -0
  265. package/dist/a2a/client.d.ts.map +1 -1
  266. package/dist/a2a/client.js +2 -0
  267. package/dist/a2a/client.js.map +1 -1
  268. package/dist/a2a/handlers.d.ts.map +1 -1
  269. package/dist/a2a/handlers.js +84 -7
  270. package/dist/a2a/handlers.js.map +1 -1
  271. package/dist/cli/connect.d.ts +2 -2
  272. package/dist/cli/connect.d.ts.map +1 -1
  273. package/dist/cli/connect.js +45 -23
  274. package/dist/cli/connect.js.map +1 -1
  275. package/dist/cli/mcp-config-writers.d.ts.map +1 -1
  276. package/dist/cli/mcp-config-writers.js +7 -0
  277. package/dist/cli/mcp-config-writers.js.map +1 -1
  278. package/dist/cli/mcp.js +5 -3
  279. package/dist/cli/mcp.js.map +1 -1
  280. package/dist/cli/skills-content/assets-skill.d.ts +1 -1
  281. package/dist/cli/skills-content/assets-skill.d.ts.map +1 -1
  282. package/dist/cli/skills-content/assets-skill.js +1 -1
  283. package/dist/cli/skills-content/assets-skill.js.map +1 -1
  284. package/dist/cli/skills-content/design-exploration-skill.d.ts +1 -1
  285. package/dist/cli/skills-content/design-exploration-skill.d.ts.map +1 -1
  286. package/dist/cli/skills-content/design-exploration-skill.js +1 -1
  287. package/dist/cli/skills-content/design-exploration-skill.js.map +1 -1
  288. package/dist/cli/skills-content/help.d.ts +1 -1
  289. package/dist/cli/skills-content/help.d.ts.map +1 -1
  290. package/dist/cli/skills-content/help.js +1 -1
  291. package/dist/cli/skills-content/help.js.map +1 -1
  292. package/dist/cli/skills-content/plan-setup-auth.d.ts +1 -1
  293. package/dist/cli/skills-content/plan-setup-auth.d.ts.map +1 -1
  294. package/dist/cli/skills-content/plan-setup-auth.js +1 -1
  295. package/dist/cli/skills-content/plan-setup-auth.js.map +1 -1
  296. package/dist/cli/skills-content/visual-plan-skill.d.ts +1 -1
  297. package/dist/cli/skills-content/visual-plan-skill.d.ts.map +1 -1
  298. package/dist/cli/skills-content/visual-plan-skill.js +1 -1
  299. package/dist/cli/skills-content/visual-plan-skill.js.map +1 -1
  300. package/dist/cli/skills-content/visual-recap-skill.d.ts +1 -1
  301. package/dist/cli/skills-content/visual-recap-skill.d.ts.map +1 -1
  302. package/dist/cli/skills-content/visual-recap-skill.js +2 -2
  303. package/dist/cli/skills-content/visual-recap-skill.js.map +1 -1
  304. package/dist/cli/skills.d.ts.map +1 -1
  305. package/dist/cli/skills.js +16 -12
  306. package/dist/cli/skills.js.map +1 -1
  307. package/dist/client/resources/McpIntegrationDialog.d.ts.map +1 -1
  308. package/dist/client/resources/McpIntegrationDialog.js +1 -1
  309. package/dist/client/resources/McpIntegrationDialog.js.map +1 -1
  310. package/dist/client/resources/mcp-integration-catalog.d.ts +1 -0
  311. package/dist/client/resources/mcp-integration-catalog.d.ts.map +1 -1
  312. package/dist/client/resources/mcp-integration-catalog.js +14 -0
  313. package/dist/client/resources/mcp-integration-catalog.js.map +1 -1
  314. package/dist/collab/routes.d.ts +2 -2
  315. package/dist/collab/struct-routes.d.ts +1 -1
  316. package/dist/embedding/agent.d.ts.map +1 -1
  317. package/dist/embedding/agent.js +6 -7
  318. package/dist/embedding/agent.js.map +1 -1
  319. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  320. package/dist/localization/default-messages.d.ts +5 -0
  321. package/dist/localization/default-messages.d.ts.map +1 -1
  322. package/dist/localization/default-messages.js +5 -0
  323. package/dist/localization/default-messages.js.map +1 -1
  324. package/dist/mcp/ask-app-inline-tasks.d.ts +47 -0
  325. package/dist/mcp/ask-app-inline-tasks.d.ts.map +1 -0
  326. package/dist/mcp/ask-app-inline-tasks.js +87 -0
  327. package/dist/mcp/ask-app-inline-tasks.js.map +1 -0
  328. package/dist/mcp/build-server.d.ts.map +1 -1
  329. package/dist/mcp/build-server.js +56 -3
  330. package/dist/mcp/build-server.js.map +1 -1
  331. package/dist/mcp/builtin-tools.d.ts.map +1 -1
  332. package/dist/mcp/builtin-tools.js +141 -21
  333. package/dist/mcp/builtin-tools.js.map +1 -1
  334. package/dist/mcp/connect-route.d.ts +12 -10
  335. package/dist/mcp/connect-route.d.ts.map +1 -1
  336. package/dist/mcp/connect-route.js +17 -14
  337. package/dist/mcp/connect-route.js.map +1 -1
  338. package/dist/mcp/oauth-route.d.ts +1 -1
  339. package/dist/mcp/oauth-route.d.ts.map +1 -1
  340. package/dist/mcp/oauth-route.js +30 -13
  341. package/dist/mcp/oauth-route.js.map +1 -1
  342. package/dist/mcp/route-paths.d.ts +13 -0
  343. package/dist/mcp/route-paths.d.ts.map +1 -0
  344. package/dist/mcp/route-paths.js +27 -0
  345. package/dist/mcp/route-paths.js.map +1 -0
  346. package/dist/mcp/server.d.ts +2 -1
  347. package/dist/mcp/server.d.ts.map +1 -1
  348. package/dist/mcp/server.js +14 -6
  349. package/dist/mcp/server.js.map +1 -1
  350. package/dist/mcp/stdio.d.ts +7 -6
  351. package/dist/mcp/stdio.d.ts.map +1 -1
  352. package/dist/mcp/stdio.js +27 -15
  353. package/dist/mcp/stdio.js.map +1 -1
  354. package/dist/mcp/workspace-resolve.d.ts +2 -1
  355. package/dist/mcp/workspace-resolve.d.ts.map +1 -1
  356. package/dist/mcp/workspace-resolve.js +2 -1
  357. package/dist/mcp/workspace-resolve.js.map +1 -1
  358. package/dist/mcp-client/manager.js +6 -3
  359. package/dist/mcp-client/manager.js.map +1 -1
  360. package/dist/mcp-client/remote-store.js +5 -7
  361. package/dist/mcp-client/remote-store.js.map +1 -1
  362. package/dist/notifications/routes.d.ts +3 -3
  363. package/dist/observability/routes.d.ts +5 -5
  364. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  365. package/dist/resources/handlers.d.ts +2 -2
  366. package/dist/secrets/routes.d.ts +9 -9
  367. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  368. package/dist/server/agent-chat-plugin.js +43 -11
  369. package/dist/server/agent-chat-plugin.js.map +1 -1
  370. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  371. package/dist/server/auth.d.ts.map +1 -1
  372. package/dist/server/auth.js +16 -6
  373. package/dist/server/auth.js.map +1 -1
  374. package/dist/server/core-routes-plugin.d.ts +4 -3
  375. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  376. package/dist/server/core-routes-plugin.js +20 -15
  377. package/dist/server/core-routes-plugin.js.map +1 -1
  378. package/dist/server/csrf.d.ts.map +1 -1
  379. package/dist/server/csrf.js +11 -0
  380. package/dist/server/csrf.js.map +1 -1
  381. package/dist/server/framework-request-handler.d.ts.map +1 -1
  382. package/dist/server/framework-request-handler.js +8 -2
  383. package/dist/server/framework-request-handler.js.map +1 -1
  384. package/dist/server/google-oauth.d.ts +2 -0
  385. package/dist/server/google-oauth.d.ts.map +1 -1
  386. package/dist/server/google-oauth.js +5 -0
  387. package/dist/server/google-oauth.js.map +1 -1
  388. package/dist/server/http-response-telemetry.d.ts.map +1 -1
  389. package/dist/server/http-response-telemetry.js +4 -1
  390. package/dist/server/http-response-telemetry.js.map +1 -1
  391. package/dist/server/recap-image-route.js +3 -3
  392. package/dist/server/recap-image-route.js.map +1 -1
  393. package/dist/server/security-headers.d.ts.map +1 -1
  394. package/dist/server/security-headers.js +4 -1
  395. package/dist/server/security-headers.js.map +1 -1
  396. package/dist/server/ssr-handler.d.ts.map +1 -1
  397. package/dist/server/ssr-handler.js +3 -1
  398. package/dist/server/ssr-handler.js.map +1 -1
  399. package/dist/shared/cache-control.d.ts +7 -6
  400. package/dist/shared/cache-control.d.ts.map +1 -1
  401. package/dist/shared/cache-control.js +4 -3
  402. package/dist/shared/cache-control.js.map +1 -1
  403. package/dist/templates/default/.agents/skills/security/SKILL.md +10 -0
  404. package/dist/templates/workspace-core/.agents/skills/authentication/SKILL.md +15 -6
  405. package/dist/templates/workspace-core/.agents/skills/external-agents/SKILL.md +16 -10
  406. package/dist/templates/workspace-core/.agents/skills/performance/SKILL.md +20 -1
  407. package/dist/templates/workspace-core/.agents/skills/security/SKILL.md +10 -0
  408. package/docs/content/actions.mdx +1 -1
  409. package/docs/content/authentication.mdx +1 -1
  410. package/docs/content/dispatch.mdx +3 -3
  411. package/docs/content/external-agents.mdx +30 -19
  412. package/docs/content/locales/ar-SA/actions.mdx +1 -1
  413. package/docs/content/locales/ar-SA/authentication.mdx +1 -1
  414. package/docs/content/locales/ar-SA/dispatch.mdx +3 -3
  415. package/docs/content/locales/ar-SA/external-agents.mdx +23 -19
  416. package/docs/content/locales/ar-SA/mcp-clients.mdx +31 -3
  417. package/docs/content/locales/ar-SA/mcp-protocol.mdx +29 -15
  418. package/docs/content/locales/ar-SA/plan-plugin.mdx +3 -3
  419. package/docs/content/locales/ar-SA/server.mdx +2 -2
  420. package/docs/content/locales/ar-SA/skills-guide.mdx +1 -1
  421. package/docs/content/locales/ar-SA/template-assets.mdx +1 -1
  422. package/docs/content/locales/ar-SA/template-dispatch.mdx +2 -2
  423. package/docs/content/locales/ar-SA/template-plan.mdx +3 -3
  424. package/docs/content/locales/de-DE/actions.mdx +1 -1
  425. package/docs/content/locales/de-DE/authentication.mdx +1 -1
  426. package/docs/content/locales/de-DE/dispatch.mdx +3 -3
  427. package/docs/content/locales/de-DE/external-agents.mdx +23 -19
  428. package/docs/content/locales/de-DE/mcp-clients.mdx +37 -3
  429. package/docs/content/locales/de-DE/mcp-protocol.mdx +29 -15
  430. package/docs/content/locales/de-DE/plan-plugin.mdx +3 -3
  431. package/docs/content/locales/de-DE/server.mdx +2 -2
  432. package/docs/content/locales/de-DE/skills-guide.mdx +1 -1
  433. package/docs/content/locales/de-DE/template-assets.mdx +1 -1
  434. package/docs/content/locales/de-DE/template-dispatch.mdx +2 -2
  435. package/docs/content/locales/de-DE/template-plan.mdx +3 -3
  436. package/docs/content/locales/es-ES/actions.mdx +1 -1
  437. package/docs/content/locales/es-ES/authentication.mdx +1 -1
  438. package/docs/content/locales/es-ES/dispatch.mdx +3 -3
  439. package/docs/content/locales/es-ES/external-agents.mdx +32 -19
  440. package/docs/content/locales/es-ES/mcp-clients.mdx +17 -3
  441. package/docs/content/locales/es-ES/mcp-protocol.mdx +38 -15
  442. package/docs/content/locales/es-ES/plan-plugin.mdx +3 -3
  443. package/docs/content/locales/es-ES/server.mdx +2 -2
  444. package/docs/content/locales/es-ES/skills-guide.mdx +1 -1
  445. package/docs/content/locales/es-ES/template-assets.mdx +1 -1
  446. package/docs/content/locales/es-ES/template-dispatch.mdx +2 -2
  447. package/docs/content/locales/es-ES/template-plan.mdx +3 -3
  448. package/docs/content/locales/fr-FR/actions.mdx +1 -1
  449. package/docs/content/locales/fr-FR/authentication.mdx +1 -1
  450. package/docs/content/locales/fr-FR/dispatch.mdx +3 -3
  451. package/docs/content/locales/fr-FR/external-agents.mdx +23 -19
  452. package/docs/content/locales/fr-FR/mcp-clients.mdx +17 -3
  453. package/docs/content/locales/fr-FR/mcp-protocol.mdx +29 -15
  454. package/docs/content/locales/fr-FR/plan-plugin.mdx +3 -3
  455. package/docs/content/locales/fr-FR/server.mdx +2 -2
  456. package/docs/content/locales/fr-FR/skills-guide.mdx +1 -1
  457. package/docs/content/locales/fr-FR/template-assets.mdx +1 -1
  458. package/docs/content/locales/fr-FR/template-dispatch.mdx +2 -2
  459. package/docs/content/locales/fr-FR/template-plan.mdx +3 -3
  460. package/docs/content/locales/hi-IN/actions.mdx +1 -1
  461. package/docs/content/locales/hi-IN/authentication.mdx +1 -1
  462. package/docs/content/locales/hi-IN/dispatch.mdx +3 -3
  463. package/docs/content/locales/hi-IN/external-agents.mdx +31 -19
  464. package/docs/content/locales/hi-IN/mcp-clients.mdx +17 -3
  465. package/docs/content/locales/hi-IN/mcp-protocol.mdx +36 -15
  466. package/docs/content/locales/hi-IN/plan-plugin.mdx +3 -3
  467. package/docs/content/locales/hi-IN/server.mdx +2 -2
  468. package/docs/content/locales/hi-IN/skills-guide.mdx +1 -1
  469. package/docs/content/locales/hi-IN/template-assets.mdx +1 -1
  470. package/docs/content/locales/hi-IN/template-dispatch.mdx +2 -2
  471. package/docs/content/locales/hi-IN/template-plan.mdx +3 -3
  472. package/docs/content/locales/ja-JP/actions.mdx +1 -1
  473. package/docs/content/locales/ja-JP/authentication.mdx +1 -1
  474. package/docs/content/locales/ja-JP/dispatch.mdx +3 -3
  475. package/docs/content/locales/ja-JP/external-agents.mdx +23 -19
  476. package/docs/content/locales/ja-JP/mcp-clients.mdx +34 -3
  477. package/docs/content/locales/ja-JP/mcp-protocol.mdx +29 -15
  478. package/docs/content/locales/ja-JP/plan-plugin.mdx +3 -3
  479. package/docs/content/locales/ja-JP/server.mdx +2 -2
  480. package/docs/content/locales/ja-JP/skills-guide.mdx +1 -1
  481. package/docs/content/locales/ja-JP/template-assets.mdx +1 -1
  482. package/docs/content/locales/ja-JP/template-dispatch.mdx +2 -2
  483. package/docs/content/locales/ja-JP/template-plan.mdx +3 -3
  484. package/docs/content/locales/ko-KR/actions.mdx +1 -1
  485. package/docs/content/locales/ko-KR/authentication.mdx +1 -1
  486. package/docs/content/locales/ko-KR/dispatch.mdx +3 -3
  487. package/docs/content/locales/ko-KR/external-agents.mdx +30 -19
  488. package/docs/content/locales/ko-KR/mcp-clients.mdx +33 -3
  489. package/docs/content/locales/ko-KR/mcp-protocol.mdx +36 -15
  490. package/docs/content/locales/ko-KR/plan-plugin.mdx +3 -3
  491. package/docs/content/locales/ko-KR/server.mdx +2 -2
  492. package/docs/content/locales/ko-KR/skills-guide.mdx +1 -1
  493. package/docs/content/locales/ko-KR/template-assets.mdx +1 -1
  494. package/docs/content/locales/ko-KR/template-dispatch.mdx +2 -2
  495. package/docs/content/locales/ko-KR/template-plan.mdx +3 -3
  496. package/docs/content/locales/pt-BR/actions.mdx +1 -1
  497. package/docs/content/locales/pt-BR/authentication.mdx +1 -1
  498. package/docs/content/locales/pt-BR/dispatch.mdx +3 -3
  499. package/docs/content/locales/pt-BR/external-agents.mdx +32 -19
  500. package/docs/content/locales/pt-BR/mcp-clients.mdx +34 -3
  501. package/docs/content/locales/pt-BR/mcp-protocol.mdx +32 -15
  502. package/docs/content/locales/pt-BR/plan-plugin.mdx +3 -3
  503. package/docs/content/locales/pt-BR/server.mdx +2 -2
  504. package/docs/content/locales/pt-BR/skills-guide.mdx +1 -1
  505. package/docs/content/locales/pt-BR/template-assets.mdx +1 -1
  506. package/docs/content/locales/pt-BR/template-dispatch.mdx +2 -2
  507. package/docs/content/locales/pt-BR/template-plan.mdx +3 -3
  508. package/docs/content/locales/zh-CN/actions.mdx +1 -1
  509. package/docs/content/locales/zh-CN/authentication.mdx +1 -1
  510. package/docs/content/locales/zh-CN/dispatch.mdx +3 -3
  511. package/docs/content/locales/zh-CN/external-agents.mdx +23 -19
  512. package/docs/content/locales/zh-CN/mcp-clients.mdx +28 -3
  513. package/docs/content/locales/zh-CN/mcp-protocol.mdx +29 -15
  514. package/docs/content/locales/zh-CN/plan-plugin.mdx +3 -3
  515. package/docs/content/locales/zh-CN/server.mdx +2 -2
  516. package/docs/content/locales/zh-CN/skills-guide.mdx +1 -1
  517. package/docs/content/locales/zh-CN/template-assets.mdx +1 -1
  518. package/docs/content/locales/zh-CN/template-dispatch.mdx +2 -2
  519. package/docs/content/locales/zh-CN/template-plan.mdx +3 -3
  520. package/docs/content/locales/zh-TW/actions.mdx +1 -1
  521. package/docs/content/locales/zh-TW/authentication.mdx +1 -1
  522. package/docs/content/locales/zh-TW/dispatch.mdx +3 -3
  523. package/docs/content/locales/zh-TW/external-agents.mdx +23 -19
  524. package/docs/content/locales/zh-TW/mcp-clients.mdx +28 -3
  525. package/docs/content/locales/zh-TW/mcp-protocol.mdx +30 -15
  526. package/docs/content/locales/zh-TW/plan-plugin.mdx +3 -3
  527. package/docs/content/locales/zh-TW/server.mdx +2 -2
  528. package/docs/content/locales/zh-TW/skills-guide.mdx +1 -1
  529. package/docs/content/locales/zh-TW/template-assets.mdx +1 -1
  530. package/docs/content/locales/zh-TW/template-dispatch.mdx +2 -2
  531. package/docs/content/locales/zh-TW/template-plan.mdx +3 -3
  532. package/docs/content/mcp-clients.mdx +31 -4
  533. package/docs/content/mcp-protocol.mdx +35 -15
  534. package/docs/content/plan-plugin.mdx +3 -3
  535. package/docs/content/server.mdx +2 -2
  536. package/docs/content/skills-guide.mdx +1 -1
  537. package/docs/content/template-assets.mdx +1 -1
  538. package/docs/content/template-dispatch.mdx +2 -2
  539. package/docs/content/template-plan.mdx +3 -3
  540. package/package.json +1 -1
  541. package/src/templates/default/.agents/skills/security/SKILL.md +10 -0
  542. package/src/templates/workspace-core/.agents/skills/authentication/SKILL.md +15 -6
  543. package/src/templates/workspace-core/.agents/skills/external-agents/SKILL.md +16 -10
  544. package/src/templates/workspace-core/.agents/skills/performance/SKILL.md +20 -1
  545. package/src/templates/workspace-core/.agents/skills/security/SKILL.md +10 -0
@@ -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
  }
@@ -161,9 +161,7 @@ export default defineEventHandler(async (event) => {
161
161
  const headers = new Headers(response.headers);
162
162
  headers.delete("content-length");
163
163
  if (discovery.privateResponse) {
164
- headers.set("Cache-Control", "private, max-age=0, no-store");
165
164
  headers.set("Referrer-Policy", "no-referrer");
166
- setResponseHeader(event, "Cache-Control", "private, max-age=0, no-store");
167
165
  setResponseHeader(event, "Referrer-Policy", "no-referrer");
168
166
  }
169
167
 
@@ -147,6 +147,16 @@ export default defineEventHandler(async (event) => {
147
147
 
148
148
  - Never create unprotected routes that modify data.
149
149
 
150
+ **Exception — the SSR HTML/`.data` catch-all is deliberately session-blind.**
151
+ The rule above is for routes that read or mutate user data. The SSR page
152
+ render and React Router `.data` route are different: they serve one
153
+ impersonal, public-cacheable shell to every visitor by design, and loaders on
154
+ that path render no user data. Do not "fix" this by adding `getSession`,
155
+ `private`, or `no-store` to it — that regresses the CDN cache contract for the
156
+ whole site. Data scoping lives in actions and API routes; the client gates
157
+ private UI after the shell loads. See the `authentication` skill and
158
+ `guard:ssr-cache-shell` / `ssr-handler.spec.ts`.
159
+
150
160
  ## Human-in-the-Loop Approval for High-Consequence Actions
151
161
 
152
162
  For a small set of outward-facing, hard-to-undo operations — sending an email, charging a card, deleting an account, posting publicly — auth and access control are necessary but not sufficient: you also do not want the **agent** to perform them autonomously. Set `needsApproval` on the `defineAction` so the agent cannot run the action without a human approving the specific call.
@@ -5,7 +5,7 @@
5
5
  "description": "Create, search, select, and export brand image and video assets from the Assets app.",
6
6
  "hosted": {
7
7
  "url": "https://assets.agent-native.com",
8
- "mcpUrl": "https://assets.agent-native.com/_agent-native/mcp"
8
+ "mcpUrl": "https://assets.agent-native.com/mcp"
9
9
  },
10
10
  "mcp": {
11
11
  "serverName": "agent-native-assets"
@@ -147,6 +147,16 @@ export default defineEventHandler(async (event) => {
147
147
 
148
148
  - Never create unprotected routes that modify data.
149
149
 
150
+ **Exception — the SSR HTML/`.data` catch-all is deliberately session-blind.**
151
+ The rule above is for routes that read or mutate user data. The SSR page
152
+ render and React Router `.data` route are different: they serve one
153
+ impersonal, public-cacheable shell to every visitor by design, and loaders on
154
+ that path render no user data. Do not "fix" this by adding `getSession`,
155
+ `private`, or `no-store` to it — that regresses the CDN cache contract for the
156
+ whole site. Data scoping lives in actions and API routes; the client gates
157
+ private UI after the shell loads. See the `authentication` skill and
158
+ `guard:ssr-cache-shell` / `ssr-handler.spec.ts`.
159
+
150
160
  ## Human-in-the-Loop Approval for High-Consequence Actions
151
161
 
152
162
  For a small set of outward-facing, hard-to-undo operations — sending an email, charging a card, deleting an account, posting publicly — auth and access control are necessary but not sufficient: you also do not want the **agent** to perform them autonomously. Set `needsApproval` on the `defineAction` so the agent cannot run the action without a human approving the specific call.
@@ -147,6 +147,16 @@ export default defineEventHandler(async (event) => {
147
147
 
148
148
  - Never create unprotected routes that modify data.
149
149
 
150
+ **Exception — the SSR HTML/`.data` catch-all is deliberately session-blind.**
151
+ The rule above is for routes that read or mutate user data. The SSR page
152
+ render and React Router `.data` route are different: they serve one
153
+ impersonal, public-cacheable shell to every visitor by design, and loaders on
154
+ that path render no user data. Do not "fix" this by adding `getSession`,
155
+ `private`, or `no-store` to it — that regresses the CDN cache contract for the
156
+ whole site. Data scoping lives in actions and API routes; the client gates
157
+ private UI after the shell loads. See the `authentication` skill and
158
+ `guard:ssr-cache-shell` / `ssr-handler.spec.ts`.
159
+
150
160
  ## Human-in-the-Loop Approval for High-Consequence Actions
151
161
 
152
162
  For a small set of outward-facing, hard-to-undo operations — sending an email, charging a card, deleting an account, posting publicly — auth and access control are necessary but not sufficient: you also do not want the **agent** to perform them autonomously. Set `needsApproval` on the `defineAction` so the agent cannot run the action without a human approving the specific call.
@@ -67,7 +67,8 @@ Detailed event, availability, booking, storage, and UI rules live in
67
67
  - Use `get-attendee-timezones` / `set-attendee-timezone` to read or save
68
68
  per-guest IANA timezone overrides (`attendee-timezones` user setting). The UI
69
69
  shows each guest's local event-start time when a timezone is known (self from
70
- the event/browser zone; others from `attendee.timeZone` or the override map).
70
+ the browser zone; others from `attendee.timeZone` or the override map, with
71
+ the event zone as a fallback for the organizer).
71
72
  - Use `rsvp-event` for invitation responses. Pass `note` when the user wants a
72
73
  visible RSVP comment on a declined or tentative response; pass an empty note to
73
74
  clear an existing RSVP comment.
@@ -11,7 +11,6 @@ import {
11
11
  IconMapPin,
12
12
  IconUser,
13
13
  IconVideo,
14
- IconGlobe,
15
14
  IconRefresh,
16
15
  IconBell,
17
16
  IconChevronRight,
@@ -86,7 +85,6 @@ import {
86
85
  dateTimeInTimezoneToIso,
87
86
  formatRecurrenceText,
88
87
  formatReminderText,
89
- formatTimezoneLabel,
90
88
  getEventEndValidationMessage,
91
89
  getLocalTimezone,
92
90
  getRecurrencePreset,
@@ -1274,17 +1272,6 @@ export function EventDetailPopover({
1274
1272
  ? t("eventForm.loadingRepeat")
1275
1273
  : formatRecurrenceText(recurrenceRules) ||
1276
1274
  (isRecurringEvent ? t("eventForm.repeats") : null);
1277
- // Show the browser's local timezone offset (this is what the user sees times in)
1278
- const localOffsetMinutes = -new Date().getTimezoneOffset();
1279
- const localOffsetSign = localOffsetMinutes >= 0 ? "+" : "-";
1280
- const localOffsetH = Math.floor(Math.abs(localOffsetMinutes) / 60);
1281
- const localOffsetM = Math.abs(localOffsetMinutes) % 60;
1282
- const tzLabel = event.startTimeZone
1283
- ? formatTimezoneLabel(event.startTimeZone)
1284
- : localOffsetM
1285
- ? `GMT${localOffsetSign}${localOffsetH}:${String(localOffsetM).padStart(2, "0")}`
1286
- : `GMT${localOffsetSign}${localOffsetH}`;
1287
-
1288
1275
  const handleOpenChange = useCallback(
1289
1276
  (newOpen: boolean) => {
1290
1277
  if (!newOpen && open) {
@@ -1726,12 +1713,6 @@ export function EventDetailPopover({
1726
1713
  />
1727
1714
  )}
1728
1715
 
1729
- {/* Timezone */}
1730
- <div className="flex items-center gap-3 py-1.5">
1731
- <IconGlobe className="h-4 w-4 shrink-0 text-muted-foreground" />
1732
- <span className="text-sm text-muted-foreground">{tzLabel}</span>
1733
- </div>
1734
-
1735
1716
  {/* Recurrence */}
1736
1717
  {editingField === "recurrence" ? (
1737
1718
  <div className="flex items-start gap-3 py-1.5">