@agent-native/core 0.90.3 → 0.90.5

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 (256) hide show
  1. package/bin/agent-native.js +25 -3
  2. package/corpus/README.md +1 -1
  3. package/corpus/core/CHANGELOG.md +17 -0
  4. package/corpus/core/bin/agent-native.js +25 -3
  5. package/corpus/core/package.json +1 -1
  6. package/corpus/core/src/agent/durable-background.ts +18 -18
  7. package/corpus/core/src/agent/engine/ai-sdk-engine.ts +8 -2
  8. package/corpus/core/src/agent/engine/anthropic-engine.ts +11 -1
  9. package/corpus/core/src/agent/engine/index.ts +1 -0
  10. package/corpus/core/src/agent/engine/registry.ts +63 -2
  11. package/corpus/core/src/agent/production-agent.ts +22 -11
  12. package/corpus/core/src/agent/run-manager.ts +17 -6
  13. package/corpus/core/src/agent/run-store.ts +16 -11
  14. package/corpus/core/src/cli/create.ts +35 -0
  15. package/corpus/core/src/cli/design-connect.ts +263 -1
  16. package/corpus/core/src/cli/index.ts +84 -7
  17. package/corpus/core/src/cli/workspacify.ts +34 -0
  18. package/corpus/core/src/client/KeepTabOpenNotice.tsx +20 -15
  19. package/corpus/core/src/client/RunStuckBanner.tsx +14 -13
  20. package/corpus/core/src/client/agent-chat-adapter.ts +28 -17
  21. package/corpus/core/src/client/history/VersionHistoryPanel.tsx +77 -47
  22. package/corpus/core/src/client/history/index.ts +3 -0
  23. package/corpus/core/src/client/history/use-history.ts +28 -0
  24. package/corpus/core/src/client/index.ts +8 -0
  25. package/corpus/core/src/client/review/index.ts +5 -0
  26. package/corpus/core/src/client/review/use-review.ts +40 -2
  27. package/corpus/core/src/client/use-run-stuck-detection.ts +8 -5
  28. package/corpus/core/src/history/actions/create-resource-version.ts +11 -8
  29. package/corpus/core/src/history/index.ts +0 -1
  30. package/corpus/core/src/history/registry.ts +24 -12
  31. package/corpus/core/src/history/store.ts +84 -53
  32. package/corpus/core/src/review/actions/consume-review-feedback.ts +15 -5
  33. package/corpus/core/src/review/actions/delete-review-comment.ts +15 -3
  34. package/corpus/core/src/review/actions/resolve-review-thread.ts +12 -5
  35. package/corpus/core/src/review/index.ts +0 -5
  36. package/corpus/core/src/review/registry.ts +24 -12
  37. package/corpus/core/src/review/store.ts +10 -7
  38. package/corpus/core/src/server/agent-chat-plugin.ts +6 -0
  39. package/corpus/core/src/server/credential-provider.ts +13 -3
  40. package/corpus/templates/assets/actions/_preset-skeleton-validation.ts +11 -11
  41. package/corpus/templates/assets/actions/generate-image.ts +64 -28
  42. package/corpus/templates/assets/app/components/generation/GenerationResults.tsx +1 -1
  43. package/corpus/templates/assets/app/routes/brand-kits.$id_.presets.$presetId.tsx +91 -74
  44. package/corpus/templates/assets/server/lib/generation.ts +29 -4
  45. package/corpus/templates/assets/server/lib/image-processing.ts +131 -0
  46. package/corpus/templates/calendar/.agents/skills/event-management/SKILL.md +24 -2
  47. package/corpus/templates/calendar/AGENTS.md +3 -0
  48. package/corpus/templates/calendar/actions/create-event.ts +8 -34
  49. package/corpus/templates/calendar/actions/event-action-helpers.ts +52 -0
  50. package/corpus/templates/calendar/actions/manage-event-draft.ts +7 -34
  51. package/corpus/templates/calendar/actions/update-event.ts +10 -23
  52. package/corpus/templates/calendar/app/components/calendar/AttendeeAutocomplete.tsx +47 -0
  53. package/corpus/templates/calendar/app/components/calendar/CreateEventDialog.tsx +23 -0
  54. package/corpus/templates/calendar/app/components/calendar/EventAttendeesSection.tsx +59 -2
  55. package/corpus/templates/calendar/app/components/calendar/EventDetailPanel.tsx +40 -1
  56. package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +34 -1
  57. package/corpus/templates/calendar/app/hooks/use-events.ts +6 -0
  58. package/corpus/templates/calendar/app/i18n/zh-TW.ts +4 -0
  59. package/corpus/templates/calendar/app/i18n-data.ts +40 -0
  60. package/corpus/templates/calendar/changelog/2026-07-07-you-can-mark-event-guests-as-optional-while-inviting-them-or.md +6 -0
  61. package/corpus/templates/calendar/server/handlers/events.ts +1 -0
  62. package/corpus/templates/calendar/server/lib/google-calendar.ts +4 -0
  63. package/corpus/templates/calendar/server/plugins/agent-chat.ts +1 -1
  64. package/corpus/templates/calendar/shared/api.ts +2 -0
  65. package/corpus/templates/clips/.agents/skills/ai-video-tools/SKILL.md +29 -3
  66. package/corpus/templates/clips/actions/generate-workflow.ts +19 -8
  67. package/corpus/templates/clips/actions/get-clips-ai-prefs.ts +36 -0
  68. package/corpus/templates/clips/actions/lib/clips-ai-prefs.ts +28 -0
  69. package/corpus/templates/clips/actions/regenerate-chapters.ts +21 -7
  70. package/corpus/templates/clips/actions/regenerate-summary.ts +26 -8
  71. package/corpus/templates/clips/actions/regenerate-title.ts +44 -7
  72. package/corpus/templates/clips/actions/update-clips-ai-prefs.ts +50 -0
  73. package/corpus/templates/clips/app/components/meetings/meeting-card.tsx +7 -2
  74. package/corpus/templates/clips/app/hooks/use-auto-title.ts +25 -10
  75. package/corpus/templates/clips/app/i18n/ar-SA.ts +28 -6
  76. package/corpus/templates/clips/app/i18n/de-DE.ts +28 -6
  77. package/corpus/templates/clips/app/i18n/en-US.ts +29 -6
  78. package/corpus/templates/clips/app/i18n/es-ES.ts +29 -6
  79. package/corpus/templates/clips/app/i18n/fr-FR.ts +30 -6
  80. package/corpus/templates/clips/app/i18n/hi-IN.ts +26 -6
  81. package/corpus/templates/clips/app/i18n/ja-JP.ts +27 -6
  82. package/corpus/templates/clips/app/i18n/ko-KR.ts +28 -6
  83. package/corpus/templates/clips/app/i18n/pt-BR.ts +28 -6
  84. package/corpus/templates/clips/app/i18n/zh-CN.ts +26 -6
  85. package/corpus/templates/clips/app/i18n/zh-TW.ts +25 -6
  86. package/corpus/templates/clips/app/routes/_app.meetings._index.tsx +73 -0
  87. package/corpus/templates/clips/app/routes/_app.settings._index.tsx +1 -0
  88. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +70 -2
  89. package/corpus/templates/clips/changelog/2026-07-07-ai-tools-can-include-the-full-video-not-just-the-transcript-.md +6 -0
  90. package/corpus/templates/clips/changelog/2026-07-07-clips-desktop-now-surfaces-meetings-and-dictate-directly-in-.md +6 -0
  91. package/corpus/templates/clips/changelog/2026-07-07-meeting-notes-now-explain-exactly-how-to-start-granola-style.md +6 -0
  92. package/corpus/templates/clips/changelog/2026-07-07-meeting-start-alerts-now-include-separate-start-notes-and-jo.md +6 -0
  93. package/corpus/templates/clips/changelog/2026-07-07-recordings-start-more-predictably-and-newly-saved-clips-refr.md +6 -0
  94. package/corpus/templates/clips/chrome-extension/public/manifest.json +1 -1
  95. package/corpus/templates/clips/desktop/src/app.tsx +435 -105
  96. package/corpus/templates/clips/desktop/src/lib/recorder.ts +27 -21
  97. package/corpus/templates/clips/desktop/src/overlays/meeting-notification.tsx +23 -22
  98. package/corpus/templates/clips/desktop/src/styles.css +167 -2
  99. package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +1 -1
  100. package/corpus/templates/clips/desktop/src-tauri/src/notifications.rs +3 -7
  101. package/corpus/templates/clips/desktop/src-tauri/src/tray_meetings.rs +5 -4
  102. package/corpus/templates/clips/server/routes/_agent-native/clips/user-prefs.put.ts +16 -8
  103. package/corpus/templates/clips/shared/clips-ai-prefs.ts +81 -0
  104. package/corpus/templates/content/AGENTS.md +1 -0
  105. package/corpus/templates/content/actions/_database-source-utils.ts +73 -8
  106. package/corpus/templates/content/actions/_database-utils.ts +2 -1
  107. package/corpus/templates/content/actions/prepare-builder-source-review.ts +20 -1
  108. package/corpus/templates/content/actions/stage-builder-source-bulk-update.ts +663 -0
  109. package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +33 -7
  110. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +53 -4
  111. package/corpus/templates/content/app/components/editor/database/settings.tsx +24 -0
  112. package/corpus/templates/content/app/hooks/use-content-database.ts +70 -1
  113. package/corpus/templates/content/app/hooks/use-document-properties.ts +63 -5
  114. package/corpus/templates/content/app/i18n/zh-TW.ts +3 -0
  115. package/corpus/templates/content/app/i18n-data.ts +31 -0
  116. package/corpus/templates/content/changelog/2026-07-01-builder-source-rows-can-be-staged-in-bulk-with-a-reviewable.md +6 -0
  117. package/corpus/templates/content/changelog/2026-07-07-builder-backed-database-fields-now-edit-like-normal-content-fields.md +6 -0
  118. package/corpus/templates/content/parity/matrix.md +24 -24
  119. package/corpus/templates/content/parity/matrix.ts +2 -0
  120. package/corpus/templates/content/shared/api.ts +59 -4
  121. package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +16 -1
  122. package/corpus/templates/design/actions/add-localhost-screens.ts +1 -0
  123. package/corpus/templates/design/actions/connect-localhost.ts +26 -3
  124. package/corpus/templates/design/actions/open-visual-edit.ts +4 -37
  125. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +362 -12
  126. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +89 -14
  127. package/corpus/templates/design/app/components/design/bridge-security.ts +7 -1
  128. package/corpus/templates/design/app/components/design/multi-screen/types.ts +1 -1
  129. package/corpus/templates/design/app/i18n/zh-TW.ts +3 -0
  130. package/corpus/templates/design/app/i18n-data.ts +41 -0
  131. package/corpus/templates/design/changelog/2026-07-07-local-visual-edit-keeps-bridge-access-when-opening-previousl.md +6 -0
  132. package/corpus/templates/design/changelog/2026-07-07-local-visual-edit-now-keeps-the-live-app-connection-authenti.md +6 -0
  133. package/corpus/templates/design/changelog/2026-07-07-local-visual-edit-uses-live-app-iframes-for-click-to-edit-pr.md +6 -0
  134. package/dist/agent/durable-background.d.ts +12 -12
  135. package/dist/agent/durable-background.js +18 -18
  136. package/dist/agent/durable-background.js.map +1 -1
  137. package/dist/agent/engine/ai-sdk-engine.d.ts.map +1 -1
  138. package/dist/agent/engine/ai-sdk-engine.js +8 -2
  139. package/dist/agent/engine/ai-sdk-engine.js.map +1 -1
  140. package/dist/agent/engine/anthropic-engine.d.ts.map +1 -1
  141. package/dist/agent/engine/anthropic-engine.js +11 -1
  142. package/dist/agent/engine/anthropic-engine.js.map +1 -1
  143. package/dist/agent/engine/index.d.ts +1 -1
  144. package/dist/agent/engine/index.d.ts.map +1 -1
  145. package/dist/agent/engine/index.js +1 -1
  146. package/dist/agent/engine/index.js.map +1 -1
  147. package/dist/agent/engine/registry.d.ts +14 -0
  148. package/dist/agent/engine/registry.d.ts.map +1 -1
  149. package/dist/agent/engine/registry.js +56 -2
  150. package/dist/agent/engine/registry.js.map +1 -1
  151. package/dist/agent/production-agent.d.ts +2 -2
  152. package/dist/agent/production-agent.d.ts.map +1 -1
  153. package/dist/agent/production-agent.js +18 -11
  154. package/dist/agent/production-agent.js.map +1 -1
  155. package/dist/agent/run-manager.d.ts +7 -1
  156. package/dist/agent/run-manager.d.ts.map +1 -1
  157. package/dist/agent/run-manager.js +6 -1
  158. package/dist/agent/run-manager.js.map +1 -1
  159. package/dist/agent/run-store.d.ts +1 -1
  160. package/dist/agent/run-store.d.ts.map +1 -1
  161. package/dist/agent/run-store.js +15 -10
  162. package/dist/agent/run-store.js.map +1 -1
  163. package/dist/cli/create.d.ts.map +1 -1
  164. package/dist/cli/create.js +31 -0
  165. package/dist/cli/create.js.map +1 -1
  166. package/dist/cli/design-connect.d.ts.map +1 -1
  167. package/dist/cli/design-connect.js +197 -1
  168. package/dist/cli/design-connect.js.map +1 -1
  169. package/dist/cli/index.js +76 -9
  170. package/dist/cli/index.js.map +1 -1
  171. package/dist/cli/workspacify.d.ts.map +1 -1
  172. package/dist/cli/workspacify.js +30 -0
  173. package/dist/cli/workspacify.js.map +1 -1
  174. package/dist/client/KeepTabOpenNotice.d.ts +9 -10
  175. package/dist/client/KeepTabOpenNotice.d.ts.map +1 -1
  176. package/dist/client/KeepTabOpenNotice.js +8 -4
  177. package/dist/client/KeepTabOpenNotice.js.map +1 -1
  178. package/dist/client/RunStuckBanner.d.ts.map +1 -1
  179. package/dist/client/RunStuckBanner.js +14 -13
  180. package/dist/client/RunStuckBanner.js.map +1 -1
  181. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  182. package/dist/client/agent-chat-adapter.js +25 -17
  183. package/dist/client/agent-chat-adapter.js.map +1 -1
  184. package/dist/client/history/VersionHistoryPanel.d.ts +1 -1
  185. package/dist/client/history/VersionHistoryPanel.d.ts.map +1 -1
  186. package/dist/client/history/VersionHistoryPanel.js +12 -5
  187. package/dist/client/history/VersionHistoryPanel.js.map +1 -1
  188. package/dist/client/history/index.d.ts +1 -1
  189. package/dist/client/history/index.d.ts.map +1 -1
  190. package/dist/client/history/index.js +1 -1
  191. package/dist/client/history/index.js.map +1 -1
  192. package/dist/client/history/use-history.d.ts +12 -0
  193. package/dist/client/history/use-history.d.ts.map +1 -1
  194. package/dist/client/history/use-history.js +6 -0
  195. package/dist/client/history/use-history.js.map +1 -1
  196. package/dist/client/index.d.ts +2 -2
  197. package/dist/client/index.d.ts.map +1 -1
  198. package/dist/client/index.js +2 -2
  199. package/dist/client/index.js.map +1 -1
  200. package/dist/client/review/index.d.ts +1 -1
  201. package/dist/client/review/index.d.ts.map +1 -1
  202. package/dist/client/review/index.js +1 -1
  203. package/dist/client/review/index.js.map +1 -1
  204. package/dist/client/review/use-review.d.ts +23 -0
  205. package/dist/client/review/use-review.d.ts.map +1 -1
  206. package/dist/client/review/use-review.js +8 -0
  207. package/dist/client/review/use-review.js.map +1 -1
  208. package/dist/client/use-run-stuck-detection.d.ts +1 -1
  209. package/dist/client/use-run-stuck-detection.d.ts.map +1 -1
  210. package/dist/client/use-run-stuck-detection.js +6 -4
  211. package/dist/client/use-run-stuck-detection.js.map +1 -1
  212. package/dist/collab/struct-routes.d.ts +1 -1
  213. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  214. package/dist/history/actions/create-resource-version.js +7 -4
  215. package/dist/history/actions/create-resource-version.js.map +1 -1
  216. package/dist/history/index.d.ts +1 -1
  217. package/dist/history/index.d.ts.map +1 -1
  218. package/dist/history/index.js +1 -1
  219. package/dist/history/index.js.map +1 -1
  220. package/dist/history/registry.d.ts.map +1 -1
  221. package/dist/history/registry.js +14 -12
  222. package/dist/history/registry.js.map +1 -1
  223. package/dist/history/store.d.ts.map +1 -1
  224. package/dist/history/store.js +74 -49
  225. package/dist/history/store.js.map +1 -1
  226. package/dist/notifications/routes.d.ts +1 -1
  227. package/dist/resources/handlers.d.ts +3 -3
  228. package/dist/review/actions/consume-review-feedback.d.ts +1 -0
  229. package/dist/review/actions/consume-review-feedback.js +8 -2
  230. package/dist/review/actions/consume-review-feedback.js.map +1 -1
  231. package/dist/review/actions/delete-review-comment.d.ts +2 -1
  232. package/dist/review/actions/delete-review-comment.js +11 -3
  233. package/dist/review/actions/delete-review-comment.js.map +1 -1
  234. package/dist/review/actions/resolve-review-thread.d.ts +2 -1
  235. package/dist/review/actions/resolve-review-thread.js +5 -2
  236. package/dist/review/actions/resolve-review-thread.js.map +1 -1
  237. package/dist/review/index.d.ts +1 -1
  238. package/dist/review/index.d.ts.map +1 -1
  239. package/dist/review/index.js +1 -1
  240. package/dist/review/index.js.map +1 -1
  241. package/dist/review/registry.d.ts.map +1 -1
  242. package/dist/review/registry.js +14 -12
  243. package/dist/review/registry.js.map +1 -1
  244. package/dist/review/store.d.ts +3 -3
  245. package/dist/review/store.d.ts.map +1 -1
  246. package/dist/review/store.js +7 -4
  247. package/dist/review/store.js.map +1 -1
  248. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  249. package/dist/server/agent-chat-plugin.js +5 -0
  250. package/dist/server/agent-chat-plugin.js.map +1 -1
  251. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  252. package/dist/server/credential-provider.d.ts +2 -0
  253. package/dist/server/credential-provider.d.ts.map +1 -1
  254. package/dist/server/credential-provider.js +13 -3
  255. package/dist/server/credential-provider.js.map +1 -1
  256. package/package.json +1 -1
@@ -999,7 +999,6 @@ async function captureTitleForRecording(params: {
999
999
  }
1000
1000
 
1001
1001
  const COUNTDOWN_EVENT_TIMEOUT_MS = 5000;
1002
- const COUNTDOWN_OVERLAY_SETTLE_MS = 120;
1003
1002
 
1004
1003
  function wait(ms: number): Promise<void> {
1005
1004
  return new Promise((resolve) => window.setTimeout(resolve, ms));
@@ -1652,10 +1651,6 @@ async function runRecordingCountdown(wantsScreen: boolean) {
1652
1651
  console.warn("[clips-recorder] countdown timed out — proceeding");
1653
1652
  return;
1654
1653
  }
1655
- // The countdown webview emits before it finishes closing. Give macOS a
1656
- // brief beat to remove the overlay and any shortcut handling before capture
1657
- // starts, so the first frame/audio sample is the real recording.
1658
- await wait(COUNTDOWN_OVERLAY_SETTLE_MS);
1659
1654
  }
1660
1655
 
1661
1656
  function showFinalizingFeedback() {
@@ -1852,29 +1847,35 @@ async function startNativeFullscreenRecording(
1852
1847
  }).catch((err) => {
1853
1848
  console.warn("[clips-recorder] mic warm failed:", err);
1854
1849
  });
1855
- const countdownPromise = runRecordingCountdown(true);
1856
1850
  if (localOnly) {
1851
+ const countdownPromise = runRecordingCountdown(true);
1857
1852
  id = localFolderName;
1858
1853
  await Promise.all([countdownPromise, warmMic(id)]);
1859
1854
  } else {
1860
- const captureTitle = await captureTitleForRecording({
1855
+ const captureTitlePromise = captureTitleForRecording({
1861
1856
  mode: params.mode,
1862
1857
  source: params.source,
1863
1858
  });
1864
- console.time("[clips-recorder] createServerRecording duration");
1865
- const recordingPromise = createServerRecording(
1866
- params.serverUrl,
1867
- wantsCamera,
1868
- wantsRecordedAudio,
1869
- captureTitle,
1870
- {
1871
- mimeType: NATIVE_FULLSCREEN_MIME_TYPE,
1872
- requestStreaming: true,
1873
- streamingUploadClient: "desktop-native",
1874
- },
1875
- ).finally(() => {
1876
- console.timeEnd("[clips-recorder] createServerRecording duration");
1877
- });
1859
+ const countdownPromise = runRecordingCountdown(true);
1860
+ const recordingPromise = (async () => {
1861
+ const captureTitle = await captureTitlePromise;
1862
+ console.time("[clips-recorder] createServerRecording duration");
1863
+ try {
1864
+ return await createServerRecording(
1865
+ params.serverUrl,
1866
+ wantsCamera,
1867
+ wantsRecordedAudio,
1868
+ captureTitle,
1869
+ {
1870
+ mimeType: NATIVE_FULLSCREEN_MIME_TYPE,
1871
+ requestStreaming: true,
1872
+ streamingUploadClient: "desktop-native",
1873
+ },
1874
+ );
1875
+ } finally {
1876
+ console.timeEnd("[clips-recorder] createServerRecording duration");
1877
+ }
1878
+ })();
1878
1879
  // The recording id usually lands well before the countdown ends — warm
1879
1880
  // native capture as soon as it does so setup overlaps the 3-2-1.
1880
1881
  const warmAndId = (async () => {
@@ -1918,6 +1919,11 @@ async function startNativeFullscreenRecording(
1918
1919
  // Capture is now live — after rebasing the transcript timeline, stamp the
1919
1920
  // timer baseline so the toolbar clock lines up with the real start.
1920
1921
  startedAt = Date.now();
1922
+ emit("clips:toolbar-enabled", true).catch(() => {});
1923
+ emit("clips:recorder-state", {
1924
+ paused: false,
1925
+ elapsedMs: 0,
1926
+ }).catch(() => {});
1921
1927
  localCameraExport?.start(2_000);
1922
1928
  } catch (err) {
1923
1929
  await localCameraExport?.cancel().catch(() => {});
@@ -23,9 +23,8 @@ const DEFAULT_AUTO_HIDE_MS = 30_000;
23
23
  const SNOOZE_MS = 5 * 60_000;
24
24
 
25
25
  /**
26
- * Open a meeting join URL via the Tauri shell plugin. Centralized here so
27
- * both the notification's "Take Notes" and the watcher's
28
- * `meetings:start-transcription` event use the same path.
26
+ * Open a meeting join URL via the Tauri shell plugin. Used by the
27
+ * notification's dedicated Join CTA (notes start is a separate action).
29
28
  */
30
29
  async function openJoinUrl(url: string | null | undefined): Promise<void> {
31
30
  if (!url) return;
@@ -41,7 +40,7 @@ async function openJoinUrl(url: string | null | undefined): Promise<void> {
41
40
  * Variants:
42
41
  *
43
42
  * - Calendar event: solid left bar (green), meeting title, time,
44
- * "Start notes" + "Snooze 5 min" buttons.
43
+ * "Start notes" + optional "Join" + "Snooze 5 min" buttons.
45
44
  * - Ad-hoc call: dashed left bar (slate), "Call detected", app name,
46
45
  * same controls.
47
46
  *
@@ -170,11 +169,6 @@ export function MeetingNotification() {
170
169
  if (!data || pending) return;
171
170
  setPending(true);
172
171
  setError(null);
173
- // Open the join URL right away so the user feels the click — the
174
- // recording action runs in parallel.
175
- if (data.joinUrl) {
176
- openJoinUrl(data.joinUrl);
177
- }
178
172
  emit("meetings:start-transcription", {
179
173
  meetingId: data.meetingId,
180
174
  joinUrl: data.joinUrl,
@@ -185,25 +179,20 @@ export function MeetingNotification() {
185
179
  });
186
180
  }
187
181
 
182
+ async function joinMeeting() {
183
+ if (!data?.joinUrl) return;
184
+ await openJoinUrl(data.joinUrl);
185
+ }
186
+
188
187
  function snooze() {
189
188
  if (!data) return;
190
189
  // Hand the snooze to the Rust watcher so the reminder re-fires after the
191
190
  // delay even though this overlay window closes right away. A setTimeout
192
- // here would be torn down with the window and never fire, and re-emitting
193
- // immediately (as before) just re-showed the same banner instantly.
191
+ // here would be torn down with the window and never fire.
194
192
  invoke("meetings_snooze", {
195
193
  meetingId: data.meetingId,
196
194
  minutes: Math.round(SNOOZE_MS / 60_000),
197
195
  }).catch(() => {});
198
- const payload = data;
199
- // Hide the current banner and re-fire the same payload after the snooze
200
- // delay. We re-emit through Tauri so the in-app banner overlay window
201
- // (which is what hosts this React component) can rerender with a fresh
202
- // auto-hide timer.
203
- emit("meetings:show-notification", {
204
- ...payload,
205
- subtitle: `${payload.subtitle} (snoozed)`,
206
- } as NotificationData).catch(() => {});
207
196
  hideNotification();
208
197
  }
209
198
 
@@ -212,6 +201,7 @@ export function MeetingNotification() {
212
201
  }
213
202
 
214
203
  const isCalendar = data.type === "calendar";
204
+ const hasJoin = Boolean(data.joinUrl);
215
205
 
216
206
  return (
217
207
  <div
@@ -227,7 +217,9 @@ export function MeetingNotification() {
227
217
  scheduleAutoHide(DEFAULT_AUTO_HIDE_MS);
228
218
  }}
229
219
  >
230
- <div className="meeting-notification">
220
+ <div
221
+ className={`meeting-notification${hasJoin ? " meeting-notification-with-join" : ""}`}
222
+ >
231
223
  <div
232
224
  className={`meeting-notification-bar ${isCalendar ? "meeting-notification-bar-calendar" : "meeting-notification-bar-adhoc"}`}
233
225
  />
@@ -243,13 +235,22 @@ export function MeetingNotification() {
243
235
  </div>
244
236
  <div className="meeting-notification-actions">
245
237
  <button
246
- className="meeting-notification-btn"
238
+ className="meeting-notification-btn meeting-notification-btn-primary"
247
239
  onClick={takeNotes}
248
240
  disabled={pending}
249
241
  data-no-drag
250
242
  >
251
243
  {pending ? "Starting…" : "Start notes"}
252
244
  </button>
245
+ {hasJoin ? (
246
+ <button
247
+ className="meeting-notification-btn meeting-notification-btn-secondary"
248
+ onClick={joinMeeting}
249
+ data-no-drag
250
+ >
251
+ Join
252
+ </button>
253
+ ) : null}
253
254
  <button
254
255
  className="meeting-notification-btn meeting-notification-btn-secondary"
255
256
  onClick={snooze}
@@ -1096,7 +1096,7 @@ body[data-clips-route="recording-pill"] #root {
1096
1096
 
1097
1097
  .bottom-row {
1098
1098
  display: grid;
1099
- grid-template-columns: repeat(3, 1fr);
1099
+ grid-template-columns: repeat(4, 1fr);
1100
1100
  gap: 4px;
1101
1101
  }
1102
1102
 
@@ -1138,6 +1138,145 @@ body[data-clips-route="recording-pill"] #root {
1138
1138
  font-weight: 500;
1139
1139
  }
1140
1140
 
1141
+ /* ------------------------------------------------------------------------- */
1142
+ /* Popover drill-in views */
1143
+ /* ------------------------------------------------------------------------- */
1144
+
1145
+ .app-popover-view {
1146
+ gap: 0;
1147
+ padding: 0;
1148
+ }
1149
+
1150
+ .popover-view {
1151
+ min-height: 260px;
1152
+ }
1153
+
1154
+ .popover-view-header {
1155
+ margin-bottom: 2px;
1156
+ }
1157
+
1158
+ .popover-view-header-spacer {
1159
+ flex: 1;
1160
+ }
1161
+
1162
+ .popover-view-link {
1163
+ font-size: 11px;
1164
+ }
1165
+
1166
+ .popover-empty-card {
1167
+ display: flex;
1168
+ flex-direction: column;
1169
+ gap: 8px;
1170
+ padding: 12px;
1171
+ border: 1px solid var(--border);
1172
+ border-radius: var(--radius);
1173
+ background: var(--surface);
1174
+ }
1175
+
1176
+ .popover-empty-card strong {
1177
+ font-size: 13px;
1178
+ color: var(--fg);
1179
+ }
1180
+
1181
+ .popover-card-heading {
1182
+ display: flex;
1183
+ align-items: center;
1184
+ gap: 8px;
1185
+ }
1186
+
1187
+ .popover-list {
1188
+ display: flex;
1189
+ flex-direction: column;
1190
+ gap: 6px;
1191
+ }
1192
+
1193
+ .popover-list-item {
1194
+ display: flex;
1195
+ align-items: center;
1196
+ gap: 9px;
1197
+ padding: 9px;
1198
+ border: 1px solid var(--border);
1199
+ border-radius: var(--radius);
1200
+ background: var(--surface);
1201
+ }
1202
+
1203
+ .popover-list-icon {
1204
+ display: inline-flex;
1205
+ align-items: center;
1206
+ justify-content: center;
1207
+ color: var(--fg-muted);
1208
+ flex-shrink: 0;
1209
+ }
1210
+
1211
+ .popover-list-main {
1212
+ flex: 1;
1213
+ min-width: 0;
1214
+ }
1215
+
1216
+ .popover-list-title {
1217
+ font-size: 12px;
1218
+ font-weight: 600;
1219
+ color: var(--fg);
1220
+ white-space: nowrap;
1221
+ overflow: hidden;
1222
+ text-overflow: ellipsis;
1223
+ }
1224
+
1225
+ .popover-list-sub {
1226
+ margin-top: 1px;
1227
+ font-size: 11px;
1228
+ color: var(--fg-subtle);
1229
+ white-space: nowrap;
1230
+ overflow: hidden;
1231
+ text-overflow: ellipsis;
1232
+ }
1233
+
1234
+ .popover-list-action {
1235
+ flex-shrink: 0;
1236
+ border: 1px solid var(--border-strong);
1237
+ border-radius: var(--radius-sm);
1238
+ background: var(--bg);
1239
+ color: var(--fg);
1240
+ padding: 6px 8px;
1241
+ font-size: 11px;
1242
+ font-weight: 600;
1243
+ cursor: pointer;
1244
+ }
1245
+
1246
+ .popover-list-action:hover {
1247
+ background: var(--surface-hover);
1248
+ }
1249
+
1250
+ .popover-list-action-primary {
1251
+ border-color: var(--brand);
1252
+ background: var(--brand);
1253
+ color: var(--bg);
1254
+ }
1255
+
1256
+ .popover-list-action-primary:hover {
1257
+ background: var(--brand-hover);
1258
+ }
1259
+
1260
+ .popover-kv {
1261
+ display: flex;
1262
+ align-items: center;
1263
+ justify-content: space-between;
1264
+ gap: 12px;
1265
+ padding-top: 6px;
1266
+ border-top: 1px solid var(--border);
1267
+ font-size: 11px;
1268
+ }
1269
+
1270
+ .popover-kv span {
1271
+ color: var(--fg-subtle);
1272
+ }
1273
+
1274
+ .popover-kv strong {
1275
+ min-width: 0;
1276
+ text-align: right;
1277
+ font-size: 11px;
1278
+ }
1279
+
1141
1280
  .badge {
1142
1281
  position: absolute;
1143
1282
  top: -4px;
@@ -3468,7 +3607,19 @@ body[data-clips-route="recording-pill"] #root {
3468
3607
  color: white;
3469
3608
  pointer-events: auto;
3470
3609
  min-width: 280px;
3471
- max-width: 320px;
3610
+ max-width: 400px;
3611
+ }
3612
+
3613
+ .meeting-notification-with-join {
3614
+ flex-wrap: wrap;
3615
+ align-items: flex-start;
3616
+ row-gap: 10px;
3617
+ }
3618
+
3619
+ .meeting-notification-with-join .meeting-notification-actions {
3620
+ flex: 1 1 100%;
3621
+ justify-content: flex-end;
3622
+ padding-left: 13px; /* align under content past the bar */
3472
3623
  }
3473
3624
 
3474
3625
  .meeting-notification-bar {
@@ -3536,6 +3687,20 @@ body[data-clips-route="recording-pill"] #root {
3536
3687
  cursor: progress;
3537
3688
  }
3538
3689
 
3690
+ .meeting-notification-btn-primary {
3691
+ background: #ffffff;
3692
+ color: #111827;
3693
+ }
3694
+
3695
+ .meeting-notification-btn-primary:hover {
3696
+ background: #f3f4f6;
3697
+ }
3698
+
3699
+ .meeting-notification-btn-primary:disabled {
3700
+ background: rgba(255, 255, 255, 0.7);
3701
+ color: #374151;
3702
+ }
3703
+
3539
3704
  .meeting-notification-actions {
3540
3705
  display: inline-flex;
3541
3706
  align-items: center;
@@ -648,7 +648,7 @@ pub async fn native_fullscreen_recording_available() -> Result<bool, String> {
648
648
  /// the countdown by now; this is just a safety net so a slow/denied mic can't
649
649
  /// stall the start.
650
650
  #[cfg(target_os = "macos")]
651
- const MIC_WARM_TIMEOUT_MS: u64 = 1500;
651
+ const MIC_WARM_TIMEOUT_MS: u64 = 250;
652
652
 
653
653
  /// Acquire a backend (ScreenCaptureKit, or the screencapture fallback) and
654
654
  /// store it as the active session. Shared by the immediate-start command and
@@ -16,8 +16,8 @@ use crate::util::{
16
16
  };
17
17
 
18
18
  const MEETING_NOTIFICATION_LABEL: &str = "meeting-notif";
19
- const NOTIFICATION_W_LOGICAL: u32 = 380;
20
- const NOTIFICATION_H_LOGICAL: u32 = 132;
19
+ const NOTIFICATION_W_LOGICAL: u32 = 420;
20
+ const NOTIFICATION_H_LOGICAL: u32 = 148;
21
21
  const NOTIFICATION_TOP_MARGIN_LOGICAL: u32 = 44;
22
22
  const NOTIFICATION_RIGHT_MARGIN_LOGICAL: u32 = 24;
23
23
 
@@ -125,11 +125,7 @@ pub async fn notify_meeting_starting(
125
125
  } else {
126
126
  format!("in {} min", (starts_in_secs / 60).max(1))
127
127
  };
128
- let body = if join_url.is_some() {
129
- format!("{} — Start notes", pretty_in)
130
- } else {
131
- format!("Starts {}", pretty_in)
132
- };
128
+ let body = format!("Starts {}", pretty_in);
133
129
  dlog!(
134
130
  "[clips-tray] notify_meeting_starting id={} title={} body={}",
135
131
  meeting_id,
@@ -25,21 +25,22 @@ pub struct MeetingItem {
25
25
 
26
26
  pub const MEETING_ID_PREFIX: &str = "meeting:";
27
27
 
28
- /// Build the "Upcoming Meetings" submenu populated with up to 3 events. If
28
+ /// Build the "Start Meeting Notes" submenu populated with up to 3 events. If
29
29
  /// the list is empty, returns a submenu containing a single disabled "No
30
- /// upcoming meetings" item — keeps the menu structure stable.
30
+ /// meetings ready" item — keeps the menu structure stable. Selecting a real
31
+ /// meeting starts live notes immediately.
31
32
  pub fn build_meetings_section(
32
33
  app: &AppHandle,
33
34
  upcoming: Vec<MeetingItem>,
34
35
  ) -> Result<Submenu<Wry>, Box<dyn std::error::Error>> {
35
36
  let mut builder: SubmenuBuilder<'_, Wry, AppHandle> =
36
- SubmenuBuilder::new(app, "Upcoming Meetings");
37
+ SubmenuBuilder::new(app, "Start Meeting Notes");
37
38
 
38
39
  if upcoming.is_empty() {
39
40
  let placeholder = MenuItem::with_id(
40
41
  app,
41
42
  "meeting:none",
42
- "No upcoming meetings",
43
+ "No meetings ready",
43
44
  false,
44
45
  None::<&str>,
45
46
  )?;
@@ -1,5 +1,5 @@
1
1
  import { getSession } from "@agent-native/core/server";
2
- import { putUserSetting } from "@agent-native/core/settings";
2
+ import { getUserSetting, putUserSetting } from "@agent-native/core/settings";
3
3
  import { defineEventHandler, getHeader, readBody, setResponseStatus } from "h3";
4
4
 
5
5
  const CLIPS_USER_PREFS_KEY = "clips-user-prefs";
@@ -17,11 +17,19 @@ export default defineEventHandler(async (event) => {
17
17
  return { error: "Invalid settings payload" };
18
18
  }
19
19
 
20
- await putUserSetting(
21
- session.email,
22
- CLIPS_USER_PREFS_KEY,
23
- body as Record<string, unknown>,
24
- { requestSource: getHeader(event, "x-request-source") || undefined },
25
- );
26
- return body;
20
+ // Merge so partial Settings saves (playback, notifications, …) don't wipe
21
+ // AI-tool prefs written from the recording page popover.
22
+ const existing =
23
+ (await getUserSetting(session.email, CLIPS_USER_PREFS_KEY)) ?? {};
24
+ const next = {
25
+ ...(typeof existing === "object" && existing && !Array.isArray(existing)
26
+ ? existing
27
+ : {}),
28
+ ...(body as Record<string, unknown>),
29
+ };
30
+
31
+ await putUserSetting(session.email, CLIPS_USER_PREFS_KEY, next, {
32
+ requestSource: getHeader(event, "x-request-source") || undefined,
33
+ });
34
+ return next;
27
35
  });
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Clips user preferences that affect AI video tools.
3
+ *
4
+ * Stored under the shared `clips-user-prefs` user-setting key so Settings and
5
+ * the AI tools popover both read/write the same object.
6
+ */
7
+
8
+ export const CLIPS_USER_PREFS_KEY = "clips-user-prefs";
9
+
10
+ /**
11
+ * Preferred chat model when Include full video is on. Full-recording video
12
+ * input only works with Gemini (Builder Gemini or Google/Gemini BYOK) —
13
+ * Claude and OpenAI cannot ingest the MP4/WebM.
14
+ */
15
+ export const CLIPS_FULL_VIDEO_AI_ENGINE = "builder";
16
+ export const CLIPS_FULL_VIDEO_AI_MODEL = "gemini-3-5-flash";
17
+
18
+ export type ClipsAiPrefs = {
19
+ /**
20
+ * When true, Clips AI tools (default title/description, regenerate title/
21
+ * description/chapters, workflows) must watch the full recording — not just
22
+ * the transcript — before writing results. Off by default for cost/latency.
23
+ * Sending the full video requires a Gemini model.
24
+ */
25
+ includeFullVideoInAi?: boolean;
26
+ };
27
+
28
+ export type ClipsUserPrefs = ClipsAiPrefs & {
29
+ defaultPlaybackSpeed?: string;
30
+ emailNotifications?: boolean;
31
+ displayName?: string;
32
+ transcriptCleanupEnabled?: boolean;
33
+ };
34
+
35
+ export function isIncludeFullVideoInAiEnabled(
36
+ prefs: ClipsAiPrefs | Record<string, unknown> | null | undefined,
37
+ ): boolean {
38
+ return prefs?.includeFullVideoInAi === true;
39
+ }
40
+
41
+ /**
42
+ * Extra agent instructions when the user wants AI tools to watch the clip,
43
+ * not just read the audio transcript. Full-video understanding requires Gemini.
44
+ */
45
+ export function buildFullVideoAiInstructions(recordingId: string): string {
46
+ return (
47
+ `The user enabled "Include full video" for Clips AI. Do NOT rely on the ` +
48
+ `transcript alone — audio is often incomplete for accurate titles, ` +
49
+ `descriptions, chapters, and workflow docs. ` +
50
+ `IMPORTANT: sending / understanding the full recording only works with ` +
51
+ `Gemini (Builder Gemini or a Google Gemini key). Use a Gemini model for ` +
52
+ `this turn — Claude and OpenAI cannot ingest the full video file. ` +
53
+ `Prefer attaching or uploading the recording video itself to Gemini when ` +
54
+ `available. Otherwise use \`get-recording-player-data --recordingId=${recordingId}\` ` +
55
+ `(preferred in-app) or \`create-recording-agent-link --recordingId=${recordingId}\` ` +
56
+ `and fetch that context URL, then sample the timeline via recommendedFrames / ` +
57
+ `the frame API. Combine on-screen UI text, product names, and visual context ` +
58
+ `with any transcript. If the transcript is thin or missing, lean harder on ` +
59
+ `the video.`
60
+ );
61
+ }
62
+
63
+ export function withFullVideoAiInstructions(
64
+ message: string,
65
+ recordingId: string,
66
+ includeFullVideo: boolean,
67
+ ): string {
68
+ if (!includeFullVideo) return message;
69
+ return `${message} ${buildFullVideoAiInstructions(recordingId)}`;
70
+ }
71
+
72
+ /** Model selection for sendToAgentChat when Include full video is enabled. */
73
+ export function fullVideoAiModelSelection(): {
74
+ engine: string;
75
+ model: string;
76
+ } {
77
+ return {
78
+ engine: CLIPS_FULL_VIDEO_AI_ENGINE,
79
+ model: CLIPS_FULL_VIDEO_AI_MODEL,
80
+ };
81
+ }
@@ -121,6 +121,7 @@ cd templates/content && pnpm action <name> [args]
121
121
  | `set-content-database-source-write-mode` | `--databaseId <id>` or `--documentId <id> --liveWritesEnabled true\|false [--allowedWriteModes <json>]` | Enable/disable per-source Builder live writes; enabling is allowed only for `agent-native-blog-article-test` with explicit modes |
122
122
  | `stage-builder-revision` | `--databaseId <id>` or `--documentId <id>` | Stage pending local Builder CMS changes as a local-only save-revision record; never calls Builder |
123
123
  | `review-content-database-source-change-set` | `--databaseId <id>` or `--documentId <id> --changeSetId <id> --decision approve\|reject [--note]` | Approve or reject a local source change-set review record without provider writes |
124
+ | `stage-builder-source-bulk-update` | `--databaseId <id>` or `--documentId <id> --itemIds <json-array>` or `--documentIds <json-array> --field <json> [--sourceId <id>] [--dryRun true\|false]` | Preview or stage one bounded field update across selected source-backed Builder rows; apply still goes through Builder review/execution |
124
125
  | `prepare-builder-source-execution` | `--databaseId <id>` or `--documentId <id> --changeSetId <id> [--pushModeConfirmation autosave\|draft\|publish]` | Prepare a dry-run Builder execution gate for approved field/body changes with request semantics/idempotency key; never calls Builder |
125
126
  | `validate-builder-source-execution` | `--databaseId <id>` or `--documentId <id> --changeSetId <id> [--idempotencyKey <key>]` | Validate/replay a prepared Builder execution gate locally as a dry run; never calls Builder |
126
127
  | `execute-builder-source-execution` | `--databaseId <id>` or `--documentId <id> --changeSetId <id> [--idempotencyKey <key>] [--pushModeConfirmation autosave\|draft\|publish]` | Execute a guarded live Builder write only when approved, validated, enabled, idempotent, and targeting `agent-native-blog-article-test` |