@agent-native/core 0.91.1 → 0.92.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 (383) hide show
  1. package/blueprints/action/crud.md +1 -1
  2. package/blueprints/channel/discord.md +1 -1
  3. package/blueprints/provider/stripe.md +1 -1
  4. package/blueprints/sandbox/docker.md +1 -1
  5. package/corpus/README.md +2 -2
  6. package/corpus/core/CHANGELOG.md +31 -0
  7. package/corpus/core/blueprints/action/crud.md +1 -1
  8. package/corpus/core/blueprints/channel/discord.md +1 -1
  9. package/corpus/core/blueprints/provider/stripe.md +1 -1
  10. package/corpus/core/blueprints/sandbox/docker.md +1 -1
  11. package/corpus/core/docs/content/faq.mdx +17 -0
  12. package/corpus/core/docs/content/notifications.mdx +12 -6
  13. package/corpus/core/package.json +6 -5
  14. package/corpus/core/src/agent/production-agent.ts +140 -46
  15. package/corpus/core/src/agent/run-manager.ts +23 -2
  16. package/corpus/core/src/agent/run-store.ts +2 -2
  17. package/corpus/core/src/agent/tool-search.ts +71 -4
  18. package/corpus/core/src/cli/add.ts +1 -1
  19. package/corpus/core/src/cli/create.ts +8 -3
  20. package/corpus/core/src/cli/index.ts +25 -4
  21. package/corpus/core/src/cli/upgrade.ts +798 -0
  22. package/corpus/core/src/client/AssistantChat.tsx +1 -1
  23. package/corpus/core/src/client/HighlightedCodeBlock.tsx +20 -3
  24. package/corpus/core/src/client/agent-chat-adapter.ts +31 -6
  25. package/corpus/core/src/client/chat/message-components.tsx +85 -8
  26. package/corpus/core/src/client/chat/tool-call-display.tsx +355 -144
  27. package/corpus/core/src/client/code-agent-chat-adapter.ts +2 -9
  28. package/corpus/core/src/client/extensions/ExtensionSlot.tsx +7 -0
  29. package/corpus/core/src/client/settings/SettingsPanel.tsx +533 -389
  30. package/corpus/core/src/client/settings/SettingsSection.tsx +154 -17
  31. package/corpus/core/src/client/sse-event-processor.ts +29 -2
  32. package/corpus/core/src/extensions/actions.ts +113 -5
  33. package/corpus/core/src/jobs/scheduler.ts +30 -1
  34. package/corpus/core/src/localization/default-messages.ts +21 -0
  35. package/corpus/core/src/notifications/channels.ts +88 -25
  36. package/corpus/core/src/notifications/registry.ts +17 -3
  37. package/corpus/core/src/notifications/types.ts +4 -1
  38. package/corpus/core/src/server/agent-chat-plugin.ts +7 -3
  39. package/corpus/core/src/server/request-context.ts +7 -0
  40. package/corpus/core/src/styles/agent-conversation.css +1 -1
  41. package/corpus/core/src/templates/default/.agents/skills/self-modifying-code/SKILL.md +20 -2
  42. package/corpus/core/src/templates/default/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  43. package/corpus/core/src/templates/default/AGENTS.md +10 -3
  44. package/corpus/core/src/templates/default/app/i18n/ar-SA.ts +21 -0
  45. package/corpus/core/src/templates/default/app/i18n/de-DE.ts +21 -0
  46. package/corpus/core/src/templates/default/app/i18n/en-US.ts +21 -0
  47. package/corpus/core/src/templates/default/app/i18n/es-ES.ts +21 -0
  48. package/corpus/core/src/templates/default/app/i18n/fr-FR.ts +21 -0
  49. package/corpus/core/src/templates/default/app/i18n/hi-IN.ts +20 -0
  50. package/corpus/core/src/templates/default/app/i18n/ja-JP.ts +21 -0
  51. package/corpus/core/src/templates/default/app/i18n/ko-KR.ts +20 -0
  52. package/corpus/core/src/templates/default/app/i18n/pt-BR.ts +21 -0
  53. package/corpus/core/src/templates/default/app/i18n/zh-CN.ts +20 -0
  54. package/corpus/core/src/templates/default/app/i18n/zh-TW.ts +19 -0
  55. package/corpus/core/src/templates/default/package.json +4 -3
  56. package/corpus/core/src/templates/headless/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  57. package/corpus/core/src/templates/headless/AGENTS.md +9 -3
  58. package/corpus/core/src/templates/headless/package.json +4 -3
  59. package/corpus/core/src/templates/workspace-core/.agents/skills/self-modifying-code/SKILL.md +20 -2
  60. package/corpus/core/src/templates/workspace-core/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  61. package/corpus/core/src/templates/workspace-core/package.json +6 -6
  62. package/corpus/core/src/templates/workspace-root/AGENTS.md +10 -4
  63. package/corpus/core/src/templates/workspace-root/package.json +3 -2
  64. package/corpus/core/src/triggers/dispatcher.ts +30 -2
  65. package/corpus/core/src/vite/client.ts +96 -1
  66. package/corpus/templates/analytics/.agents/skills/self-modifying-code/SKILL.md +20 -2
  67. package/corpus/templates/analytics/.agents/skills/session-replay/SKILL.md +20 -2
  68. package/corpus/templates/analytics/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  69. package/corpus/templates/analytics/AGENTS.md +7 -5
  70. package/corpus/templates/analytics/actions/save-monitor.ts +14 -0
  71. package/corpus/templates/analytics/app/components/dashboard/DataTable.tsx +41 -6
  72. package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +122 -11
  73. package/corpus/templates/analytics/app/global.css +26 -10
  74. package/corpus/templates/analytics/app/i18n/zh-TW.ts +2 -0
  75. package/corpus/templates/analytics/app/i18n-data.ts +25 -0
  76. package/corpus/templates/analytics/app/pages/monitoring/MonitoringPage.tsx +8 -2
  77. package/corpus/templates/analytics/app/pages/monitoring/uptime/MonitorFormPage.tsx +151 -32
  78. package/corpus/templates/analytics/app/pages/monitoring/uptime/i18n.ts +17 -2
  79. package/corpus/templates/analytics/app/pages/monitoring/uptime/types.ts +4 -0
  80. package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +461 -286
  81. package/corpus/templates/analytics/app/pages/sessions/SessionDevToolsPanel.tsx +285 -294
  82. package/corpus/templates/analytics/changelog/2026-07-08-console-details-expand-inline-under-each-row-and-the-event-t.md +6 -0
  83. package/corpus/templates/analytics/changelog/2026-07-08-dashboard-tables-keep-their-size-while-loading.md +6 -0
  84. package/corpus/templates/analytics/changelog/2026-07-08-monitoring-uptime-and-errors-tabs-now-clearly-show-which-view-is-active.md +6 -0
  85. package/corpus/templates/analytics/changelog/2026-07-08-session-replay-keeps-a-usable-stage-when-dev-tools-is-open-a.md +6 -0
  86. package/corpus/templates/analytics/changelog/2026-07-08-session-replay-resumes-after-scrubbing-and-expands-console-r.md +6 -0
  87. package/corpus/templates/analytics/changelog/2026-07-08-session-replay-uses-stock-rrweb-sizing-again-so-recorded.md +6 -0
  88. package/corpus/templates/analytics/changelog/2026-07-08-session-replays-wait-for-full-load-keep-a-normal-viewport-an.md +6 -0
  89. package/corpus/templates/analytics/changelog/2026-07-08-sidebar-dashboard-menus-now-open-above-the-dashboard-canvas.md +6 -0
  90. package/corpus/templates/analytics/changelog/2026-07-08-uptime-alerting-clarifies-in-app-inbox-vs-email-and-lets-you-paste-slack-or-webhook-urls-per-monitor.md +6 -0
  91. package/corpus/templates/analytics/docs/uptime-monitoring.md +13 -11
  92. package/corpus/templates/analytics/package.json +2 -2
  93. package/corpus/templates/analytics/server/db/schema-monitoring.ts +4 -0
  94. package/corpus/templates/analytics/server/lib/uptime-monitors.ts +60 -12
  95. package/corpus/templates/analytics/server/plugins/db.ts +10 -0
  96. package/corpus/templates/assets/.agents/skills/self-modifying-code/SKILL.md +20 -2
  97. package/corpus/templates/assets/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  98. package/corpus/templates/assets/package.json +2 -2
  99. package/corpus/templates/brain/.agents/skills/self-modifying-code/SKILL.md +20 -2
  100. package/corpus/templates/brain/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  101. package/corpus/templates/brain/package.json +2 -2
  102. package/corpus/templates/calendar/.agents/skills/self-modifying-code/SKILL.md +20 -2
  103. package/corpus/templates/calendar/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  104. package/corpus/templates/calendar/AGENTS.md +10 -0
  105. package/corpus/templates/calendar/actions/create-event.ts +5 -1
  106. package/corpus/templates/calendar/actions/event-action-helpers.ts +41 -0
  107. package/corpus/templates/calendar/actions/get-attendee-timezones.ts +28 -0
  108. package/corpus/templates/calendar/actions/set-attendee-timezone.ts +68 -0
  109. package/corpus/templates/calendar/app/components/calendar/EventAttendeesSection.tsx +150 -11
  110. package/corpus/templates/calendar/app/components/calendar/EventDetailPanel.tsx +36 -1
  111. package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +32 -0
  112. package/corpus/templates/calendar/app/hooks/use-attendee-timezones.ts +24 -0
  113. package/corpus/templates/calendar/app/i18n/zh-TW.ts +2 -0
  114. package/corpus/templates/calendar/app/i18n-data.ts +20 -0
  115. package/corpus/templates/calendar/app/lib/attendee-local-time.ts +122 -0
  116. package/corpus/templates/calendar/changelog/2026-07-08-event-details-now-support-extension-widgets-and-you-can-see-.md +6 -0
  117. package/corpus/templates/calendar/changelog/2026-07-08-when-you-invite-guests-to-a-new-event-you-now-appear-in.md +6 -0
  118. package/corpus/templates/calendar/package.json +2 -2
  119. package/corpus/templates/calendar/server/handlers/events.ts +5 -1
  120. package/corpus/templates/calendar/shared/api.ts +6 -0
  121. package/corpus/templates/chat/.agents/skills/self-modifying-code/SKILL.md +20 -2
  122. package/corpus/templates/chat/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  123. package/corpus/templates/chat/package.json +2 -2
  124. package/corpus/templates/clips/.agents/skills/meetings/SKILL.md +6 -2
  125. package/corpus/templates/clips/.agents/skills/self-modifying-code/SKILL.md +20 -2
  126. package/corpus/templates/clips/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  127. package/corpus/templates/clips/actions/create-meeting.ts +8 -3
  128. package/corpus/templates/clips/actions/list-meetings.ts +31 -3
  129. package/corpus/templates/clips/changelog/2026-07-08-after-finishing-a-desktop-recording-reopening-clips-restor.md +6 -0
  130. package/corpus/templates/clips/changelog/2026-07-08-clips-now-pops-up-to-take-notes-when-you-join-an-adhoc-zoom-.md +6 -0
  131. package/corpus/templates/clips/changelog/2026-07-08-desktop-clip-recordings-are-louder-after-capture-mic-only.md +6 -0
  132. package/corpus/templates/clips/changelog/2026-07-08-desktop-meeting-notes-stay-active-from-tray-and-shortcut-controls.md +6 -0
  133. package/corpus/templates/clips/changelog/2026-07-08-desktop-meeting-popovers-can-start-notes-and-join-the-meetin.md +6 -0
  134. package/corpus/templates/clips/changelog/2026-07-08-meeting-notes-keep-their-recording-state-when-the-desktop.md +6 -0
  135. package/corpus/templates/clips/changelog/2026-07-08-meeting-reminder-dropdowns-now-expand-fully-instead-of-being.md +6 -0
  136. package/corpus/templates/clips/changelog/2026-07-08-meeting-reminders-now-appear-1-minute-before-start-stay-unti.md +6 -0
  137. package/corpus/templates/clips/changelog/2026-07-08-the-desktop-meetings-panel-has-room-for-its-drop-shadow-and-.md +6 -0
  138. package/corpus/templates/clips/chrome-extension/package.json +2 -2
  139. package/corpus/templates/clips/desktop/package.json +2 -2
  140. package/corpus/templates/clips/desktop/src/app.tsx +214 -37
  141. package/corpus/templates/clips/desktop/src/hooks/useMeetingTranscription.ts +30 -6
  142. package/corpus/templates/clips/desktop/src/lib/meeting-notification-timing.ts +83 -0
  143. package/corpus/templates/clips/desktop/src/lib/native-recording-warm.ts +21 -0
  144. package/corpus/templates/clips/desktop/src/lib/recorder.ts +98 -20
  145. package/corpus/templates/clips/desktop/src/overlays/meeting-notification.tsx +197 -50
  146. package/corpus/templates/clips/desktop/src/styles.css +150 -53
  147. package/corpus/templates/clips/desktop/src-tauri/src/adhoc_meetings_watcher.rs +362 -0
  148. package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +41 -7
  149. package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +9 -2
  150. package/corpus/templates/clips/desktop/src-tauri/src/meetings_watcher.rs +89 -11
  151. package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +71 -17
  152. package/corpus/templates/clips/desktop/src-tauri/src/native_speech.rs +36 -1
  153. package/corpus/templates/clips/desktop/src-tauri/src/notifications.rs +62 -10
  154. package/corpus/templates/clips/desktop/src-tauri/src/shortcuts.rs +3 -3
  155. package/corpus/templates/clips/desktop/src-tauri/src/silence_detector.rs +1 -25
  156. package/corpus/templates/clips/desktop/src-tauri/src/state.rs +5 -0
  157. package/corpus/templates/clips/desktop/src-tauri/src/tray.rs +18 -6
  158. package/corpus/templates/clips/desktop/src-tauri/src/util.rs +36 -1
  159. package/corpus/templates/clips/package.json +2 -2
  160. package/corpus/templates/content/.agents/skills/self-modifying-code/SKILL.md +20 -2
  161. package/corpus/templates/content/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  162. package/corpus/templates/content/actions/_batch-utils.ts +8 -0
  163. package/corpus/templates/content/actions/_builder-cms-read-client.ts +7 -5
  164. package/corpus/templates/content/actions/_content-database-personal-view.ts +80 -0
  165. package/corpus/templates/content/actions/_database-source-utils.ts +776 -319
  166. package/corpus/templates/content/actions/_database-utils.ts +6 -1
  167. package/corpus/templates/content/actions/_property-utils.ts +168 -0
  168. package/corpus/templates/content/actions/add-content-database-source-field-property.ts +19 -12
  169. package/corpus/templates/content/actions/bind-content-database-source-field.ts +19 -12
  170. package/corpus/templates/content/actions/delete-document.ts +9 -13
  171. package/corpus/templates/content/actions/get-content-database-personal-view.ts +27 -0
  172. package/corpus/templates/content/actions/refresh-content-database-source.ts +5 -5
  173. package/corpus/templates/content/actions/update-content-database-personal-view.ts +31 -0
  174. package/corpus/templates/content/actions/update-content-database-view.ts +3 -0
  175. package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +1 -1
  176. package/corpus/templates/content/app/components/editor/body-hydration.ts +35 -11
  177. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +2631 -551
  178. package/corpus/templates/content/app/components/editor/database/filter-sort.ts +220 -18
  179. package/corpus/templates/content/app/hooks/use-content-database.ts +32 -0
  180. package/corpus/templates/content/app/hooks/use-documents.ts +8 -2
  181. package/corpus/templates/content/app/i18n/zh-TW.ts +16 -0
  182. package/corpus/templates/content/app/i18n-data.ts +154 -0
  183. package/corpus/templates/content/changelog/2026-07-08-builder-source-sync-now-completes-reliably-on-hosted-databases.md +6 -0
  184. package/corpus/templates/content/changelog/2026-07-08-database-filters-now-support-notion-style-reset-save-for-eve.md +6 -0
  185. package/corpus/templates/content/changelog/2026-07-08-date-filters-can-now-target-a-between-range.md +6 -0
  186. package/corpus/templates/content/changelog/2026-07-08-fixed-builder-database-sync-so-large-sources-continue-past-t.md +6 -0
  187. package/corpus/templates/content/package.json +2 -2
  188. package/corpus/templates/content/parity/matrix.md +1 -1
  189. package/corpus/templates/content/parity/matrix.ts +2 -0
  190. package/corpus/templates/content/server/plugins/db.ts +19 -0
  191. package/corpus/templates/content/shared/api.ts +24 -0
  192. package/corpus/templates/design/.agents/skills/self-modifying-code/SKILL.md +20 -2
  193. package/corpus/templates/design/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  194. package/corpus/templates/design/package.json +2 -2
  195. package/corpus/templates/dispatch/.agents/skills/self-modifying-code/SKILL.md +20 -2
  196. package/corpus/templates/dispatch/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  197. package/corpus/templates/dispatch/app/i18n/zh-TW.ts +34 -1
  198. package/corpus/templates/dispatch/app/i18n-data.ts +387 -10
  199. package/corpus/templates/dispatch/app/routes/automations.tsx +1 -0
  200. package/corpus/templates/dispatch/changelog/2026-07-08-added-an-automations-page-to-review-pause-and-create.md +6 -0
  201. package/corpus/templates/dispatch/changelog/2026-07-08-destinations-now-shows-outbound-delivery-queue-health.md +6 -0
  202. package/corpus/templates/dispatch/package.json +2 -2
  203. package/corpus/templates/forms/.agents/skills/self-modifying-code/SKILL.md +20 -2
  204. package/corpus/templates/forms/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  205. package/corpus/templates/forms/package.json +2 -2
  206. package/corpus/templates/macros/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  207. package/corpus/templates/macros/package.json +2 -2
  208. package/corpus/templates/mail/.agents/skills/self-modifying-code/SKILL.md +20 -2
  209. package/corpus/templates/mail/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  210. package/corpus/templates/mail/app/hooks/use-emails.ts +56 -44
  211. package/corpus/templates/mail/app/lib/gmail-mutation-queue.ts +325 -0
  212. package/corpus/templates/mail/changelog/2026-07-08-rapid-archive-and-mark-read-now-batch-gmail-updates-so-the-i.md +6 -0
  213. package/corpus/templates/mail/package.json +2 -2
  214. package/corpus/templates/plan/.agents/skills/self-modifying-code/SKILL.md +20 -2
  215. package/corpus/templates/plan/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  216. package/corpus/templates/plan/package.json +2 -2
  217. package/corpus/templates/slides/.agents/skills/self-modifying-code/SKILL.md +20 -2
  218. package/corpus/templates/slides/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  219. package/corpus/templates/slides/app/components/editor/ImageDropPromptPopover.tsx +46 -29
  220. package/corpus/templates/slides/app/context/DeckContext.tsx +45 -18
  221. package/corpus/templates/slides/app/i18n/ar-SA.ts +4 -0
  222. package/corpus/templates/slides/app/i18n/de-DE.ts +4 -0
  223. package/corpus/templates/slides/app/i18n/en-US.ts +4 -0
  224. package/corpus/templates/slides/app/i18n/es-ES.ts +4 -0
  225. package/corpus/templates/slides/app/i18n/fr-FR.ts +4 -0
  226. package/corpus/templates/slides/app/i18n/hi-IN.ts +4 -0
  227. package/corpus/templates/slides/app/i18n/ja-JP.ts +4 -0
  228. package/corpus/templates/slides/app/i18n/ko-KR.ts +4 -0
  229. package/corpus/templates/slides/app/i18n/pt-BR.ts +4 -0
  230. package/corpus/templates/slides/app/i18n/zh-CN.ts +4 -0
  231. package/corpus/templates/slides/app/i18n/zh-TW.ts +4 -0
  232. package/corpus/templates/slides/app/lib/image-drop-to-agent.ts +115 -0
  233. package/corpus/templates/slides/app/pages/DeckEditor.tsx +22 -13
  234. package/corpus/templates/slides/changelog/2026-07-08-dropping-an-image-onto-a-slide-and-sending-it-to-the-agent-w.md +6 -0
  235. package/corpus/templates/slides/changelog/2026-07-08-undo-works-after-the-agent-edits-a-slide-so-you-can-reverse.md +6 -0
  236. package/corpus/templates/slides/package.json +2 -2
  237. package/corpus/templates/videos/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  238. package/corpus/templates/voice/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  239. package/dist/agent/production-agent.d.ts +5 -1
  240. package/dist/agent/production-agent.d.ts.map +1 -1
  241. package/dist/agent/production-agent.js +112 -42
  242. package/dist/agent/production-agent.js.map +1 -1
  243. package/dist/agent/run-manager.d.ts.map +1 -1
  244. package/dist/agent/run-manager.js +23 -2
  245. package/dist/agent/run-manager.js.map +1 -1
  246. package/dist/agent/run-store.js +2 -2
  247. package/dist/agent/run-store.js.map +1 -1
  248. package/dist/agent/tool-search.d.ts +2 -0
  249. package/dist/agent/tool-search.d.ts.map +1 -1
  250. package/dist/agent/tool-search.js +60 -4
  251. package/dist/agent/tool-search.js.map +1 -1
  252. package/dist/catalog.json +2 -2
  253. package/dist/cli/add.js +1 -1
  254. package/dist/cli/add.js.map +1 -1
  255. package/dist/cli/create.d.ts.map +1 -1
  256. package/dist/cli/create.js +8 -3
  257. package/dist/cli/create.js.map +1 -1
  258. package/dist/cli/index.js +24 -4
  259. package/dist/cli/index.js.map +1 -1
  260. package/dist/cli/upgrade.d.ts +100 -0
  261. package/dist/cli/upgrade.d.ts.map +1 -0
  262. package/dist/cli/upgrade.js +678 -0
  263. package/dist/cli/upgrade.js.map +1 -0
  264. package/dist/client/AssistantChat.js +1 -1
  265. package/dist/client/AssistantChat.js.map +1 -1
  266. package/dist/client/HighlightedCodeBlock.d.ts +2 -0
  267. package/dist/client/HighlightedCodeBlock.d.ts.map +1 -1
  268. package/dist/client/HighlightedCodeBlock.js +10 -1
  269. package/dist/client/HighlightedCodeBlock.js.map +1 -1
  270. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  271. package/dist/client/agent-chat-adapter.js +32 -4
  272. package/dist/client/agent-chat-adapter.js.map +1 -1
  273. package/dist/client/chat/message-components.d.ts.map +1 -1
  274. package/dist/client/chat/message-components.js +61 -8
  275. package/dist/client/chat/message-components.js.map +1 -1
  276. package/dist/client/chat/tool-call-display.d.ts +12 -0
  277. package/dist/client/chat/tool-call-display.d.ts.map +1 -1
  278. package/dist/client/chat/tool-call-display.js +136 -42
  279. package/dist/client/chat/tool-call-display.js.map +1 -1
  280. package/dist/client/code-agent-chat-adapter.js +2 -9
  281. package/dist/client/code-agent-chat-adapter.js.map +1 -1
  282. package/dist/client/extensions/ExtensionSlot.js +6 -0
  283. package/dist/client/extensions/ExtensionSlot.js.map +1 -1
  284. package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
  285. package/dist/client/settings/SettingsPanel.js +52 -19
  286. package/dist/client/settings/SettingsPanel.js.map +1 -1
  287. package/dist/client/settings/SettingsSection.d.ts +19 -4
  288. package/dist/client/settings/SettingsSection.d.ts.map +1 -1
  289. package/dist/client/settings/SettingsSection.js +33 -4
  290. package/dist/client/settings/SettingsSection.js.map +1 -1
  291. package/dist/client/sse-event-processor.d.ts +9 -0
  292. package/dist/client/sse-event-processor.d.ts.map +1 -1
  293. package/dist/client/sse-event-processor.js +20 -2
  294. package/dist/client/sse-event-processor.js.map +1 -1
  295. package/dist/collab/routes.d.ts +1 -1
  296. package/dist/collab/struct-routes.d.ts +1 -1
  297. package/dist/extensions/actions.d.ts.map +1 -1
  298. package/dist/extensions/actions.js +85 -5
  299. package/dist/extensions/actions.js.map +1 -1
  300. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  301. package/dist/jobs/scheduler.js +26 -1
  302. package/dist/jobs/scheduler.js.map +1 -1
  303. package/dist/localization/default-messages.d.ts +19 -0
  304. package/dist/localization/default-messages.d.ts.map +1 -1
  305. package/dist/localization/default-messages.js +19 -0
  306. package/dist/localization/default-messages.js.map +1 -1
  307. package/dist/notifications/channels.d.ts +8 -3
  308. package/dist/notifications/channels.d.ts.map +1 -1
  309. package/dist/notifications/channels.js +61 -22
  310. package/dist/notifications/channels.js.map +1 -1
  311. package/dist/notifications/registry.d.ts.map +1 -1
  312. package/dist/notifications/registry.js +14 -3
  313. package/dist/notifications/registry.js.map +1 -1
  314. package/dist/notifications/routes.d.ts +1 -1
  315. package/dist/notifications/types.d.ts +4 -1
  316. package/dist/notifications/types.d.ts.map +1 -1
  317. package/dist/notifications/types.js.map +1 -1
  318. package/dist/observability/routes.d.ts +1 -1
  319. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  320. package/dist/resources/handlers.d.ts +3 -3
  321. package/dist/server/agent-chat-plugin.d.ts +5 -1
  322. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  323. package/dist/server/agent-chat-plugin.js +2 -2
  324. package/dist/server/agent-chat-plugin.js.map +1 -1
  325. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  326. package/dist/server/request-context.d.ts +7 -0
  327. package/dist/server/request-context.d.ts.map +1 -1
  328. package/dist/server/request-context.js.map +1 -1
  329. package/dist/styles/agent-conversation.css +1 -1
  330. package/dist/templates/default/.agents/skills/self-modifying-code/SKILL.md +20 -2
  331. package/dist/templates/default/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  332. package/dist/templates/default/AGENTS.md +10 -3
  333. package/dist/templates/default/app/i18n/ar-SA.ts +21 -0
  334. package/dist/templates/default/app/i18n/de-DE.ts +21 -0
  335. package/dist/templates/default/app/i18n/en-US.ts +21 -0
  336. package/dist/templates/default/app/i18n/es-ES.ts +21 -0
  337. package/dist/templates/default/app/i18n/fr-FR.ts +21 -0
  338. package/dist/templates/default/app/i18n/hi-IN.ts +20 -0
  339. package/dist/templates/default/app/i18n/ja-JP.ts +21 -0
  340. package/dist/templates/default/app/i18n/ko-KR.ts +20 -0
  341. package/dist/templates/default/app/i18n/pt-BR.ts +21 -0
  342. package/dist/templates/default/app/i18n/zh-CN.ts +20 -0
  343. package/dist/templates/default/app/i18n/zh-TW.ts +19 -0
  344. package/dist/templates/default/package.json +4 -3
  345. package/dist/templates/headless/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  346. package/dist/templates/headless/AGENTS.md +9 -3
  347. package/dist/templates/headless/package.json +4 -3
  348. package/dist/templates/workspace-core/.agents/skills/self-modifying-code/SKILL.md +20 -2
  349. package/dist/templates/workspace-core/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  350. package/dist/templates/workspace-core/package.json +6 -6
  351. package/dist/templates/workspace-root/AGENTS.md +10 -4
  352. package/dist/templates/workspace-root/package.json +3 -2
  353. package/dist/triggers/dispatcher.js +27 -2
  354. package/dist/triggers/dispatcher.js.map +1 -1
  355. package/dist/vite/client.d.ts.map +1 -1
  356. package/dist/vite/client.js +70 -1
  357. package/dist/vite/client.js.map +1 -1
  358. package/docs/content/faq.mdx +17 -0
  359. package/docs/content/notifications.mdx +12 -6
  360. package/package.json +7 -6
  361. package/src/templates/default/.agents/skills/self-modifying-code/SKILL.md +20 -2
  362. package/src/templates/default/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  363. package/src/templates/default/AGENTS.md +10 -3
  364. package/src/templates/default/app/i18n/ar-SA.ts +21 -0
  365. package/src/templates/default/app/i18n/de-DE.ts +21 -0
  366. package/src/templates/default/app/i18n/en-US.ts +21 -0
  367. package/src/templates/default/app/i18n/es-ES.ts +21 -0
  368. package/src/templates/default/app/i18n/fr-FR.ts +21 -0
  369. package/src/templates/default/app/i18n/hi-IN.ts +20 -0
  370. package/src/templates/default/app/i18n/ja-JP.ts +21 -0
  371. package/src/templates/default/app/i18n/ko-KR.ts +20 -0
  372. package/src/templates/default/app/i18n/pt-BR.ts +21 -0
  373. package/src/templates/default/app/i18n/zh-CN.ts +20 -0
  374. package/src/templates/default/app/i18n/zh-TW.ts +19 -0
  375. package/src/templates/default/package.json +4 -3
  376. package/src/templates/headless/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  377. package/src/templates/headless/AGENTS.md +9 -3
  378. package/src/templates/headless/package.json +4 -3
  379. package/src/templates/workspace-core/.agents/skills/self-modifying-code/SKILL.md +20 -2
  380. package/src/templates/workspace-core/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  381. package/src/templates/workspace-core/package.json +6 -6
  382. package/src/templates/workspace-root/AGENTS.md +10 -4
  383. package/src/templates/workspace-root/package.json +3 -2
@@ -25,7 +25,9 @@
25
25
  --radius: 12px;
26
26
  --radius-sm: 8px;
27
27
  --radius-pill: 999px;
28
- --popover-shadow-gutter: 12px;
28
+ /* Must cover `--shadow-md` blur (24px in dark mode) or the transparent
29
+ Tauri window clips the drop shadow on every edge. */
30
+ --popover-shadow-gutter: 24px;
29
31
  --overlay-shadow-gutter: 18px;
30
32
 
31
33
  --shadow-sm: 0 1px 2px rgba(23, 23, 23, 0.06);
@@ -1193,6 +1195,7 @@ body[data-clips-route="recording-pill"] #root {
1193
1195
  display: flex;
1194
1196
  flex-direction: column;
1195
1197
  gap: 6px;
1198
+ margin-bottom: 4px;
1196
1199
  }
1197
1200
 
1198
1201
  .popover-list-item {
@@ -1262,6 +1265,27 @@ body[data-clips-route="recording-pill"] #root {
1262
1265
  background: var(--brand-hover);
1263
1266
  }
1264
1267
 
1268
+ .popover-list-action-active {
1269
+ display: inline-flex;
1270
+ align-items: center;
1271
+ gap: 5px;
1272
+ border-color: rgba(34, 197, 94, 0.36);
1273
+ background: rgba(34, 197, 94, 0.12);
1274
+ color: #4ade80;
1275
+ }
1276
+
1277
+ .popover-list-action-active:hover {
1278
+ background: rgba(34, 197, 94, 0.18);
1279
+ }
1280
+
1281
+ .popover-list-action-dot {
1282
+ width: 6px;
1283
+ height: 6px;
1284
+ border-radius: 999px;
1285
+ background: currentColor;
1286
+ box-shadow: 0 0 0 3px rgba(34, 197, 94, 0.12);
1287
+ }
1288
+
1265
1289
  .popover-kv {
1266
1290
  display: flex;
1267
1291
  align-items: center;
@@ -1419,6 +1443,13 @@ body[data-clips-route="recording-pill"] #root {
1419
1443
  overscroll-behavior: contain;
1420
1444
  }
1421
1445
 
1446
+ /* Meetings / dictation drill-ins: a touch more room under the last row so
1447
+ content isn't flush with the panel edge. Must follow `.setup` so it wins
1448
+ over the shorthand padding above. */
1449
+ .setup.popover-view {
1450
+ padding-bottom: 26px;
1451
+ }
1452
+
1422
1453
  .setup h2 {
1423
1454
  margin: 0;
1424
1455
  font-size: 14px;
@@ -3599,7 +3630,7 @@ body[data-clips-route="recording-pill"] #root {
3599
3630
  position: relative;
3600
3631
  display: flex;
3601
3632
  align-items: center;
3602
- gap: 10px;
3633
+ gap: 12px;
3603
3634
  padding: 12px 14px;
3604
3635
  border-radius: 14px;
3605
3636
  background: rgba(20, 22, 28, 0.94);
@@ -3611,37 +3642,26 @@ body[data-clips-route="recording-pill"] #root {
3611
3642
  0 2px 6px rgba(0, 0, 0, 0.3);
3612
3643
  color: white;
3613
3644
  pointer-events: auto;
3614
- min-width: 280px;
3615
- max-width: 400px;
3616
- }
3617
-
3618
- .meeting-notification-with-join {
3619
- flex-wrap: wrap;
3620
- align-items: flex-start;
3621
- row-gap: 10px;
3622
- }
3623
-
3624
- .meeting-notification-with-join .meeting-notification-actions {
3625
- flex: 1 1 100%;
3626
- justify-content: flex-end;
3627
- padding-left: 13px; /* align under content past the bar */
3645
+ min-width: 320px;
3646
+ max-width: 440px;
3628
3647
  }
3629
3648
 
3630
3649
  .meeting-notification-bar {
3631
3650
  width: 3px;
3632
- height: 40px;
3633
- border-radius: 2px;
3651
+ height: 36px;
3652
+ border-radius: 999px;
3634
3653
  flex-shrink: 0;
3635
3654
  }
3636
3655
 
3637
3656
  .meeting-notification-bar-calendar {
3638
- background: #16a34a;
3657
+ background: #7dd3fc;
3639
3658
  }
3640
3659
 
3641
3660
  .meeting-notification-bar-adhoc {
3642
3661
  background: transparent;
3643
3662
  border-left: 3px dashed #a3a3a3;
3644
3663
  width: 3px;
3664
+ border-radius: 0;
3645
3665
  }
3646
3666
 
3647
3667
  .meeting-notification-content {
@@ -3650,6 +3670,7 @@ body[data-clips-route="recording-pill"] #root {
3650
3670
  display: flex;
3651
3671
  flex-direction: column;
3652
3672
  gap: 2px;
3673
+ padding-right: 4px;
3653
3674
  }
3654
3675
 
3655
3676
  .meeting-notification-title {
@@ -3666,65 +3687,141 @@ body[data-clips-route="recording-pill"] #root {
3666
3687
  color: rgba(255, 255, 255, 0.55);
3667
3688
  }
3668
3689
 
3669
- .meeting-notification-btn {
3670
- border: none;
3671
- border-radius: 8px;
3672
- padding: 6px 12px;
3673
- background: rgba(255, 255, 255, 0.1);
3674
- color: white;
3675
- font-size: 12px;
3676
- font-weight: 600;
3677
- cursor: pointer;
3678
- white-space: nowrap;
3690
+ .meeting-notification-split {
3691
+ position: relative;
3692
+ display: inline-flex;
3693
+ align-items: stretch;
3679
3694
  flex-shrink: 0;
3695
+ border: 1px solid rgba(255, 255, 255, 0.14);
3696
+ border-radius: 10px;
3697
+ overflow: visible;
3698
+ background: rgba(255, 255, 255, 0.04);
3680
3699
  }
3681
3700
 
3682
- .meeting-notification-btn:hover {
3683
- background: rgba(255, 255, 255, 0.16);
3701
+ .meeting-notification-split-main {
3702
+ display: inline-flex;
3703
+ align-items: center;
3704
+ gap: 8px;
3705
+ border: none;
3706
+ background: transparent;
3707
+ color: white;
3708
+ padding: 6px 10px;
3709
+ cursor: pointer;
3710
+ text-align: left;
3711
+ min-height: 40px;
3684
3712
  }
3685
3713
 
3686
- .meeting-notification-btn:active {
3687
- transform: scale(0.96);
3714
+ .meeting-notification-split-main:hover {
3715
+ background: rgba(255, 255, 255, 0.06);
3688
3716
  }
3689
3717
 
3690
- .meeting-notification-btn:disabled {
3691
- opacity: 0.6;
3718
+ .meeting-notification-split-main:disabled {
3719
+ opacity: 0.65;
3692
3720
  cursor: progress;
3693
3721
  }
3694
3722
 
3695
- .meeting-notification-btn-primary {
3696
- background: #ffffff;
3697
- color: #111827;
3723
+ .meeting-notification-split-copy {
3724
+ display: flex;
3725
+ flex-direction: column;
3726
+ gap: 0;
3727
+ line-height: 1.15;
3698
3728
  }
3699
3729
 
3700
- .meeting-notification-btn-primary:hover {
3701
- background: #f3f4f6;
3730
+ .meeting-notification-split-primary {
3731
+ font-size: 12px;
3732
+ font-weight: 600;
3733
+ color: white;
3734
+ white-space: nowrap;
3702
3735
  }
3703
3736
 
3704
- .meeting-notification-btn-primary:disabled {
3705
- background: rgba(255, 255, 255, 0.7);
3706
- color: #374151;
3737
+ .meeting-notification-split-secondary {
3738
+ font-size: 10px;
3739
+ font-weight: 500;
3740
+ color: rgba(255, 255, 255, 0.55);
3741
+ white-space: nowrap;
3707
3742
  }
3708
3743
 
3709
- .meeting-notification-actions {
3744
+ .meeting-notification-split-chevron {
3710
3745
  display: inline-flex;
3711
3746
  align-items: center;
3712
- gap: 6px;
3713
- flex-shrink: 0;
3747
+ justify-content: center;
3748
+ width: 28px;
3749
+ border: none;
3750
+ border-left: 1px solid rgba(255, 255, 255, 0.12);
3751
+ background: transparent;
3752
+ color: rgba(255, 255, 255, 0.75);
3753
+ cursor: pointer;
3754
+ border-radius: 0 10px 10px 0;
3755
+ }
3756
+
3757
+ .meeting-notification-split-chevron:hover {
3758
+ background: rgba(255, 255, 255, 0.06);
3759
+ color: white;
3714
3760
  }
3715
3761
 
3716
- .meeting-notification-btn-secondary {
3762
+ .meeting-notification-provider {
3763
+ width: 22px;
3764
+ height: 22px;
3765
+ border-radius: 6px;
3717
3766
  display: inline-flex;
3718
3767
  align-items: center;
3719
- gap: 4px;
3720
- padding: 6px 8px;
3721
- background: rgba(255, 255, 255, 0.06);
3722
- color: rgba(255, 255, 255, 0.85);
3768
+ justify-content: center;
3769
+ flex-shrink: 0;
3770
+ color: white;
3771
+ }
3772
+
3773
+ .meeting-notification-provider-zoom {
3774
+ background: #2d8cff;
3775
+ }
3776
+
3777
+ .meeting-notification-provider-meet {
3778
+ background: #34a853;
3723
3779
  }
3724
3780
 
3725
- .meeting-notification-btn-secondary:hover {
3781
+ .meeting-notification-provider-teams {
3782
+ background: #6264a7;
3783
+ }
3784
+
3785
+ .meeting-notification-provider-other {
3726
3786
  background: rgba(255, 255, 255, 0.12);
3727
- color: white;
3787
+ }
3788
+
3789
+ .meeting-notification-menu {
3790
+ position: absolute;
3791
+ top: calc(100% + 6px);
3792
+ right: 0;
3793
+ min-width: 160px;
3794
+ padding: 4px;
3795
+ border-radius: 10px;
3796
+ background: rgba(24, 26, 32, 0.98);
3797
+ border: 1px solid rgba(255, 255, 255, 0.1);
3798
+ box-shadow: 0 12px 28px rgba(0, 0, 0, 0.4);
3799
+ z-index: 5;
3800
+ display: flex;
3801
+ flex-direction: column;
3802
+ gap: 2px;
3803
+ }
3804
+
3805
+ .meeting-notification-menu-item {
3806
+ border: none;
3807
+ background: transparent;
3808
+ color: rgba(255, 255, 255, 0.9);
3809
+ text-align: left;
3810
+ font-size: 12px;
3811
+ font-weight: 500;
3812
+ padding: 8px 10px;
3813
+ border-radius: 7px;
3814
+ cursor: pointer;
3815
+ white-space: nowrap;
3816
+ }
3817
+
3818
+ .meeting-notification-menu-item:hover {
3819
+ background: rgba(255, 255, 255, 0.08);
3820
+ }
3821
+
3822
+ .meeting-notification-menu-item:disabled {
3823
+ opacity: 0.5;
3824
+ cursor: progress;
3728
3825
  }
3729
3826
 
3730
3827
  .meeting-notification-error {
@@ -0,0 +1,362 @@
1
+ //! Granola-style adhoc Zoom / Teams detection.
2
+ //!
3
+ //! Polls the frontmost macOS app every few seconds. When Zoom or Teams stays
4
+ //! frontmost for a short dwell window, creates a meeting row via
5
+ //! `create-meeting` and shows the same meeting-notification overlay used for
6
+ //! calendar reminders — with `type: "adhoc"`.
7
+ //!
8
+ //! Reuses `MeetingsWatcherState` session (server URL + cookie + auth token)
9
+ //! so the popover only needs to push credentials once.
10
+
11
+ use std::collections::HashMap;
12
+ use std::sync::Mutex;
13
+ use std::time::{Duration, Instant};
14
+
15
+ use tauri::{AppHandle, Emitter, Manager};
16
+
17
+ use crate::config::{feature_config, MeetingTranscriptionMode};
18
+ use crate::dlog;
19
+ use crate::meetings_watcher::MeetingsWatcherState;
20
+
21
+ /// How often to sample the frontmost app.
22
+ const POLL_SECS: u64 = 4;
23
+
24
+ /// Require this many consecutive frontmost seconds before firing.
25
+ const DWELL_SECS: u64 = 9;
26
+
27
+ /// After dismiss / fire, suppress re-prompts for this platform until the
28
+ /// cooldown elapses (or the VC app leaves front — see tick logic).
29
+ const COOLDOWN_SECS: i64 = 30 * 60;
30
+
31
+ /// Soft guard: skip adhoc if a calendar reminder for the same platform fired
32
+ /// this recently.
33
+ const CALENDAR_SOFT_GUARD_SECS: i64 = 3 * 60;
34
+
35
+ const STRONG_VC_BUNDLES: &[(&str, &str, &str)] = &[
36
+ // (bundle_id, platform, display title)
37
+ ("us.zoom.xos", "zoom", "Zoom meeting detected"),
38
+ ("us.zoom.ZoomClips", "zoom", "Zoom meeting detected"),
39
+ ("com.microsoft.teams2", "teams", "Teams meeting detected"),
40
+ ("com.microsoft.teams", "teams", "Teams meeting detected"),
41
+ ];
42
+
43
+ #[derive(Default)]
44
+ pub struct AdhocMeetingsWatcherState {
45
+ inner: Mutex<AdhocMeetingsWatcherInner>,
46
+ }
47
+
48
+ #[derive(Default)]
49
+ struct AdhocMeetingsWatcherInner {
50
+ /// platform -> unix-seconds when we last fired (or dismissed via cooldown).
51
+ cooldown_until: HashMap<String, i64>,
52
+ /// Current dwell: which platform is accumulating frontmost time.
53
+ dwell_platform: Option<String>,
54
+ dwell_since: Option<Instant>,
55
+ /// Platforms already notified for the current continuous foreground session.
56
+ session_notified: HashMap<String, bool>,
57
+ }
58
+
59
+ /// Spawn the long-running adhoc watcher. Idempotent — gated by OnceLock.
60
+ pub fn spawn_watcher(app: AppHandle) {
61
+ use std::sync::OnceLock;
62
+ static STARTED: OnceLock<()> = OnceLock::new();
63
+ if STARTED.set(()).is_err() {
64
+ return;
65
+ }
66
+ tauri::async_runtime::spawn(async move {
67
+ run_watcher(app).await;
68
+ });
69
+ }
70
+
71
+ async fn run_watcher(app: AppHandle) {
72
+ let mut interval = tokio::time::interval(Duration::from_secs(POLL_SECS));
73
+ // Skip the first tick — give the frontend time to push session creds.
74
+ interval.tick().await;
75
+ let client = match reqwest::Client::builder()
76
+ .timeout(Duration::from_secs(10))
77
+ .build()
78
+ {
79
+ Ok(c) => c,
80
+ Err(err) => {
81
+ eprintln!("[clips-tray] adhoc_meetings_watcher: reqwest build failed: {err}");
82
+ return;
83
+ }
84
+ };
85
+ loop {
86
+ interval.tick().await;
87
+ if let Err(err) = tick_once(&app, &client).await {
88
+ eprintln!("[clips-tray] adhoc_meetings_watcher tick failed: {err}");
89
+ }
90
+ }
91
+ }
92
+
93
+ fn match_vc_bundle(bundle: &str) -> Option<(&'static str, &'static str)> {
94
+ STRONG_VC_BUNDLES
95
+ .iter()
96
+ .find(|(id, _, _)| *id == bundle)
97
+ .map(|(_, platform, title)| (*platform, *title))
98
+ }
99
+
100
+ async fn tick_once(app: &AppHandle, client: &reqwest::Client) -> Result<(), String> {
101
+ let config = feature_config(app);
102
+ if !config.meetings_enabled {
103
+ reset_dwell(app);
104
+ return Ok(());
105
+ }
106
+
107
+ #[cfg(not(target_os = "macos"))]
108
+ {
109
+ let _ = (app, client);
110
+ return Ok(());
111
+ }
112
+
113
+ #[cfg(target_os = "macos")]
114
+ {
115
+ tick_macos(app, client, &config).await
116
+ }
117
+ }
118
+
119
+ #[cfg(target_os = "macos")]
120
+ async fn tick_macos(
121
+ app: &AppHandle,
122
+ client: &reqwest::Client,
123
+ config: &crate::config::FeatureConfig,
124
+ ) -> Result<(), String> {
125
+ let front = crate::util::frontmost_bundle_id();
126
+ let matched = front.as_deref().and_then(match_vc_bundle);
127
+
128
+ let Some((platform, title)) = matched else {
129
+ // VC app left front — clear session dedupe so a later re-focus can
130
+ // fire again (cooldown still applies).
131
+ clear_session_if_left(app, None);
132
+ reset_dwell(app);
133
+ return Ok(());
134
+ };
135
+
136
+ clear_session_if_left(app, Some(platform));
137
+
138
+ // Skip while already transcribing a meeting.
139
+ if crate::util::is_meeting_active(app) {
140
+ reset_dwell(app);
141
+ return Ok(());
142
+ }
143
+
144
+ if config.meeting_transcription_mode == MeetingTranscriptionMode::Manual
145
+ && !config.show_meeting_widget_enabled
146
+ {
147
+ reset_dwell(app);
148
+ return Ok(());
149
+ }
150
+
151
+ // Soft guard against double-prompting after a calendar reminder.
152
+ if let Some(state) = app.try_state::<MeetingsWatcherState>() {
153
+ if state.recent_calendar_notify(platform, CALENDAR_SOFT_GUARD_SECS) {
154
+ dlog!(
155
+ "[clips-tray] adhoc skip: recent calendar notify for {}",
156
+ platform
157
+ );
158
+ reset_dwell(app);
159
+ return Ok(());
160
+ }
161
+ }
162
+
163
+ let now_ts = chrono::Utc::now().timestamp();
164
+ let should_fire = {
165
+ let state = app
166
+ .try_state::<AdhocMeetingsWatcherState>()
167
+ .ok_or_else(|| "no AdhocMeetingsWatcherState".to_string())?;
168
+ let mut g = state.inner.lock().map_err(|e| e.to_string())?;
169
+
170
+ // Prune expired cooldowns.
171
+ g.cooldown_until.retain(|_, until| *until > now_ts);
172
+
173
+ if g.cooldown_until.get(platform).copied().unwrap_or(0) > now_ts {
174
+ return Ok(());
175
+ }
176
+ if g.session_notified.get(platform).copied().unwrap_or(false) {
177
+ return Ok(());
178
+ }
179
+
180
+ match (g.dwell_platform.as_deref(), g.dwell_since) {
181
+ (Some(p), Some(since)) if p == platform => {
182
+ if since.elapsed() >= Duration::from_secs(DWELL_SECS) {
183
+ g.session_notified.insert(platform.to_string(), true);
184
+ g.cooldown_until
185
+ .insert(platform.to_string(), now_ts + COOLDOWN_SECS);
186
+ g.dwell_platform = None;
187
+ g.dwell_since = None;
188
+ true
189
+ } else {
190
+ false
191
+ }
192
+ }
193
+ _ => {
194
+ g.dwell_platform = Some(platform.to_string());
195
+ g.dwell_since = Some(Instant::now());
196
+ false
197
+ }
198
+ }
199
+ };
200
+
201
+ if !should_fire {
202
+ return Ok(());
203
+ }
204
+
205
+ dlog!(
206
+ "[clips-tray] adhoc dwell met for {} — creating meeting",
207
+ platform
208
+ );
209
+
210
+ let meeting_id = match create_adhoc_meeting(app, client, platform).await {
211
+ Ok(id) => id,
212
+ Err(err) => {
213
+ // Allow retry on next dwell if create failed.
214
+ if let Some(state) = app.try_state::<AdhocMeetingsWatcherState>() {
215
+ if let Ok(mut g) = state.inner.lock() {
216
+ g.session_notified.remove(platform);
217
+ g.cooldown_until.remove(platform);
218
+ }
219
+ }
220
+ return Err(err);
221
+ }
222
+ };
223
+
224
+ let auto_start = config.meeting_transcription_mode == MeetingTranscriptionMode::Auto;
225
+ let show_widget = config.show_meeting_widget_enabled
226
+ || config.meeting_transcription_mode == MeetingTranscriptionMode::Ask
227
+ || auto_start;
228
+
229
+ if show_widget {
230
+ let app_clone = app.clone();
231
+ let id_clone = meeting_id.clone();
232
+ let title_clone = title.to_string();
233
+ let platform_clone = platform.to_string();
234
+ let scheduled_start = chrono::Utc::now().to_rfc3339();
235
+ tauri::async_runtime::spawn(async move {
236
+ let _ = crate::notifications::notify_meeting_starting(
237
+ app_clone,
238
+ id_clone,
239
+ title_clone,
240
+ 0,
241
+ None,
242
+ Some(scheduled_start),
243
+ None,
244
+ Some(platform_clone),
245
+ Some(auto_start),
246
+ Some("adhoc".to_string()),
247
+ )
248
+ .await;
249
+ });
250
+ }
251
+
252
+ if auto_start {
253
+ let _ = app.emit(
254
+ "meetings:start-transcription",
255
+ serde_json::json!({
256
+ "meetingId": meeting_id,
257
+ "joinUrl": null,
258
+ "reason": "adhoc-auto",
259
+ }),
260
+ );
261
+ }
262
+
263
+ Ok(())
264
+ }
265
+
266
+ fn reset_dwell(app: &AppHandle) {
267
+ if let Some(state) = app.try_state::<AdhocMeetingsWatcherState>() {
268
+ if let Ok(mut g) = state.inner.lock() {
269
+ g.dwell_platform = None;
270
+ g.dwell_since = None;
271
+ }
272
+ }
273
+ }
274
+
275
+ /// When the frontmost VC platform changes (or clears), drop session_notified
276
+ /// for platforms that are no longer front. Keep `cooldown_until` so leaving
277
+ /// and re-focusing Zoom within the cooldown does not spam another popup.
278
+ fn clear_session_if_left(app: &AppHandle, current: Option<&str>) {
279
+ if let Some(state) = app.try_state::<AdhocMeetingsWatcherState>() {
280
+ if let Ok(mut g) = state.inner.lock() {
281
+ g.session_notified
282
+ .retain(|platform, _| current == Some(platform.as_str()));
283
+ }
284
+ }
285
+ }
286
+
287
+ async fn create_adhoc_meeting(
288
+ app: &AppHandle,
289
+ client: &reqwest::Client,
290
+ platform: &str,
291
+ ) -> Result<String, String> {
292
+ let session = app
293
+ .try_state::<MeetingsWatcherState>()
294
+ .map(|s| s.session_snapshot())
295
+ .unwrap_or_default();
296
+ let Some(server_url) = session.server_url else {
297
+ return Err("no server_url for create-meeting".to_string());
298
+ };
299
+
300
+ let url = format!("{}/_agent-native/actions/create-meeting", server_url);
301
+ let row_title = if platform == "zoom" {
302
+ "Zoom meeting"
303
+ } else {
304
+ "Teams meeting"
305
+ };
306
+ let body = serde_json::json!({
307
+ "title": row_title,
308
+ "platform": platform,
309
+ "source": "adhoc",
310
+ "scheduledStart": chrono::Utc::now().to_rfc3339(),
311
+ });
312
+
313
+ let mut req = client
314
+ .post(&url)
315
+ .header("Content-Type", "application/json")
316
+ .header("X-Request-Source", "clips-desktop")
317
+ .json(&body);
318
+ if let Some(c) = session.session_cookie.as_deref() {
319
+ req = req.header("Cookie", c);
320
+ }
321
+ if let Some(token) = session.auth_token.as_deref() {
322
+ req = req.bearer_auth(token);
323
+ }
324
+
325
+ let resp = req
326
+ .send()
327
+ .await
328
+ .map_err(|e| format!("create-meeting fetch: {e}"))?;
329
+ let status = resp.status();
330
+ if status == reqwest::StatusCode::UNAUTHORIZED {
331
+ let _ = app.emit("meetings:auth-needed", serde_json::json!({}));
332
+ return Err("create-meeting http 401".to_string());
333
+ }
334
+ if !status.is_success() {
335
+ let text = resp.text().await.unwrap_or_default();
336
+ return Err(format!(
337
+ "create-meeting http {} — {}",
338
+ status,
339
+ text.chars().take(180).collect::<String>()
340
+ ));
341
+ }
342
+ let body: serde_json::Value = resp.json().await.map_err(|e| e.to_string())?;
343
+ extract_meeting_id(&body).ok_or_else(|| {
344
+ format!(
345
+ "create-meeting response missing meeting id: {}",
346
+ body.to_string().chars().take(200).collect::<String>()
347
+ )
348
+ })
349
+ }
350
+
351
+ fn extract_meeting_id(body: &serde_json::Value) -> Option<String> {
352
+ // Framework wraps action returns as `{ result: { meeting, created } }`.
353
+ let meeting = body
354
+ .get("result")
355
+ .and_then(|r| r.get("meeting"))
356
+ .or_else(|| body.get("meeting"))
357
+ .or_else(|| body.get("result"));
358
+ meeting
359
+ .and_then(|m| m.get("id"))
360
+ .and_then(|id| id.as_str())
361
+ .map(|s| s.to_string())
362
+ }