@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,9 +3,12 @@
3
3
  *
4
4
  * Resolution priority for an attendee's IANA timezone:
5
5
  * 1. Self attendee (`attendee.self` or email matches accountEmail) →
6
- * event.startTimeZone, else the browser timezone
6
+ * the browser timezone, so an event owner's timezone is never shown as
7
+ * the signed-in user's local time
7
8
  * 2. Optional `attendee.timeZone` on the attendee object
8
9
  * 3. User-stored override from the `attendee-timezones` setting map
10
+ * 4. The event start timezone for the organizer when no more specific zone is
11
+ * available
9
12
  *
10
13
  * When no timezone is known, returns null — never invent a zone.
11
14
  */
@@ -13,6 +16,7 @@
13
16
  export type AttendeeTimezoneSource = {
14
17
  email: string;
15
18
  self?: boolean;
19
+ organizer?: boolean;
16
20
  timeZone?: string;
17
21
  };
18
22
 
@@ -61,15 +65,16 @@ export function resolveAttendeeTimeZone(
61
65
  } = input;
62
66
 
63
67
  if (isSelfAttendee(attendee, accountEmail)) {
64
- if (isValidIanaTimeZone(eventStartTimeZone)) {
65
- return eventStartTimeZone!.trim();
66
- }
67
68
  const browser =
68
69
  browserTimeZone?.trim() ||
69
70
  (typeof Intl !== "undefined"
70
71
  ? Intl.DateTimeFormat().resolvedOptions().timeZone
71
72
  : undefined);
72
- return isValidIanaTimeZone(browser) ? browser!.trim() : null;
73
+ if (isValidIanaTimeZone(browser)) return browser!.trim();
74
+
75
+ return isValidIanaTimeZone(eventStartTimeZone)
76
+ ? eventStartTimeZone!.trim()
77
+ : null;
73
78
  }
74
79
 
75
80
  if (isValidIanaTimeZone(attendee.timeZone)) {
@@ -82,6 +87,10 @@ export function resolveAttendeeTimeZone(
82
87
  return override!.trim();
83
88
  }
84
89
 
90
+ if (attendee.organizer && isValidIanaTimeZone(eventStartTimeZone)) {
91
+ return eventStartTimeZone!.trim();
92
+ }
93
+
85
94
  return null;
86
95
  }
87
96
 
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-13
4
+ ---
5
+
6
+ Your local time now follows your browser timezone, and event details no longer show a redundant timezone row.
@@ -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.
@@ -44,7 +44,7 @@ Raw `MediaRecorder` files are not friendly to progressive HTTP playback, which
44
44
  shows up as "clip takes minutes to load" and "re-buffers every time I seek"
45
45
  even though the file downloads fine:
46
46
 
47
- - **MP4** is written with the `moov` metadata atom *after* `mdat`, so a player
47
+ - **MP4** is written with the `moov` metadata atom _after_ `mdat`, so a player
48
48
  must fetch the whole file before it can start or seek.
49
49
  - **WebM** is a live stream with no Cues (seek index) and an unknown Segment
50
50
  duration, so Chrome won't honor `currentTime = X` and has to scan/download.
@@ -116,7 +116,12 @@ different listing.
116
116
 
117
117
  ## Camera bubble
118
118
 
119
- When mode is `screen+camera`, we composite a circular camera feed in the corner. Render the bubble in a separate `<video>` element and record it into a second `MediaRecorder`; the server side stitches them with ffmpeg.wasm during `processing`. Do **not** try to pre-composite in the browser — that burns GPU and drops frames.
119
+ When mode is `screen+camera`, "the bubble" is two different things:
120
+
121
+ - **On-screen self-view.** `CameraBubble` renders a plain, unrecorded `<video>` element the user drags around to frame themselves during countdown/recording. It is never captured — it's just local framing UI.
122
+ - **Recorded composite.** Display capture can't include that DOM element once the user records another window/app, so the saved video has to bake the camera circle in before `MediaRecorder` ever sees a frame. `createCameraCompositeStream` (`app/lib/camera-composite.ts`) draws the display video plus a clipped, mirrored camera circle onto an offscreen `<canvas>` and feeds `canvas.captureStream()` into a single `MediaRecorder`. There is **no** second `MediaRecorder` and **no** server-side ffmpeg stitching — the composite happens client-side, live, before upload.
123
+
124
+ Because that draw loop runs continuously for the whole recording, keep its CPU/GPU cost bounded: a Worker-based timer drives the draw loop at the capture frame rate (`SCREEN_CAPTURE_FRAME_RATE`, 24fps — falling back to `requestAnimationFrame` only if Worker creation fails, e.g. under a strict CSP), the canvas is hard-capped to 1080p-class dimensions (1920px on its longest edge) even if the source display track is Retina/4K, and the bubble's drop shadow is pre-rendered into a small cached sprite (keyed by bubble size) instead of re-blurring with `shadowBlur` every frame.
120
125
 
121
126
  ## Error recovery
122
127
 
@@ -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.
@@ -36,6 +36,9 @@ Detailed media, meeting, dictation, editing, and sharing rules live in
36
36
  generation run in the durable post-finalize path; do not hide a usable native
37
37
  transcript behind failed metadata work, and keep heuristic titles replaceable
38
38
  until the agent refinement lands.
39
+ - Desktop native transcripts merge microphone and system-audio streams while
40
+ removing overlapping duplicate speech and low-speech Whisper hallucinations;
41
+ keep system audio enabled when the meeting audio comes from another app.
39
42
  - Use `request-transcript --recordingId=<id> --force=true` to retry a failed
40
43
  transcript. Pass `--regenerate=true` to replace an existing ready transcript
41
44
  from the stored recording media; if regeneration fails, keep the prior ready
@@ -95,6 +98,10 @@ Detailed media, meeting, dictation, editing, and sharing rules live in
95
98
  (`connect-slack`, `/api/slack/oauth/callback`) so each Slack workspace gets
96
99
  its own encrypted bot token in `app_secrets`. `SLACK_BOT_TOKEN` is only a
97
100
  legacy single-workspace fallback and must remain behind the team allowlist.
101
+ - Atlassian/Jira is available through the shared MCP integration catalog. It
102
+ uses Atlassian Rovo MCP OAuth; explain that an Atlassian organization admin
103
+ may need to allow the Clips app domain and enable the required Read, Write,
104
+ and Search permissions before the connection can complete.
98
105
  - Browser recordings can include redacted browser diagnostics captured during
99
106
  the recording session. `save-browser-diagnostics` is UI/internal and stores
100
107
  bounded console logs plus fetch/XHR method, URL path/query keys, status, and
@@ -14,6 +14,7 @@
14
14
  */
15
15
 
16
16
  import { defineAction } from "@agent-native/core";
17
+ import { getRequestUserEmail } from "@agent-native/core/server/request-context";
17
18
  import { accessFilter } from "@agent-native/core/sharing";
18
19
  import {
19
20
  and,
@@ -31,7 +32,7 @@ import {
31
32
  import { z } from "zod";
32
33
 
33
34
  import { getDb, schema } from "../server/db/index.js";
34
- import { isPersonalSoloCalendarEvent } from "../server/lib/calendar-event-classification.js";
35
+ import { isSoloCalendarEvent } from "../server/lib/calendar-event-classification.js";
35
36
  import {
36
37
  calendarEventToMeetingView,
37
38
  eventEndIso,
@@ -86,12 +87,13 @@ export default defineAction({
86
87
  excludePersonalSoloEvents: booleanParam
87
88
  .default(false)
88
89
  .describe(
89
- "Exclude obvious solo personal calendar blocks such as Gym or Dinner. Used by desktop meeting reminders.",
90
+ "Exclude calendar events with no active attendee besides the current user. Used by desktop meeting reminders.",
90
91
  ),
91
92
  }),
92
93
  http: { method: "GET" },
93
94
  run: async (args) => {
94
95
  const db = getDb();
96
+ const currentUserEmail = getRequestUserEmail();
95
97
  const now = new Date();
96
98
  const nowIso = now.toISOString();
97
99
 
@@ -188,6 +190,11 @@ export default defineAction({
188
190
  // event was emitted here — not merely because some other account returned
189
191
  // data or errored.
190
192
  const emittedLiveEventKeys = new Set<string>();
193
+ // Calendar events excluded from desktop reminders because they have no
194
+ // active attendee besides the current user. Keep the correlated
195
+ // persisted meeting ids here too, so materialized solo events cannot
196
+ // re-enter the reminder list through the fallback persisted-row merge.
197
+ const excludedLiveEventKeys = new Set<string>();
191
198
  // Map a persisted meeting's `calendarEventId` (calendar_events.id) to the
192
199
  // Google event externalId so we can match it against the emitted set.
193
200
  const calendarEventIdToExternalId = new Map<string, string>();
@@ -269,10 +276,15 @@ export default defineAction({
269
276
  for (const event of items) {
270
277
  if (!event.id || event.status === "cancelled") continue;
271
278
  if (!isTimedCalendarEvent(event)) continue;
279
+ const cached = cachedByExternalId.get(event.id);
272
280
  if (
273
281
  args.excludePersonalSoloEvents &&
274
- isPersonalSoloCalendarEvent({ account, event })
282
+ isSoloCalendarEvent({ account, event, currentUserEmail })
275
283
  ) {
284
+ excludedLiveEventKeys.add(event.id);
285
+ if (cached?.meetingId) {
286
+ excludedLiveEventKeys.add(cached.meetingId);
287
+ }
276
288
  continue;
277
289
  }
278
290
  const startIso = eventStartIso(event);
@@ -301,7 +313,6 @@ export default defineAction({
301
313
  continue;
302
314
  }
303
315
 
304
- const cached = cachedByExternalId.get(event.id);
305
316
  const persisted = cached?.meetingId
306
317
  ? persistedById.get(cached.meetingId)
307
318
  : null;
@@ -344,6 +355,10 @@ export default defineAction({
344
355
  const liveExternalId = meeting.calendarEventId
345
356
  ? calendarEventIdToExternalId.get(meeting.calendarEventId)
346
357
  : undefined;
358
+ const liveEventExcluded =
359
+ excludedLiveEventKeys.has(meeting.id) ||
360
+ (liveExternalId ? excludedLiveEventKeys.has(liveExternalId) : false);
361
+ if (liveEventExcluded) continue;
347
362
  const liveEventEmitted =
348
363
  emittedLiveEventKeys.has(meeting.id) ||
349
364
  (liveExternalId ? emittedLiveEventKeys.has(liveExternalId) : false);
@@ -71,6 +71,7 @@ export default defineAction({
71
71
  recording: existing,
72
72
  meetingId,
73
73
  created: false,
74
+ scheduledEnd: meeting.scheduledEnd,
74
75
  };
75
76
  }
76
77
  }
@@ -117,6 +118,11 @@ export default defineAction({
117
118
  .where(eq(schema.recordings.id, recordingId))
118
119
  .limit(1);
119
120
 
120
- return { recording, meetingId, created: true };
121
+ return {
122
+ recording,
123
+ meetingId,
124
+ created: true,
125
+ scheduledEnd: meeting.scheduledEnd,
126
+ };
121
127
  },
122
128
  });
@@ -310,92 +310,89 @@ export function TranscriptBubbles({
310
310
  )}
311
311
  </div>
312
312
  <div ref={containerRef} className="flex-1 overflow-y-auto p-4">
313
- <div className="space-y-3">
313
+ <div className="mx-auto max-w-3xl space-y-6">
314
314
  {groups.map((group, gi) => {
315
315
  const isMe = group.source === "mic";
316
316
  return (
317
- <div
318
- key={gi}
319
- className={cn(
320
- "flex flex-col gap-1",
321
- isMe ? "items-end" : "items-start",
322
- )}
323
- >
324
- <div
325
- className={cn(
326
- "text-[10px] font-medium uppercase tracking-wide text-muted-foreground/70 px-1",
327
- )}
328
- >
317
+ <section key={gi} className="space-y-1.5">
318
+ <div className="flex items-center gap-2 px-1 text-[11px] font-medium tracking-wide text-muted-foreground">
319
+ <span
320
+ aria-hidden="true"
321
+ className={cn(
322
+ "h-1.5 w-1.5 rounded-full",
323
+ isMe ? "bg-primary" : "bg-muted-foreground/50",
324
+ )}
325
+ />
329
326
  {isMe
330
327
  ? t("transcriptBubbles.me")
331
328
  : t("transcriptBubbles.them")}
332
329
  </div>
333
- {group.segments.map(({ seg, index }) => {
334
- const clickable = !!recordingId;
335
- return (
336
- <Tooltip key={index}>
337
- <TooltipTrigger asChild>
338
- <div
339
- ref={(el) => {
340
- segmentRefs.current[index] = el;
341
- }}
342
- role={clickable ? "button" : undefined}
343
- tabIndex={clickable ? 0 : -1}
344
- onClick={() => clickable && onSeek(seg.startMs)}
345
- onKeyDown={(e) => {
346
- if (
330
+ <div className="space-y-0.5">
331
+ {group.segments.map(({ seg, index }) => {
332
+ const clickable = !!recordingId;
333
+ return (
334
+ <Tooltip key={index}>
335
+ <TooltipTrigger asChild>
336
+ <div
337
+ ref={(el) => {
338
+ segmentRefs.current[index] = el;
339
+ }}
340
+ role={clickable ? "button" : undefined}
341
+ tabIndex={clickable ? 0 : -1}
342
+ onClick={() => clickable && onSeek(seg.startMs)}
343
+ onKeyDown={(e) => {
344
+ if (
345
+ clickable &&
346
+ (e.key === "Enter" || e.key === " ")
347
+ ) {
348
+ e.preventDefault();
349
+ onSeek(seg.startMs);
350
+ }
351
+ }}
352
+ className={cn(
353
+ "group/segment -mx-1 rounded-md px-1 py-1 text-left text-sm leading-relaxed text-foreground transition-colors",
347
354
  clickable &&
348
- (e.key === "Enter" || e.key === " ")
349
- ) {
350
- e.preventDefault();
351
- onSeek(seg.startMs);
352
- }
353
- }}
354
- className={cn(
355
- "max-w-[80%] rounded-2xl px-3.5 py-2 text-sm leading-relaxed transition-shadow",
356
- isMe
357
- ? "bg-primary text-primary-foreground"
358
- : "bg-muted text-foreground",
359
- clickable && "cursor-pointer hover:opacity-90",
360
- )}
361
- >
362
- {seg.speaker && !isMe && (
363
- <div className="text-[10px] font-medium opacity-70 mb-0.5">
364
- {seg.speaker}
365
- </div>
366
- )}
367
- <p className="whitespace-pre-wrap">
368
- {normalizedQuery
369
- ? highlightRuns(seg.text, normalizedQuery).map(
370
- (run, ri) =>
371
- run.match ? (
372
- <mark
373
- key={ri}
374
- className={cn(
375
- "rounded-sm bg-yellow-400/70 text-foreground",
376
- index === activeMatchIndex &&
377
- "bg-yellow-400 ring-1 ring-yellow-600",
378
- )}
379
- >
380
- {run.text}
381
- </mark>
382
- ) : (
383
- <span key={ri}>{run.text}</span>
384
- ),
385
- )
386
- : seg.text}
387
- </p>
388
- </div>
389
- </TooltipTrigger>
390
- <TooltipContent side={isMe ? "left" : "right"}>
391
- <span className="font-mono tabular-nums text-[11px]">
392
- {formatTimestamp(seg.startMs)}
393
- </span>
394
- </TooltipContent>
395
- </Tooltip>
396
- );
397
- })}
398
- </div>
355
+ "cursor-pointer hover:bg-accent/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
356
+ )}
357
+ >
358
+ {seg.speaker && !isMe && (
359
+ <span className="me-2 text-[11px] font-medium text-muted-foreground">
360
+ {seg.speaker}
361
+ </span>
362
+ )}
363
+ <span className="whitespace-pre-wrap">
364
+ {normalizedQuery
365
+ ? highlightRuns(seg.text, normalizedQuery).map(
366
+ (run, ri) =>
367
+ run.match ? (
368
+ <mark
369
+ key={ri}
370
+ className={cn(
371
+ "rounded-sm bg-yellow-400/70 text-foreground",
372
+ index === activeMatchIndex &&
373
+ "bg-yellow-400 ring-1 ring-yellow-600",
374
+ )}
375
+ >
376
+ {run.text}
377
+ </mark>
378
+ ) : (
379
+ <span key={ri}>{run.text}</span>
380
+ ),
381
+ )
382
+ : seg.text}
383
+ </span>
384
+ </div>
385
+ </TooltipTrigger>
386
+ <TooltipContent side="right">
387
+ <span className="font-mono tabular-nums text-[11px]">
388
+ {formatTimestamp(seg.startMs)}
389
+ </span>
390
+ </TooltipContent>
391
+ </Tooltip>
392
+ );
393
+ })}
394
+ </div>
395
+ </section>
399
396
  );
400
397
  })}
401
398
  <div ref={liveEndRef} />
@@ -34,6 +34,7 @@ export const ConfettiCanvas = forwardRef<ConfettiHandle>(
34
34
  const canvasRef = useRef<HTMLCanvasElement>(null);
35
35
  const particlesRef = useRef<Particle[]>([]);
36
36
  const rafRef = useRef<number | null>(null);
37
+ const startTickRef = useRef<() => void>(() => {});
37
38
 
38
39
  useEffect(() => {
39
40
  const canvas = canvasRef.current;
@@ -93,9 +94,24 @@ export const ConfettiCanvas = forwardRef<ConfettiHandle>(
93
94
  live.push(p);
94
95
  }
95
96
  particlesRef.current = live;
97
+
98
+ // Stop instead of redrawing an empty canvas at full refresh rate for
99
+ // the rest of the page's lifetime — burst() restarts the loop.
100
+ if (live.length === 0) {
101
+ rafRef.current = null;
102
+ return;
103
+ }
96
104
  rafRef.current = window.requestAnimationFrame(tick);
97
105
  }
98
- rafRef.current = window.requestAnimationFrame(tick);
106
+
107
+ startTickRef.current = () => {
108
+ if (rafRef.current !== null) return;
109
+ // Reset the dt baseline so the first frame after a restart doesn't
110
+ // see a huge elapsed gap from the idle period.
111
+ lastTs = performance.now();
112
+ rafRef.current = window.requestAnimationFrame(tick);
113
+ };
114
+
99
115
  return () => {
100
116
  if (rafRef.current !== null)
101
117
  window.cancelAnimationFrame(rafRef.current);
@@ -129,6 +145,7 @@ export const ConfettiCanvas = forwardRef<ConfettiHandle>(
129
145
  life: 1.6 + Math.random() * 0.6,
130
146
  });
131
147
  }
148
+ startTickRef.current();
132
149
  },
133
150
  }));
134
151
 
@@ -18,6 +18,10 @@ import {
18
18
  isLikelyPhoneMicLabel,
19
19
  type AudioInputFallback,
20
20
  } from "@shared/media-device-selection";
21
+ import {
22
+ SCREEN_CAPTURE_FRAME_RATE,
23
+ screenCaptureVideoConstraints,
24
+ } from "@shared/recording-capture";
21
25
  import {
22
26
  chunkUploadUrl,
23
27
  pickMimeType,
@@ -234,9 +238,6 @@ const FINAL_CHUNK_UPLOAD_TIMEOUT_MS = 180_000;
234
238
  const RETRYABLE_CHUNK_UPLOAD_STATUSES = new Set([
235
239
  408, 425, 429, 500, 502, 503, 504,
236
240
  ]);
237
- const SCREEN_CAPTURE_FRAME_RATE = 24;
238
- const SCREEN_CAPTURE_MAX_WIDTH = 1920;
239
- const SCREEN_CAPTURE_MAX_HEIGHT = 1080;
240
241
  // Capture quality for the browser MediaRecorder. We no longer shrink files
241
242
  // client-side (ffmpeg.wasm re-encode is disabled — see COMPRESSION_ENABLED in
242
243
  // `@/lib/compress`) and the upload provider streams large files directly, so we
@@ -907,15 +908,7 @@ export class RecorderEngine {
907
908
  this.opts.displaySurface ?? "window",
908
909
  );
909
910
  const displayOptions: ExtendedDisplayMediaOptions = {
910
- video: {
911
- frameRate: {
912
- ideal: SCREEN_CAPTURE_FRAME_RATE,
913
- max: SCREEN_CAPTURE_FRAME_RATE,
914
- },
915
- width: { ideal: SCREEN_CAPTURE_MAX_WIDTH },
916
- height: { ideal: SCREEN_CAPTURE_MAX_HEIGHT },
917
- displaySurface,
918
- },
911
+ video: screenCaptureVideoConstraints(displaySurface),
919
912
  audio: wantsMic,
920
913
  // Let "Browser tab" open the tab picker. preferCurrentTab turns it
921
914
  // into a current-tab shortcut, which makes choosing another tab harder.
@@ -1834,6 +1827,7 @@ export class RecorderEngine {
1834
1827
  displayStream: this.displayStream!,
1835
1828
  cameraStream: this.cameraStream!,
1836
1829
  bubbleSizeRatio: this.cameraBubbleSizeRatio(),
1830
+ frameRate: SCREEN_CAPTURE_FRAME_RATE,
1837
1831
  });
1838
1832
  const combined = new MediaStream();
1839
1833
  for (const t of this.cameraComposite.stream.getVideoTracks())
@@ -16,7 +16,11 @@ import {
16
16
  import { clampRectToViewport, type BubblePosition } from "./camera-positioner";
17
17
 
18
18
  export interface RecordingToolbarProps {
19
- elapsedMs: number;
19
+ /** Whether the elapsed-time ticker should run — true only while actively
20
+ * recording (not during upload/compress, which freeze the last value). */
21
+ active: boolean;
22
+ /** Reads the current elapsed time from the recorder engine on each tick. */
23
+ getElapsedMs: () => number;
20
24
  isPaused: boolean;
21
25
  onTogglePause: () => void;
22
26
  onStop: () => void;
@@ -38,7 +42,8 @@ function formatElapsed(ms: number): string {
38
42
  }
39
43
 
40
44
  export function RecordingToolbar({
41
- elapsedMs,
45
+ active,
46
+ getElapsedMs,
42
47
  isPaused,
43
48
  onTogglePause,
44
49
  onStop,
@@ -46,6 +51,18 @@ export function RecordingToolbar({
46
51
  }: RecordingToolbarProps) {
47
52
  const t = useT();
48
53
  const rootRef = useRef<HTMLDivElement>(null);
54
+ // Own the elapsed-time poll here instead of in the route component, so the
55
+ // 4x/sec tick only re-renders this toolbar rather than the whole record page.
56
+ const [elapsedMs, setElapsedMs] = useState(0);
57
+ const getElapsedMsRef = useRef(getElapsedMs);
58
+ getElapsedMsRef.current = getElapsedMs;
59
+ useEffect(() => {
60
+ if (!active) return;
61
+ const id = window.setInterval(() => {
62
+ setElapsedMs(getElapsedMsRef.current());
63
+ }, 250);
64
+ return () => window.clearInterval(id);
65
+ }, [active]);
49
66
  const [pos, setPos] = useState<BubblePosition>(() =>
50
67
  typeof window === "undefined"
51
68
  ? { left: 16, top: 16, corner: "tl" }
@@ -128,16 +128,79 @@ function drawCover(
128
128
  ctx.restore();
129
129
  }
130
130
 
131
+ // Hard ceiling on the composite canvas regardless of source resolution — keeps
132
+ // the per-frame drawImage + encode at 1080p-class cost even on a Retina/4K
133
+ // display or if a browser ignores getDisplayMedia's `max` constraint.
134
+ const MAX_CANVAS_DIMENSION_PX = 1920;
135
+
136
+ export function clampToMaxDimension(
137
+ width: number,
138
+ height: number,
139
+ ): { width: number; height: number } {
140
+ const longest = Math.max(width, height);
141
+ if (longest <= MAX_CANVAS_DIMENSION_PX) {
142
+ return { width, height };
143
+ }
144
+ const scale = MAX_CANVAS_DIMENSION_PX / longest;
145
+ // Round to even numbers — odd canvas dimensions can trip up video encoders.
146
+ return {
147
+ width: Math.round((width * scale) / 2) * 2,
148
+ height: Math.round((height * scale) / 2) * 2,
149
+ };
150
+ }
151
+
131
152
  function resizeCanvasToDisplay(
132
153
  canvas: HTMLCanvasElement,
133
154
  displayVideo: HTMLVideoElement,
134
155
  displayStream: MediaStream,
135
156
  ): void {
136
- const { width, height } = videoDimensions(displayVideo, displayStream);
157
+ const source = videoDimensions(displayVideo, displayStream);
158
+ const { width, height } = clampToMaxDimension(source.width, source.height);
137
159
  if (canvas.width !== width) canvas.width = width;
138
160
  if (canvas.height !== height) canvas.height = height;
139
161
  }
140
162
 
163
+ interface BubbleShadowSprite {
164
+ size: number;
165
+ canvas: HTMLCanvasElement;
166
+ pad: number;
167
+ }
168
+
169
+ // The drop shadow is a per-frame gaussian blur, which is expensive to redraw
170
+ // every frame. Cache the rendered shadow in an offscreen sprite keyed by
171
+ // bubble size — margin only shifts where the sprite is later placed, not its
172
+ // bitmap — and only re-render when the size actually changes.
173
+ let bubbleShadowSprite: BubbleShadowSprite | null = null;
174
+
175
+ function getBubbleShadowSprite(size: number): BubbleShadowSprite {
176
+ if (bubbleShadowSprite && bubbleShadowSprite.size === size) {
177
+ return bubbleShadowSprite;
178
+ }
179
+ const blur = Math.max(16, size * 0.12);
180
+ const offsetY = Math.max(8, size * 0.05);
181
+ // Padding wide enough that the blurred, offset shadow never clips at the
182
+ // sprite's edge.
183
+ const pad = Math.ceil(blur * 2 + offsetY);
184
+ const spriteSize = size + pad * 2;
185
+ const sprite = document.createElement("canvas");
186
+ sprite.width = spriteSize;
187
+ sprite.height = spriteSize;
188
+ const sctx = sprite.getContext("2d");
189
+ if (sctx) {
190
+ const radius = size / 2;
191
+ const center = pad + radius;
192
+ sctx.shadowColor = "rgba(0, 0, 0, 0.35)";
193
+ sctx.shadowBlur = blur;
194
+ sctx.shadowOffsetY = offsetY;
195
+ sctx.fillStyle = "#000";
196
+ sctx.beginPath();
197
+ sctx.arc(center, center, radius, 0, Math.PI * 2);
198
+ sctx.fill();
199
+ }
200
+ bubbleShadowSprite = { size, canvas: sprite, pad };
201
+ return bubbleShadowSprite;
202
+ }
203
+
141
204
  function drawCameraBubble(
142
205
  ctx: CanvasRenderingContext2D,
143
206
  cameraVideo: HTMLVideoElement,
@@ -172,15 +235,12 @@ function drawCameraBubble(
172
235
  const centerX = x + radius;
173
236
  const centerY = y + radius;
174
237
 
175
- ctx.save();
176
- ctx.shadowColor = "rgba(0, 0, 0, 0.35)";
177
- ctx.shadowBlur = Math.max(16, size * 0.12);
178
- ctx.shadowOffsetY = Math.max(8, size * 0.05);
179
- ctx.fillStyle = "#000";
180
- ctx.beginPath();
181
- ctx.arc(centerX, centerY, radius, 0, Math.PI * 2);
182
- ctx.fill();
183
- ctx.restore();
238
+ const shadow = getBubbleShadowSprite(size);
239
+ ctx.drawImage(
240
+ shadow.canvas,
241
+ centerX - shadow.pad - radius,
242
+ centerY - shadow.pad - radius,
243
+ );
184
244
 
185
245
  ctx.save();
186
246
  ctx.beginPath();
@@ -912,7 +912,7 @@ export default function MeetingDetailRoute() {
912
912
  </div>
913
913
  </div>
914
914
 
915
- {/* Transcript pane — chat-bubble layout */}
915
+ {/* Transcript pane — plain agent-chat-style text layout */}
916
916
  <div className="rounded-lg border border-border bg-background min-h-[480px] lg:min-h-0 overflow-hidden flex flex-col">
917
917
  <div className="flex shrink-0 items-center justify-between gap-2 border-b border-border px-4 py-2.5 bg-background">
918
918
  <div className="flex items-center gap-1.5 text-xs font-medium">