@agent-native/core 0.169.0 → 0.170.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 (496) hide show
  1. package/agent-native.eject.json +4 -1
  2. package/bin/agent-native.js +23 -5
  3. package/corpus/README.md +1 -1
  4. package/corpus/templates/analytics/.agents/skills/secrets/SKILL.md +11 -4
  5. package/corpus/templates/analytics/actions/bigquery-table-info.ts +3 -2
  6. package/corpus/templates/analytics/actions/query-agent-native-analytics.ts +1 -1
  7. package/corpus/templates/analytics/actions/search-bigquery-schema.ts +130 -4
  8. package/corpus/templates/analytics/agent-native.config.ts +1 -0
  9. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +36 -23
  10. package/corpus/templates/analytics/app/i18n-data.ts +28 -0
  11. package/corpus/templates/analytics/app/lib/dashboard-visibility.ts +41 -0
  12. package/corpus/templates/analytics/app/pages/Agents.tsx +25 -1
  13. package/corpus/templates/analytics/app/pages/DashboardOverview.tsx +34 -9
  14. package/corpus/templates/analytics/app/routes/$.tsx +10 -0
  15. package/corpus/templates/analytics/app/routes.ts +3 -1
  16. package/corpus/templates/analytics/changelog/2026-08-21-dashboard-filters-keep-other-users-dashboards-out-of-mine.md +6 -0
  17. package/corpus/templates/analytics/changelog/2026-08-22-fixed-the-agent-native-templates-first-party-dashboard-panel.md +6 -0
  18. package/corpus/templates/analytics/changelog/2026-08-22-org-admin-panels-no-longer-report-a-database-error-as-a-per.md +9 -0
  19. package/corpus/templates/analytics/changelog/2026-08-22-slack-requests-report-invalid-inputs.md +6 -0
  20. package/corpus/templates/analytics/changelog/2026-08-22-unknown-analytics-links-now-return-a-real-not-found-status.md +6 -0
  21. package/corpus/templates/analytics/docs/schemas/first-party-analytics.md +2 -1
  22. package/corpus/templates/analytics/docs/schemas/tracked-events.md +2 -0
  23. package/corpus/templates/analytics/scripts/emit-netlify-dashboard-report-cron.ts +29 -7
  24. package/corpus/templates/analytics/server/handlers/slack.ts +59 -5
  25. package/corpus/templates/analytics/server/lib/db-admin-connections.ts +11 -6
  26. package/corpus/templates/analytics/server/lib/first-party-analytics.ts +132 -2
  27. package/corpus/templates/analytics/server/lib/real-data-actions.ts +14 -3
  28. package/corpus/templates/analytics/server/lib/slack.ts +4 -2
  29. package/corpus/templates/analytics/server/plugins/agent-chat.ts +33 -3
  30. package/corpus/templates/analytics/vitest.config.ts +6 -1
  31. package/corpus/templates/assets/.agents/skills/secrets/SKILL.md +11 -4
  32. package/corpus/templates/assets/app/routes/library.tsx +5 -3
  33. package/corpus/templates/assets/app/routes.ts +3 -1
  34. package/corpus/templates/assets/changelog/2026-08-22-library-page-loads-again-instead-of-crashing-on-open.md +6 -0
  35. package/corpus/templates/assets/vitest.config.ts +6 -1
  36. package/corpus/templates/brain/.agents/skills/secrets/SKILL.md +11 -4
  37. package/corpus/templates/brain/app/components/ui/pointer-lock.ts +1 -0
  38. package/corpus/templates/brain/app/routes/sources.tsx +8 -3
  39. package/corpus/templates/brain/changelog/2026-08-22-tuning-or-adding-a-source-no-longer-leaves-the-sources-page.md +6 -0
  40. package/corpus/templates/brain/server/lib/brain.ts +27 -2
  41. package/corpus/templates/brain/vitest.config.ts +1 -0
  42. package/corpus/templates/calendar/.agents/skills/secrets/SKILL.md +11 -4
  43. package/corpus/templates/calendar/actions/list-booking-links.ts +83 -4
  44. package/corpus/templates/calendar/actions/update-booking-link.ts +4 -3
  45. package/corpus/templates/calendar/app/components/calendar/EventDetailPanel.tsx +11 -3
  46. package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +1 -1
  47. package/corpus/templates/calendar/app/components/calendar/GoogleConnectBanner.tsx +103 -44
  48. package/corpus/templates/calendar/app/components/calendar/OutOfOfficeEvent.tsx +28 -24
  49. package/corpus/templates/calendar/app/components/layout/AppLayout.tsx +9 -2
  50. package/corpus/templates/calendar/app/components/layout/Sidebar.tsx +47 -58
  51. package/corpus/templates/calendar/app/hooks/use-booking-links.ts +5 -1
  52. package/corpus/templates/calendar/app/lib/event-layout.ts +52 -26
  53. package/corpus/templates/calendar/app/pages/BookingLinksPage.tsx +126 -98
  54. package/corpus/templates/calendar/app/pages/CalendarView.tsx +1 -0
  55. package/corpus/templates/calendar/app/pages/Settings.tsx +88 -81
  56. package/corpus/templates/calendar/app/routes.ts +3 -1
  57. package/corpus/templates/calendar/changelog/2026-08-22-booking-link-visibility-respects-shared-access.md +6 -0
  58. package/corpus/templates/calendar/changelog/2026-08-22-clicking-an-out-of-office-event-s-marker-no-longer-creates-a.md +6 -0
  59. package/corpus/templates/calendar/changelog/2026-08-22-event-detail-views-now-show-your-calendar-timezone.md +6 -0
  60. package/corpus/templates/calendar/changelog/2026-08-22-google-calendar-can-now-connect-with-the-shared-google-oauth.md +6 -0
  61. package/corpus/templates/calendar/changelog/2026-08-22-overlapping-events-now-show-each-others-boundaries.md +6 -0
  62. package/corpus/templates/calendar/changelog/2026-08-22-public-booking-pages-now-offer-time-slots-from-the-host-s-we.md +6 -0
  63. package/corpus/templates/calendar/server/handlers/bookings.ts +5 -7
  64. package/corpus/templates/calendar/server/handlers/google-auth.ts +10 -2
  65. package/corpus/templates/calendar/server/lib/google-calendar.ts +139 -14
  66. package/corpus/templates/calendar/server/onboarding.ts +85 -0
  67. package/corpus/templates/calendar/server/plugins/agent-chat.ts +2 -0
  68. package/corpus/templates/calendar/shared/api.ts +9 -1
  69. package/corpus/templates/chat/.agents/skills/secrets/SKILL.md +11 -4
  70. package/corpus/templates/chat/app/routes.ts +3 -1
  71. package/corpus/templates/chat/vitest.config.ts +8 -0
  72. package/corpus/templates/clips/.agents/skills/secrets/SKILL.md +11 -4
  73. package/corpus/templates/clips/actions/get-meeting.ts +9 -4
  74. package/corpus/templates/clips/actions/list-recordings.ts +16 -3
  75. package/corpus/templates/clips/actions/search-meetings.ts +1 -0
  76. package/corpus/templates/clips/actions/stop-meeting-recording.ts +87 -6
  77. package/corpus/templates/clips/actions/view-screen.ts +8 -1
  78. package/corpus/templates/clips/app/components/library/search-bar.tsx +1 -1
  79. package/corpus/templates/clips/app/components/meetings/meeting-history-row.tsx +32 -1
  80. package/corpus/templates/clips/app/components/player/delete-recording-menu.tsx +1 -1
  81. package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +29 -1
  82. package/corpus/templates/clips/app/i18n/en-US.ts +5 -0
  83. package/corpus/templates/clips/app/routes/_app.meetings.$meetingId.tsx +33 -4
  84. package/corpus/templates/clips/app/routes/_app.meetings._index.tsx +1 -0
  85. package/corpus/templates/clips/app/routes/access-request.approve.tsx +16 -9
  86. package/corpus/templates/clips/app/routes/embed.$shareId.tsx +17 -7
  87. package/corpus/templates/clips/app/routes/share.$shareId.tsx +17 -7
  88. package/corpus/templates/clips/changelog/2026-08-21-camera-bubble-black-circle.md +6 -0
  89. package/corpus/templates/clips/changelog/2026-08-22-screen-recordings-respect-the-microphone-toggle.md +6 -0
  90. package/corpus/templates/clips/changelog/2026-08-22-the-recordings-library-no-longer-fails-right-after-server-start.md +6 -0
  91. package/corpus/templates/clips/changelog/2026-08-23-the-agent-can-see-your-library-right-after-server-start.md +6 -0
  92. package/corpus/templates/clips/chrome-extension/src/background.ts +1 -2
  93. package/corpus/templates/clips/chrome-extension/src/native-transcription.ts +39 -7
  94. package/corpus/templates/clips/chrome-extension/src/offscreen.ts +21 -25
  95. package/corpus/templates/clips/desktop/src/app.tsx +53 -24
  96. package/corpus/templates/clips/desktop/src/lib/bubble-playback.ts +68 -0
  97. package/corpus/templates/clips/desktop/src/lib/recorder.ts +3 -0
  98. package/corpus/templates/clips/desktop/src/lib/transcription-capture.ts +72 -14
  99. package/corpus/templates/clips/desktop/src/lib/updater.ts +8 -3
  100. package/corpus/templates/clips/desktop/src/overlays/bubble.tsx +162 -20
  101. package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +43 -6
  102. package/corpus/templates/clips/desktop/src-tauri/src/native_screen/custom_capture.rs +150 -13
  103. package/corpus/templates/clips/desktop/src-tauri/src/tray.rs +12 -0
  104. package/corpus/templates/clips/server/lib/calendar-event-meetings.ts +28 -3
  105. package/corpus/templates/clips/server/lib/emails.ts +17 -33
  106. package/corpus/templates/content/.agents/skills/secrets/SKILL.md +11 -4
  107. package/corpus/templates/content/app/components/editor/VisualEditor.tsx +140 -15
  108. package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +12 -14
  109. package/corpus/templates/content/app/components/ui/pointer-lock.ts +1 -0
  110. package/corpus/templates/content/app/hooks/use-content-database.ts +18 -2
  111. package/corpus/templates/content/app/hooks/use-local-storage.ts +38 -22
  112. package/corpus/templates/content/app/i18n-data.ts +25 -0
  113. package/corpus/templates/content/app/routes.ts +3 -1
  114. package/corpus/templates/content/changelog/2026-08-22-code-blocks-inserted-from-the-slash-menu-no-longer-get-stuck.md +6 -0
  115. package/corpus/templates/content/changelog/2026-08-22-the-sidebar-page-tree-no-longer-flashes-empty-after-creating-a-page.md +6 -0
  116. package/corpus/templates/content/docs/solutions/2026-08-21-content-mermaid-rendering-shape.md +233 -0
  117. package/corpus/templates/content/server/plugins/agent-chat.ts +9 -2
  118. package/corpus/templates/content/vitest.config.ts +7 -1
  119. package/corpus/templates/crm/.agents/skills/secrets/SKILL.md +11 -4
  120. package/corpus/templates/design/.agents/skills/secrets/SKILL.md +11 -4
  121. package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +1386 -92
  122. package/corpus/templates/design/actions/generate-design.ts +98 -47
  123. package/corpus/templates/design/actions/get-design-system.ts +6 -1
  124. package/corpus/templates/design/actions/refresh-design-system-with-builder.ts +146 -0
  125. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +70 -26
  126. package/corpus/templates/design/app/components/design/EditPanel.tsx +15 -47
  127. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +284 -136
  128. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +2060 -144
  129. package/corpus/templates/design/app/components/design/editor/DesignBottomToolbar.tsx +6 -2
  130. package/corpus/templates/design/app/components/design/editor/DesignWorkspaceRail.tsx +4 -4
  131. package/corpus/templates/design/app/components/design/keyboard-shortcuts.ts +10 -8
  132. package/corpus/templates/design/app/components/design/multi-screen/alt-hover-measurement.ts +19 -2
  133. package/corpus/templates/design/app/components/design/multi-screen/types.ts +9 -2
  134. package/corpus/templates/design/app/components/layout/Header.tsx +15 -0
  135. package/corpus/templates/design/app/components/layout/Layout.tsx +7 -2
  136. package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +13 -8
  137. package/corpus/templates/design/app/i18n-data.ts +11 -10
  138. package/corpus/templates/design/app/i18n-keyboard-shortcuts.ts +12 -0
  139. package/corpus/templates/design/app/lib/design-system-data.ts +68 -0
  140. package/corpus/templates/design/app/pages/DesignEditor.tsx +99 -57
  141. package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +39 -8
  142. package/corpus/templates/design/app/pages/DesignSystems.tsx +137 -43
  143. package/corpus/templates/design/app/pages/Index.tsx +3 -1
  144. package/corpus/templates/design/app/pages/Templates.tsx +0 -10
  145. package/corpus/templates/design/app/pages/design-editor/code-layer-state.ts +10 -0
  146. package/corpus/templates/design/app/pages/design-editor/collab-sync.ts +50 -0
  147. package/corpus/templates/design/app/pages/design-editor/commands/apply-design-editor-command.ts +1 -1
  148. package/corpus/templates/design/app/pages/design-editor/commands/apply-file-content-update.ts +10 -8
  149. package/corpus/templates/design/app/pages/design-editor/commands/apply-local-content-update.ts +6 -6
  150. package/corpus/templates/design/app/pages/design-editor/commands/apply-pending-visual-styles-with-agent.ts +1 -1
  151. package/corpus/templates/design/app/pages/design-editor/commands/commit-visual-styles.ts +18 -7
  152. package/corpus/templates/design/app/pages/design-editor/commands/escape-hotkey.ts +4 -90
  153. package/corpus/templates/design/app/pages/design-editor/commands/layer-marquee-selection-change.ts +2 -1
  154. package/corpus/templates/design/app/pages/design-editor/commands/layer-selection-change.ts +4 -3
  155. package/corpus/templates/design/app/pages/design-editor/commands/screen-element-select.ts +12 -2
  156. package/corpus/templates/design/app/pages/design-editor/commands/screen-visual-style-change.ts +9 -2
  157. package/corpus/templates/design/app/pages/design-editor/commands/send-runtime-layer-move-semantic-handoff.ts +1 -1
  158. package/corpus/templates/design/app/pages/design-editor/commands/send-runtime-layer-semantic-handoff.ts +1 -1
  159. package/corpus/templates/design/app/pages/design-editor/commands/send-runtime-layer-state-semantic-handoff.ts +1 -1
  160. package/corpus/templates/design/app/pages/design-editor/commands/start-sidebar-resize.ts +1 -1
  161. package/corpus/templates/design/app/pages/design-editor/editor-state.ts +11 -1
  162. package/corpus/templates/design/app/pages/design-editor/effects/adopt-db-file-content.ts +11 -19
  163. package/corpus/templates/design/app/pages/design-editor/effects/observe-collab-text.ts +6 -6
  164. package/corpus/templates/design/app/pages/design-editor/effects/publish-agent-selection-context.ts +1 -1
  165. package/corpus/templates/design/app/pages/design-editor/effects/seed-collab-content.ts +12 -15
  166. package/corpus/templates/design/app/pages/design-editor/preview-navigation.ts +25 -0
  167. package/corpus/templates/design/app/pages/design-editor/selection-state.ts +5 -59
  168. package/corpus/templates/design/app/pages/design-editor/tool-state.ts +8 -0
  169. package/corpus/templates/design/app/pages/design-editor/types.ts +3 -3
  170. package/corpus/templates/design/app/routes.ts +3 -1
  171. package/corpus/templates/design/changelog/2026-08-20-canvas-edits-patch-the-frame-instead-of-rebuilding-it.md +6 -0
  172. package/corpus/templates/design/changelog/2026-08-21-builder-design-system-imports-reconcile-after-indexing.md +6 -0
  173. package/corpus/templates/design/changelog/2026-08-21-draw-mode-needs-shift-y.md +6 -0
  174. package/corpus/templates/design/changelog/2026-08-21-eyedropper-shortcut.md +6 -0
  175. package/corpus/templates/design/changelog/2026-08-21-figma-style-alignment-guides-and-snapping.md +6 -0
  176. package/corpus/templates/design/changelog/2026-08-21-group-transforms-keep-the-selection.md +6 -0
  177. package/corpus/templates/design/changelog/2026-08-21-inspector-fields-keep-focus-on-enter.md +6 -0
  178. package/corpus/templates/design/changelog/2026-08-21-pen-alt-keeps-the-incoming-handle.md +6 -0
  179. package/corpus/templates/design/changelog/2026-08-21-pen-on-the-board-creates-one-vector.md +6 -0
  180. package/corpus/templates/design/changelog/2026-08-21-scale-a-multi-selection-from-the-group-handles.md +6 -0
  181. package/corpus/templates/design/changelog/2026-08-21-scale-resizes-text-with-the-object.md +6 -0
  182. package/corpus/templates/design/changelog/2026-08-21-selecting-another-object-keeps-the-scale-tool-armed.md +6 -0
  183. package/corpus/templates/design/changelog/2026-08-21-the-shape-tool-remembers-the-shape-you-picked.md +6 -0
  184. package/corpus/templates/design/changelog/2026-08-21-vectors-drawn-in-a-frame-land-where-you-drew-them.md +6 -0
  185. package/corpus/templates/design/changelog/2026-08-22-multi-screen-generation-no-longer-loses-a-saved-screens-layo.md +6 -0
  186. package/corpus/templates/design/changelog/2026-08-22-rename-and-search-fields-are-labeled-for-screen-readers.md +6 -0
  187. package/corpus/templates/design/package.json +2 -0
  188. package/corpus/templates/design/server/lib/builder-design-system-proxy.ts +311 -0
  189. package/corpus/templates/design/shared/canvas-math.ts +612 -206
  190. package/corpus/templates/design/shared/pen-path.ts +37 -0
  191. package/corpus/templates/design/vitest.config.ts +7 -1
  192. package/corpus/templates/dispatch/.agents/skills/secrets/SKILL.md +11 -4
  193. package/corpus/templates/dispatch/actions/list-workspace-connections.ts +34 -3
  194. package/corpus/templates/dispatch/app/routes/integrations.tsx +55 -21
  195. package/corpus/templates/dispatch/app/routes.ts +3 -1
  196. package/corpus/templates/factory/.agents/skills/secrets/SKILL.md +11 -4
  197. package/corpus/templates/factory/.env.example +2 -7
  198. package/corpus/templates/factory/AGENTS.md +3 -0
  199. package/corpus/templates/factory/README.md +5 -3
  200. package/corpus/templates/factory/actions/create-factory.ts +16 -3
  201. package/corpus/templates/factory/actions/get-slack-feedback-context.ts +2 -0
  202. package/corpus/templates/factory/actions/get-triage-item.ts +2 -0
  203. package/corpus/templates/factory/actions/list-factory-audit.ts +116 -36
  204. package/corpus/templates/factory/actions/list-factory-automations.ts +2 -1
  205. package/corpus/templates/factory/actions/list-triage-items.ts +22 -19
  206. package/corpus/templates/factory/actions/run-factory-automation.ts +7 -3
  207. package/corpus/templates/factory/actions/save-factory-automation.ts +5 -2
  208. package/corpus/templates/factory/actions/save-triage-config.ts +2 -8
  209. package/corpus/templates/factory/actions/start-builder-for-item.ts +21 -0
  210. package/corpus/templates/factory/app/components/factory/FactoryAuditView.tsx +229 -297
  211. package/corpus/templates/factory/app/components/factory/FactorySettingsView.tsx +397 -247
  212. package/corpus/templates/factory/app/components/factory/FactorySourceSettingsGroup.tsx +33 -0
  213. package/corpus/templates/factory/app/components/factory/NewFactoryForm.tsx +202 -162
  214. package/corpus/templates/factory/app/i18n/en-US.ts +32 -4
  215. package/corpus/templates/factory/app/routes/factory.tsx +84 -11
  216. package/corpus/templates/factory/app/routes.ts +3 -1
  217. package/corpus/templates/factory/changelog/2026-08-21-activity-runs-use-automation-display-names.md +6 -0
  218. package/corpus/templates/factory/changelog/2026-08-21-create-and-settings-source-cards.md +6 -0
  219. package/corpus/templates/factory/changelog/2026-08-21-keep-automations-visible-while-running.md +6 -0
  220. package/corpus/templates/factory/changelog/2026-08-21-nested-factory-automations-can-poll.md +6 -0
  221. package/corpus/templates/factory/changelog/2026-08-21-settings-sticky-unsaved-bar.md +6 -0
  222. package/corpus/templates/factory/server/connectors/credentials.ts +10 -5
  223. package/corpus/templates/factory/server/lib/factory-audit-report.ts +412 -0
  224. package/corpus/templates/factory/server/lib/factory-config-reconcile.ts +321 -0
  225. package/corpus/templates/factory/server/lib/factory-scope.ts +25 -2
  226. package/corpus/templates/factory/server/lib/require-factory-automation.ts +13 -4
  227. package/corpus/templates/factory/server/plugins/factory-migrations.ts +148 -4
  228. package/corpus/templates/factory/server/plugins/factory-scheduler-job.ts +4 -6
  229. package/corpus/templates/factory/vitest.config.ts +17 -2
  230. package/corpus/templates/forms/.agents/skills/secrets/SKILL.md +11 -4
  231. package/corpus/templates/forms/app/routes/_app.settings.tsx +4 -1
  232. package/corpus/templates/forms/changelog/2026-08-22-the-extensions-link-now-opens-the-extensions-tab-instead-o.md +6 -0
  233. package/corpus/templates/forms/server/lib/public-form-ssr.ts +11 -1
  234. package/corpus/templates/forms/vitest.config.ts +1 -0
  235. package/corpus/templates/macros/.agents/skills/secrets/SKILL.md +11 -4
  236. package/corpus/templates/macros/app/routes.ts +3 -1
  237. package/corpus/templates/macros/vitest.config.ts +6 -1
  238. package/corpus/templates/mail/.agents/skills/secrets/SKILL.md +11 -4
  239. package/corpus/templates/mail/actions/manage-gmail-filters.ts +10 -2
  240. package/corpus/templates/mail/app/components/GoogleConnectBanner.tsx +100 -37
  241. package/corpus/templates/mail/app/components/email/IntegrationsSidebar.tsx +14 -2
  242. package/corpus/templates/mail/app/components/layout/AppLayout.tsx +66 -36
  243. package/corpus/templates/mail/app/hooks/use-emails.ts +13 -3
  244. package/corpus/templates/mail/app/hooks/use-google-auth.ts +2 -0
  245. package/corpus/templates/mail/app/lib/google-oauth-setup.ts +17 -0
  246. package/corpus/templates/mail/app/pages/InboxPage.tsx +69 -9
  247. package/corpus/templates/mail/app/routes/$.tsx +9 -0
  248. package/corpus/templates/mail/app/routes/$view.$threadId.tsx +23 -1
  249. package/corpus/templates/mail/app/routes/$view.tsx +47 -1
  250. package/corpus/templates/mail/app/routes.ts +3 -1
  251. package/corpus/templates/mail/changelog/2026-08-22-gmail-can-now-connect-with-the-shared-google-oauth.md +6 -0
  252. package/corpus/templates/mail/changelog/2026-08-22-gmail-filters-now-shows-the-real-error.md +6 -0
  253. package/corpus/templates/mail/changelog/2026-08-22-unknown-mail-urls-now-show-the-404-page.md +6 -0
  254. package/corpus/templates/mail/server/handlers/google-auth.ts +11 -2
  255. package/corpus/templates/mail/server/lib/google-auth.ts +92 -12
  256. package/corpus/templates/mail/server/onboarding.ts +66 -18
  257. package/corpus/templates/mail/vitest.config.ts +6 -1
  258. package/corpus/templates/plan/.agents/skills/secrets/SKILL.md +11 -4
  259. package/corpus/templates/plan/app/routes/settings.tsx +4 -1
  260. package/corpus/templates/plan/app/routes.ts +3 -1
  261. package/corpus/templates/plan/changelog/2026-08-22-the-extensions-link-now-opens-the-extensions-tab-instead-o.md +6 -0
  262. package/corpus/templates/plan/netlify.toml +1 -1
  263. package/corpus/templates/slides/.agents/skills/secrets/SKILL.md +11 -4
  264. package/corpus/templates/slides/actions/patch-deck.ts +79 -10
  265. package/corpus/templates/slides/actions/update-slide.ts +11 -1
  266. package/corpus/templates/slides/app/components/deck/SlideRenderer.tsx +14 -4
  267. package/corpus/templates/slides/app/components/editor/ExportMenu.tsx +10 -52
  268. package/corpus/templates/slides/app/components/editor/GoogleDocImportHint.tsx +8 -53
  269. package/corpus/templates/slides/app/components/editor/GoogleDriveConnectionCta.tsx +9 -75
  270. package/corpus/templates/slides/app/components/editor/GoogleSlidesReferenceImport.tsx +8 -60
  271. package/corpus/templates/slides/app/components/editor/SlideContextToolbar.tsx +10 -2
  272. package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +19 -7
  273. package/corpus/templates/slides/app/lib/sanitize-slide-html.ts +49 -36
  274. package/corpus/templates/slides/app/routes/access-request.approve.tsx +16 -9
  275. package/corpus/templates/slides/app/routes.ts +3 -1
  276. package/corpus/templates/slides/changelog/2026-08-22-already-uploaded-chat-images-are-no-longer-dropped.md +6 -0
  277. package/corpus/templates/slides/changelog/2026-08-22-google-drive-and-slides-can-now-connect-with-the-shared-google-oauth.md +6 -0
  278. package/corpus/templates/slides/changelog/2026-08-22-multi-slide-repairs-now-report-slides-that-still-overflow.md +6 -0
  279. package/corpus/templates/slides/changelog/2026-08-22-update-slide-now-reports-which-batched-edit-was-skipped.md +6 -0
  280. package/corpus/templates/slides/server/handlers/google-docs-auth.ts +52 -1
  281. package/corpus/templates/slides/server/lib/chat-attachments.ts +29 -3
  282. package/corpus/templates/slides/server/lib/google-docs-oauth.ts +66 -16
  283. package/corpus/templates/slides/server/lib/slide-content-patch.ts +13 -2
  284. package/corpus/templates/slides/server/plugins/agent-chat.ts +5 -2
  285. package/corpus/templates/slides/tsconfig.json +5 -0
  286. package/corpus/templates/slides/vitest.config.ts +6 -1
  287. package/corpus/templates/tasks/.agents/skills/secrets/SKILL.md +11 -4
  288. package/corpus/templates/tasks/app/routes/settings.tsx +4 -1
  289. package/corpus/templates/tasks/app/routes.ts +3 -1
  290. package/corpus/templates/tasks/changelog/2026-08-22-the-extensions-link-now-opens-the-extensions-tab-instead-o.md +6 -0
  291. package/corpus/templates/tasks/vitest.config.ts +7 -1
  292. package/dist/action.d.ts +91 -0
  293. package/dist/action.js +50 -0
  294. package/dist/agent/abort-reasons.d.ts +38 -0
  295. package/dist/agent/abort-reasons.js +49 -0
  296. package/dist/agent/engine/ai-sdk-engine.js +8 -5
  297. package/dist/agent/engine/anthropic-engine.js +7 -5
  298. package/dist/agent/engine/credential-errors.d.ts +1 -1
  299. package/dist/agent/engine/credential-errors.js +2 -2
  300. package/dist/agent/engine/first-event-timeout.d.ts +42 -17
  301. package/dist/agent/engine/first-event-timeout.js +67 -25
  302. package/dist/agent/production-agent.d.ts +23 -0
  303. package/dist/agent/production-agent.js +107 -102
  304. package/dist/agent/run-loop-with-resume.d.ts +2 -28
  305. package/dist/agent/run-loop-with-resume.js +25 -40
  306. package/dist/agent/run-manager.js +5 -4
  307. package/dist/agent/run-store.d.ts +55 -0
  308. package/dist/agent/run-store.js +124 -1
  309. package/dist/agent/tool-search.js +2 -1
  310. package/dist/agent/types.d.ts +9 -4
  311. package/dist/app-config/configuration-error.d.ts +11 -0
  312. package/dist/app-config/configuration-error.js +14 -0
  313. package/dist/app-config/index.d.ts +1 -0
  314. package/dist/app-config/index.js +1 -0
  315. package/dist/app-config/integrations.d.ts +1 -0
  316. package/dist/app-config/integrations.js +10 -0
  317. package/dist/app-config/plugins.d.ts +34 -0
  318. package/dist/app-config/plugins.js +39 -0
  319. package/dist/app-config/run-lifecycle-invariants.d.ts +18 -9
  320. package/dist/app-config/run-lifecycle-invariants.js +18 -25
  321. package/dist/app-config/schema.d.ts +16 -0
  322. package/dist/app-config/schema.js +2 -0
  323. package/dist/chat-threads/schema-migrations.js +20 -0
  324. package/dist/chat-threads/store.js +21 -4
  325. package/dist/cli/native-dependencies.d.ts +4 -0
  326. package/dist/cli/native-dependencies.js +15 -0
  327. package/dist/client/AssistantChat.js +47 -15
  328. package/dist/client/ConnectBuilderCard.d.ts +3 -1
  329. package/dist/client/ConnectBuilderCard.js +6 -3
  330. package/dist/client/EnvironmentBadge.d.ts +1 -1
  331. package/dist/client/EnvironmentBadge.js +40 -5
  332. package/dist/client/MultiTabAssistantChat.d.ts +1 -1
  333. package/dist/client/MultiTabAssistantChat.js +35 -14
  334. package/dist/client/agent-chat/index.d.ts +1 -1
  335. package/dist/client/agent-chat/index.js +1 -1
  336. package/dist/client/agent-page/use-jobs.d.ts +2 -1
  337. package/dist/client/agent-page/use-jobs.js +3 -5
  338. package/dist/client/agent-sidebar-state.js +3 -1
  339. package/dist/client/blocks/library/code-tabs.js +5 -0
  340. package/dist/client/blocks/library/code.js +1 -0
  341. package/dist/client/chat/message-components.d.ts +7 -0
  342. package/dist/client/chat/message-components.js +1 -1
  343. package/dist/client/chat/run-recovery.js +5 -2
  344. package/dist/client/chat/tool-call-display.js +9 -3
  345. package/dist/client/chat-first/apps-rail.js +2 -2
  346. package/dist/client/chat-model-groups.d.ts +12 -0
  347. package/dist/client/chat-model-groups.js +3 -2
  348. package/dist/client/error-format.js +12 -0
  349. package/dist/client/integrations/GoogleProductLogo.d.ts +5 -0
  350. package/dist/client/integrations/GoogleProductLogo.js +45 -0
  351. package/dist/client/integrations/index.d.ts +2 -0
  352. package/dist/client/integrations/index.js +2 -0
  353. package/dist/client/integrations/workspace-provider-oauth.d.ts +8 -0
  354. package/dist/client/integrations/workspace-provider-oauth.js +13 -0
  355. package/dist/client/navigation/index.d.ts +1 -0
  356. package/dist/client/navigation/index.js +1 -0
  357. package/dist/client/resources/mcp-integration-catalog.js +0 -29
  358. package/dist/client/route-state.js +6 -0
  359. package/dist/client/settings/SettingsTabsPage.js +10 -6
  360. package/dist/client/sharing/ShareButton.js +11 -2
  361. package/dist/client/use-chat-models.d.ts +6 -0
  362. package/dist/client/use-chat-models.js +10 -0
  363. package/dist/client/use-db-sync.js +27 -9
  364. package/dist/client/workspace-app-navigation.d.ts +10 -0
  365. package/dist/client/workspace-app-navigation.js +38 -0
  366. package/dist/collab/struct-routes.d.ts +1 -1
  367. package/dist/connections/catalog.d.ts +111 -9
  368. package/dist/connections/catalog.js +86 -30
  369. package/dist/db/create-get-db.js +18 -0
  370. package/dist/db/ddl-guard.d.ts +0 -4
  371. package/dist/db/ddl-guard.js +46 -0
  372. package/dist/deploy/build.d.ts +32 -0
  373. package/dist/deploy/build.js +241 -16
  374. package/dist/deploy/function-bundle.d.ts +45 -0
  375. package/dist/deploy/function-bundle.js +337 -0
  376. package/dist/deploy/workspace-deploy.js +19 -1
  377. package/dist/eject/workspace-connections.js +4 -0
  378. package/dist/email-catalog/registry.d.ts +4 -0
  379. package/dist/email-catalog/registry.js +79 -14
  380. package/dist/extensions/content-patch.js +14 -2
  381. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  382. package/dist/integrations/plugin.d.ts +10 -0
  383. package/dist/integrations/plugin.js +40 -6
  384. package/dist/jobs/actions/run-automation-now.d.ts +2 -1
  385. package/dist/jobs/actions/run-automation-now.js +11 -5
  386. package/dist/jobs/frontmatter.js +53 -2
  387. package/dist/jobs/run-now.d.ts +11 -1
  388. package/dist/jobs/run-now.js +42 -7
  389. package/dist/jobs/scheduler.d.ts +1 -0
  390. package/dist/jobs/scheduler.js +2 -1
  391. package/dist/localization/core-messages/ar-SA.js +4 -1
  392. package/dist/localization/core-messages/de-DE.js +4 -1
  393. package/dist/localization/core-messages/en-US.d.ts +4 -1
  394. package/dist/localization/core-messages/en-US.js +4 -1
  395. package/dist/localization/core-messages/es-ES.js +4 -1
  396. package/dist/localization/core-messages/fr-FR.js +4 -1
  397. package/dist/localization/core-messages/hi-IN.js +4 -1
  398. package/dist/localization/core-messages/ja-JP.js +4 -1
  399. package/dist/localization/core-messages/ko-KR.js +4 -1
  400. package/dist/localization/core-messages/pt-BR.js +4 -1
  401. package/dist/localization/core-messages/zh-CN.js +4 -1
  402. package/dist/localization/core-messages/zh-TW.js +4 -1
  403. package/dist/localization/core-messages.d.ts +4 -1
  404. package/dist/localization/default-messages.d.ts +2 -5
  405. package/dist/localization/default-messages.js +2 -5
  406. package/dist/mcp/build-server.d.ts +9 -0
  407. package/dist/mcp/build-server.js +37 -8
  408. package/dist/mcp-client/oauth-client.d.ts +7 -0
  409. package/dist/mcp-client/oauth-client.js +12 -1
  410. package/dist/notifications/routes.d.ts +1 -1
  411. package/dist/oauth-tokens/index.d.ts +1 -1
  412. package/dist/oauth-tokens/index.js +1 -1
  413. package/dist/oauth-tokens/lifecycle.d.ts +11 -0
  414. package/dist/oauth-tokens/lifecycle.js +17 -0
  415. package/dist/oauth-tokens/store.js +7 -0
  416. package/dist/observability/posthog-ai.d.ts +15 -1
  417. package/dist/observability/posthog-ai.js +24 -12
  418. package/dist/observability/traces.js +158 -28
  419. package/dist/onboarding/app-profile.js +2 -2
  420. package/dist/onboarding/plugin.js +10 -2
  421. package/dist/onboarding/types.d.ts +2 -0
  422. package/dist/provider-api/actions/custom-provider-registration.d.ts +12 -12
  423. package/dist/provider-api/actions/provider-api.d.ts +11 -11
  424. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  425. package/dist/provider-api/index.js +2 -0
  426. package/dist/resources/handlers.d.ts +1 -1
  427. package/dist/resources/store.js +13 -4
  428. package/dist/scripts/agent-engines/list-agent-engines.js +29 -3
  429. package/dist/scripts/db/check-scoping.js +9 -6
  430. package/dist/secrets/register-framework-secrets.js +1 -1
  431. package/dist/secrets/routes.d.ts +6 -6
  432. package/dist/server/action-discovery.js +5 -0
  433. package/dist/server/agent-chat/action-filters-a2a.d.ts +27 -3
  434. package/dist/server/agent-chat/action-filters-a2a.js +48 -9
  435. package/dist/server/agent-chat-plugin.js +36 -9
  436. package/dist/server/agent-discovery.js +43 -20
  437. package/dist/server/auth.js +18 -16
  438. package/dist/server/beta-opt-out-html.js +45 -4
  439. package/dist/server/better-auth-instance.d.ts +2 -1
  440. package/dist/server/better-auth-instance.js +23 -7
  441. package/dist/server/builder-browser.d.ts +4 -0
  442. package/dist/server/builder-browser.js +23 -1
  443. package/dist/server/builder-design-systems.d.ts +4 -1
  444. package/dist/server/builder-design-systems.js +87 -6
  445. package/dist/server/builder-oauth.d.ts +1 -7
  446. package/dist/server/builder-oauth.js +80 -163
  447. package/dist/server/core-routes-plugin.d.ts +29 -0
  448. package/dist/server/core-routes-plugin.js +113 -21
  449. package/dist/server/default-plugins.d.ts +18 -0
  450. package/dist/server/default-plugins.js +22 -0
  451. package/dist/server/edge.d.ts +1 -0
  452. package/dist/server/edge.js +1 -0
  453. package/dist/server/framework-request-handler.js +31 -3
  454. package/dist/server/google-oauth.d.ts +3 -0
  455. package/dist/server/google-oauth.js +3 -0
  456. package/dist/server/index.d.ts +4 -4
  457. package/dist/server/index.js +3 -3
  458. package/dist/server/onboarding-html.js +37 -3
  459. package/dist/server/workspace-provider-oauth.d.ts +6 -1
  460. package/dist/server/workspace-provider-oauth.js +72 -20
  461. package/dist/shared/agent-sidebar-url.d.ts +1 -0
  462. package/dist/shared/agent-sidebar-url.js +1 -0
  463. package/dist/shared/environment-lanes.d.ts +2 -0
  464. package/dist/shared/environment-lanes.js +3 -1
  465. package/dist/shared/index.d.ts +3 -1
  466. package/dist/shared/index.js +3 -1
  467. package/dist/shared/surface-visibility.d.ts +31 -0
  468. package/dist/shared/surface-visibility.js +57 -0
  469. package/dist/templates/chat/.agents/skills/secrets/SKILL.md +11 -4
  470. package/dist/templates/chat/app/routes.ts +3 -1
  471. package/dist/templates/chat/vitest.config.ts +8 -0
  472. package/dist/templates/default/.agents/skills/secrets/SKILL.md +11 -4
  473. package/dist/templates/default/app/routes.ts +3 -1
  474. package/dist/templates/headless/.agents/skills/secrets/SKILL.md +11 -4
  475. package/dist/templates/workspace-core/.agents/skills/secrets/SKILL.md +11 -4
  476. package/dist/tracking/registry.d.ts +11 -0
  477. package/dist/tracking/registry.js +5 -2
  478. package/dist/triggers/actions.js +10 -3
  479. package/dist/usage/store.js +23 -3
  480. package/dist/user-profile/actions/get-auth-methods.js +18 -5
  481. package/dist/vite/client.js +24 -5
  482. package/docs/content/actions-access-control.mdx +43 -4
  483. package/docs/content/actions-defining.mdx +16 -0
  484. package/docs/content/template-clips-capture-everywhere.mdx +29 -30
  485. package/docs/content/template-clips-developers.mdx +4 -4
  486. package/docs/content/template-clips.mdx +3 -1
  487. package/package.json +3 -3
  488. package/src/eject/workspace-connections.ts +4 -0
  489. package/src/templates/chat/.agents/skills/secrets/SKILL.md +11 -4
  490. package/src/templates/chat/app/routes.ts +3 -1
  491. package/src/templates/chat/vitest.config.ts +8 -0
  492. package/src/templates/default/.agents/skills/secrets/SKILL.md +11 -4
  493. package/src/templates/default/app/routes.ts +3 -1
  494. package/src/templates/headless/.agents/skills/secrets/SKILL.md +11 -4
  495. package/src/templates/workspace-core/.agents/skills/secrets/SKILL.md +11 -4
  496. /package/corpus/templates/dispatch/app/routes/{apps.$appId.tsx → apps.$appId.$.tsx} +0 -0
@@ -210,7 +210,14 @@ async function fetchLibrary({
210
210
  conditions.push(eq(schema.recordings.organizationId, organizationId));
211
211
  }
212
212
  }
213
- const meetingRecordingIds = db
213
+ // Meeting recordings are transcript-only and live on /meetings, so keep them
214
+ // out of clip library views. `await db` with no chain resolves the lazy proxy
215
+ // from create-get-db.ts to the real instance without issuing a query; the
216
+ // subquery must be built off *that*. Building it off `db` hands an unresolved
217
+ // chain to notInArray(), which reads `.getSQL()` synchronously on a cold-start
218
+ // proxy. The subquery filters NULLs so NOT IN doesn't collapse to empty.
219
+ const resolvedDb = await db;
220
+ const meetingRecordingIds = resolvedDb
214
221
  .select({ id: schema.meetings.recordingId })
215
222
  .from(schema.meetings)
216
223
  .where(isNotNull(schema.meetings.recordingId));
@@ -160,7 +160,7 @@ export function SearchBar({ className, side = "right" }: SearchBarProps) {
160
160
  </button>
161
161
  ) : (
162
162
  <span className="absolute end-1.5 top-1/2 -translate-y-1/2 inline-flex items-center gap-0.5 rounded border border-border bg-muted px-1 py-0.5 text-[10px] font-medium text-muted-foreground">
163
- {shortcutLabel("cmd+k")}
163
+ {shortcutLabel("/")}
164
164
  </span>
165
165
  )}
166
166
  </div>
@@ -26,6 +26,7 @@ export interface MeetingHistoryItem {
26
26
  actualEnd?: string | null;
27
27
  createdAt?: string | null;
28
28
  participants?: AttendeeStackParticipant[];
29
+ ownerEmail?: string | null;
29
30
  }
30
31
 
31
32
  function formatTime(iso?: string | null): string {
@@ -56,6 +57,28 @@ export function formatParticipantNames(
56
57
  return `${names.slice(0, 2).join(", ")} & ${names.length - 2} others`;
57
58
  }
58
59
 
60
+ /**
61
+ * A meeting's owner — who actually recorded it in Clips — isn't necessarily
62
+ * on the attendee list (an ad-hoc note has none at all), and two attendees on
63
+ * the same call can each hold their own copy. Unlike the attendee subtitle,
64
+ * this is shown unconditionally, including the viewer's own meetings, so
65
+ * ownership is never ambiguous once a meeting is shared.
66
+ */
67
+ export function formatOwnerHint(
68
+ ownerEmail: string | null | undefined,
69
+ viewerEmail: string | null | undefined,
70
+ t: ReturnType<typeof useT>,
71
+ ): string {
72
+ const owner = ownerEmail?.trim();
73
+ if (!owner) return "";
74
+ const viewer = viewerEmail?.trim().toLowerCase();
75
+ const name =
76
+ viewer && owner.toLowerCase() === viewer
77
+ ? t("meetingDetail.me")
78
+ : owner.replace(/@.*$/, "");
79
+ return t("meetingDetail.recordedBy", { name });
80
+ }
81
+
59
82
  export function MeetingHistoryRow({
60
83
  meeting,
61
84
  snippet,
@@ -66,11 +89,17 @@ export function MeetingHistoryRow({
66
89
  const t = useT();
67
90
  const { session } = useSession();
68
91
  const participants = meeting.participants ?? [];
69
- const subtitle =
92
+ const ownerHint = formatOwnerHint(meeting.ownerEmail, session?.email, t);
93
+ const primaryText =
70
94
  snippet?.trim() || formatParticipantNames(participants, session?.email);
95
+ const subtitle = [primaryText, ownerHint].filter(Boolean).join(" · ");
71
96
  const time = formatTime(
72
97
  meeting.actualStart ?? meeting.scheduledStart ?? meeting.createdAt,
73
98
  );
99
+ const soloOwnerAvatar: AttendeeStackParticipant[] =
100
+ participants.length === 0 && ownerHint && meeting.ownerEmail
101
+ ? [{ email: meeting.ownerEmail }]
102
+ : [];
74
103
 
75
104
  return (
76
105
  <NavLink
@@ -79,6 +108,8 @@ export function MeetingHistoryRow({
79
108
  >
80
109
  {participants.length > 0 ? (
81
110
  <AttendeeStack participants={participants} size="md" max={2} />
111
+ ) : soloOwnerAvatar.length > 0 ? (
112
+ <AttendeeStack participants={soloOwnerAvatar} size="md" max={1} />
82
113
  ) : (
83
114
  <span className="flex h-7 w-7 shrink-0 items-center justify-center rounded-md bg-muted text-muted-foreground">
84
115
  <IconFileText className="h-3.5 w-3.5" />
@@ -94,7 +94,7 @@ export function RecordingOptionsMenu({
94
94
  <Button
95
95
  variant="ghost"
96
96
  size="icon"
97
- className="-mx-1.5 h-auto w-auto shrink-0 px-0.5 py-1.5"
97
+ className="h-auto w-auto shrink-0 px-0.5 py-1.5"
98
98
  aria-label={t("deleteRecordingMenu.clipOptions")}
99
99
  >
100
100
  <IconDotsVertical className="h-4 w-4" />
@@ -506,6 +506,7 @@ export class RecorderEngine {
506
506
  private audioMixSources: MediaStreamAudioSourceNode[] = [];
507
507
  private wakeLock: WakeLockSentinel | null = null;
508
508
  private wakeLockGeneration = 0;
509
+ private wakeLockRetake: (() => void) | null = null;
509
510
  private recorder: MediaRecorder | null = null;
510
511
  private mimeType: string = "video/webm";
511
512
 
@@ -909,7 +910,7 @@ export class RecorderEngine {
909
910
  // during active recording, which ends getDisplayMedia's video track
910
911
  // outright (unlike the mic/camera, display capture requires an
911
912
  // active compositor). Without this, a paused recording left idle
912
- // finalizes early with no warning — see onDisplayTrackEnded below.
913
+ // finalizes early — see onDisplayTrackEnded below.
913
914
  // Best-effort: unsupported browsers or a denied request must never
914
915
  // block capture.
915
916
  void this.acquireWakeLock();
@@ -1010,6 +1011,14 @@ export class RecorderEngine {
1010
1011
  );
1011
1012
  track.addEventListener("ended", () => {
1012
1013
  if (this.state === "recording" || this.state === "paused") {
1014
+ // Paused, the user cannot have clicked "Stop sharing" — the
1015
+ // display slept or locked. Same finalize, but say so instead of
1016
+ // ending as if they had asked for it.
1017
+ if (this.state === "paused") {
1018
+ this.emitWarning(
1019
+ "Screen sharing ended while the recording was paused; saving what was captured so far.",
1020
+ );
1021
+ }
1013
1022
  if (this.opts.onDisplayTrackEnded) {
1014
1023
  this.opts.onDisplayTrackEnded();
1015
1024
  } else {
@@ -2371,7 +2380,22 @@ export class RecorderEngine {
2371
2380
 
2372
2381
  /** Best-effort — unsupported browsers or a denied request must never block capture. */
2373
2382
  private async acquireWakeLock(): Promise<void> {
2383
+ // The platform drops a screen wake lock the moment the document goes
2384
+ // hidden and refuses to grant one while it is hidden, so a single
2385
+ // acquisition only ever protects the foreground. Taking it again on the
2386
+ // way back is the most the page can do.
2387
+ if (!this.wakeLockRetake && typeof document !== "undefined") {
2388
+ const doc = document;
2389
+ this.wakeLockRetake = () => {
2390
+ if (doc.visibilityState === "visible" && this.displayStream) {
2391
+ void this.acquireWakeLock();
2392
+ }
2393
+ };
2394
+ doc.addEventListener("visibilitychange", this.wakeLockRetake);
2395
+ }
2374
2396
  const generation = ++this.wakeLockGeneration;
2397
+ this.wakeLock?.release().catch(() => {});
2398
+ this.wakeLock = null;
2375
2399
  try {
2376
2400
  // coercion-ok: optional chaining yields undefined only when the Wake
2377
2401
  // Lock API itself is absent (older/other browsers) — a real rejection
@@ -2389,6 +2413,10 @@ export class RecorderEngine {
2389
2413
 
2390
2414
  private releaseWakeLock(): void {
2391
2415
  this.wakeLockGeneration += 1;
2416
+ if (this.wakeLockRetake) {
2417
+ document.removeEventListener("visibilitychange", this.wakeLockRetake);
2418
+ this.wakeLockRetake = null;
2419
+ }
2392
2420
  this.wakeLock?.release().catch(() => {});
2393
2421
  this.wakeLock = null;
2394
2422
  }
@@ -351,6 +351,7 @@ const messages = {
351
351
  },
352
352
  meetingDetail: {
353
353
  untitledMeeting: "Untitled meeting",
354
+ recordedBy: "Recorded by {{name}}",
354
355
  unassigned: "Unassigned",
355
356
  them: "Them",
356
357
  me: "Me",
@@ -358,6 +359,10 @@ const messages = {
358
359
  meetingRemoved: "Meeting removed",
359
360
  couldNotRemoveMeeting: "Couldn't remove meeting",
360
361
  couldNotLoadMeeting: "Couldn't load this meeting.",
362
+ retry: "Retry",
363
+ meetingNotFound: "Meeting not found",
364
+ meetingUnavailable: "This meeting isn't available.",
365
+ noAccess: "You may not have access to this meeting.",
361
366
  transcriptCopied: "Transcript copied",
362
367
  couldNotCopyTranscript: "Couldn't copy transcript",
363
368
  allMeetings: "All meetings",
@@ -466,6 +466,7 @@ export default function MeetingDetailRoute() {
466
466
  } | null;
467
467
  recording?: { id: string; durationMs?: number | null } | null;
468
468
  role?: "owner" | "admin" | "editor" | "commenter" | "viewer";
469
+ reason?: "unavailable";
469
470
  };
470
471
 
471
472
  const {
@@ -1018,7 +1019,28 @@ export default function MeetingDetailRoute() {
1018
1019
  finalize,
1019
1020
  ]);
1020
1021
 
1021
- if (isLoading || !meeting) {
1022
+ // Failed, still pending, and loaded-but-absent are three outcomes: the query
1023
+ // never retries, so collapsing any of them into the skeleton pins it forever.
1024
+ // A failed live-poll on top of an already-loaded meeting is none of them —
1025
+ // keep showing the meeting.
1026
+ if (isError && !meeting) {
1027
+ return (
1028
+ <div className="p-6 max-w-2xl mx-auto w-full">
1029
+ <div className="rounded-md border border-destructive/30 bg-destructive/5 px-4 py-3 text-sm text-destructive">
1030
+ {t("meetingDetail.couldNotLoadMeeting")}
1031
+ </div>
1032
+ <Button
1033
+ variant="outline"
1034
+ className="mt-3"
1035
+ onClick={() => refetchMeeting()}
1036
+ >
1037
+ {t("meetingDetail.retry")}
1038
+ </Button>
1039
+ </div>
1040
+ );
1041
+ }
1042
+
1043
+ if (isLoading) {
1022
1044
  return (
1023
1045
  <div className="p-6 max-w-6xl mx-auto w-full">
1024
1046
  <Skeleton className="h-6 w-32 mb-4" />
@@ -1032,12 +1054,19 @@ export default function MeetingDetailRoute() {
1032
1054
  );
1033
1055
  }
1034
1056
 
1035
- if (isError) {
1057
+ if (!meeting) {
1036
1058
  return (
1037
1059
  <div className="p-6 max-w-2xl mx-auto w-full">
1038
- <div className="rounded-md border border-destructive/30 bg-destructive/5 px-4 py-3 text-sm text-destructive">
1039
- {t("meetingDetail.couldNotLoadMeeting")}
1060
+ <div className="rounded-md border px-4 py-3 text-sm text-muted-foreground">
1061
+ {/* Neutral on purpose: `get-meeting` returns one reason for missing
1062
+ and inaccessible so callers cannot probe which ids exist, and
1063
+ saying "not found" here would leak back the distinction the
1064
+ action withholds. */}
1065
+ {t("meetingDetail.meetingUnavailable")}
1040
1066
  </div>
1067
+ <Button asChild variant="outline" className="mt-3">
1068
+ <NavLink to="/meetings">{t("meetingDetail.allMeetings")}</NavLink>
1069
+ </Button>
1041
1070
  </div>
1042
1071
  );
1043
1072
  }
@@ -94,6 +94,7 @@ interface Meeting {
94
94
  userNotesMd?: string | null;
95
95
  source?: "calendar" | "adhoc" | "manual";
96
96
  participants?: AttendeeStackParticipant[];
97
+ ownerEmail?: string | null;
97
98
  }
98
99
 
99
100
  interface SearchMeetingResult extends Meeting {
@@ -43,16 +43,23 @@ export default function ApproveRecordingAccessRequestRoute() {
43
43
  const approvalTokenFromUrl = searchParams.get("token") ?? "";
44
44
  const approvalTokenStorageKey =
45
45
  recordingAccessApprovalSessionKey(recordingId);
46
- const [approvalToken, setApprovalToken] = useState(() => {
47
- if (approvalTokenFromUrl) return approvalTokenFromUrl;
48
- if (typeof window === "undefined" || !recordingId) return "";
46
+ // Only the URL token is knowable on the server, so reading storage in the
47
+ // initializer makes the first client render disagree with the server's and
48
+ // React re-renders the page from scratch. Adopt the stored token after mount.
49
+ const [approvalToken, setApprovalToken] = useState(
50
+ () => approvalTokenFromUrl ?? "",
51
+ );
52
+
53
+ useEffect(() => {
54
+ if (approvalTokenFromUrl || !recordingId) return;
49
55
  try {
50
- return sessionStorage.getItem(approvalTokenStorageKey) ?? "";
51
- } catch {
52
- // coercion-ok: unavailable tab storage is an absent continuation.
53
- return "";
54
- }
55
- });
56
+ const stored = sessionStorage.getItem(approvalTokenStorageKey);
57
+ if (stored) setApprovalToken(stored);
58
+ // Unavailable tab storage is an absent continuation: the page then asks
59
+ // for the token rather than silently continuing without one.
60
+ // coercion-ok: the absent case is visible to the user, not swallowed.
61
+ } catch {}
62
+ }, [approvalTokenFromUrl, recordingId, approvalTokenStorageKey]);
56
63
  const [state, setState] = useState<ApprovalState>({ kind: "loading" });
57
64
 
58
65
  useEffect(() => {
@@ -71,14 +71,24 @@ export default function EmbedRoute() {
71
71
  [searchParams],
72
72
  );
73
73
 
74
- const [password, setPassword] = useState<string | null>(() => {
75
- if (typeof window === "undefined" || !shareId) return null;
74
+ // Same hydration trap the share route had: reading sessionStorage in the
75
+ // initializer makes the first client render disagree with the server's,
76
+ // which has no storage and always renders the locked state. React discards
77
+ // the hydrated tree and re-renders from scratch, so an embedded player goes
78
+ // blank for a returning viewer. Start where the server started and adopt the
79
+ // stored password after mount.
80
+ const [password, setPassword] = useState<string | null>(null);
81
+
82
+ useEffect(() => {
83
+ if (!shareId) return;
76
84
  try {
77
- return sessionStorage.getItem(STORAGE_KEY_PREFIX + shareId);
78
- } catch {
79
- return null;
80
- }
81
- });
85
+ const stored = sessionStorage.getItem(STORAGE_KEY_PREFIX + shareId);
86
+ if (stored) setPassword(stored);
87
+ // Unreadable storage and no stored password are the same state here:
88
+ // both leave `password` null, which renders the password prompt.
89
+ // coercion-ok: the fallback is visible to the viewer, not swallowed.
90
+ } catch {}
91
+ }, [shareId]);
82
92
  const [pwError, setPwError] = useState<string | null>(null);
83
93
  const readyMediaPollRef = useRef<{ key: string; until: number } | null>(null);
84
94
 
@@ -443,14 +443,24 @@ export default function ShareRoute() {
443
443
 
444
444
  const playerRef = useRef<VideoPlayerHandle | null>(null);
445
445
  const readyMediaPollRef = useRef<{ key: string; until: number } | null>(null);
446
- const [password, setPassword] = useState<string | null>(() => {
447
- if (typeof window === "undefined" || !shareId) return null;
446
+ // Reading sessionStorage in the initializer makes the first client render
447
+ // disagree with the server's, which has no storage and always renders the
448
+ // locked state. React answers a mismatch by throwing away the hydrated tree
449
+ // and re-rendering from scratch, so a returning viewer watches a blank share
450
+ // page while everything refetches. Start where the server started and adopt
451
+ // the stored password after mount.
452
+ const [password, setPassword] = useState<string | null>(null);
453
+
454
+ useEffect(() => {
455
+ if (!shareId) return;
448
456
  try {
449
- return sessionStorage.getItem(STORAGE_KEY_PREFIX + shareId);
450
- } catch {
451
- return null;
452
- }
453
- });
457
+ const stored = sessionStorage.getItem(STORAGE_KEY_PREFIX + shareId);
458
+ if (stored) setPassword(stored);
459
+ // Unreadable storage and no stored password are the same state to this
460
+ // screen: both leave `password` null, which renders the password prompt.
461
+ // coercion-ok: the fallback is visible to the viewer, not swallowed.
462
+ } catch {}
463
+ }, [shareId]);
454
464
  const [pwError, setPwError] = useState<string | null>(null);
455
465
  const [currentMs, setCurrentMs] = useState(0);
456
466
  const [commentOpen, setCommentOpen] = useState(false);
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-08-21
4
+ ---
5
+
6
+ The camera bubble now recovers on its own when macOS refuses to start its video, instead of sitting as a black circle until you clicked it.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-08-22
4
+ ---
5
+
6
+ The Chrome extension no longer records tab/system audio when "Include microphone" is turned off.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-08-22
4
+ ---
5
+
6
+ The recordings library no longer returns an error on the first request right after the app starts.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-08-23
4
+ ---
5
+
6
+ Asking the agent what is in your library or shared clips no longer fails on the first request right after the app starts.
@@ -1330,8 +1330,7 @@ async function armRecording(args: {
1330
1330
  sourceAppName: "Chrome",
1331
1331
  sourceWindowTitle: tab.title ?? null,
1332
1332
  hasCamera: cameraInvolved,
1333
- hasAudio:
1334
- settings.includeMicrophone || settings.captureSurface !== "camera",
1333
+ hasAudio: settings.includeMicrophone,
1335
1334
  mimeType: "video/webm",
1336
1335
  requestStreaming: true,
1337
1336
  });
@@ -43,6 +43,7 @@ export interface NativeTranscriptionCapture {
43
43
 
44
44
  const STOP_SETTLE_MS = 1_500;
45
45
  const RESTART_DELAY_MS = 250;
46
+ const MAX_RESTART_ATTEMPTS = 8;
46
47
 
47
48
  function getSpeechRecognitionCtor(): SpeechRecognitionConstructor | null {
48
49
  if (typeof window === "undefined") return null;
@@ -81,20 +82,29 @@ export function createNativeTranscriptionCapture(options?: {
81
82
  let paused = false;
82
83
  let disposed = false;
83
84
  let failureReason = unsupportedReason;
85
+ // Kept apart from `failureReason`: a failed `start()` attempt loses no
86
+ // audio once a later attempt succeeds, whereas `failureReason` records
87
+ // speech that was actually dropped and must survive a recovery.
88
+ let restartStartFailure: string | null = null;
84
89
  let restartTimer: ReturnType<typeof setTimeout> | null = null;
90
+ let restartFailures = 0;
85
91
  let stopPromise: Promise<NativeTranscriptResult> | null = null;
86
92
  let resolveStop: ((result: NativeTranscriptResult) => void) | null = null;
87
93
  let stopTimer: ReturnType<typeof setTimeout> | null = null;
88
94
 
89
95
  const result = (): NativeTranscriptResult => {
90
96
  const text = appendTranscript(finalText, interimText).trim();
97
+ // Captured text does not clear a failure: a capture that died mid-recording
98
+ // still has to be re-transcribed in the cloud, and the reason is the only
99
+ // signal the server has that this transcript is truncated.
91
100
  return {
92
101
  text,
93
102
  failureReason:
94
- text.length > 0
103
+ failureReason ||
104
+ restartStartFailure ||
105
+ (text.length > 0
95
106
  ? null
96
- : failureReason ||
97
- "Chrome Web Speech recognition returned no transcript.",
107
+ : "Chrome Web Speech recognition returned no transcript."),
98
108
  };
99
109
  };
100
110
 
@@ -111,20 +121,34 @@ export function createNativeTranscriptionCapture(options?: {
111
121
  resolve(result());
112
122
  };
113
123
 
114
- const scheduleRestart = (): void => {
124
+ const scheduleRestart = (delayMs: number = RESTART_DELAY_MS): void => {
115
125
  if (restartTimer !== null) return;
116
126
  restartTimer = setTimeout(() => {
117
127
  restartTimer = null;
118
128
  if (disposed || stopped || paused || !recognition) return;
119
129
  try {
120
130
  recognition.start();
131
+ restartFailures = 0;
132
+ // The engine recovered, so a failed start attempt is no longer the
133
+ // transcript's outcome. `failureReason` is deliberately NOT cleared:
134
+ // it records audio that was actually dropped, which a restart cannot
135
+ // recover.
136
+ restartStartFailure = null;
121
137
  } catch (error) {
122
- failureReason =
138
+ restartStartFailure =
123
139
  error instanceof Error
124
140
  ? `Chrome Web Speech recognition could not restart: ${error.message}`
125
141
  : "Chrome Web Speech recognition could not restart.";
142
+ // Nothing started, so no `onend` will arrive to schedule the next try.
143
+ restartFailures += 1;
144
+ if (restartFailures < MAX_RESTART_ATTEMPTS) {
145
+ scheduleRestart(RESTART_DELAY_MS * restartFailures);
146
+ } else {
147
+ // Out of retries: the gap is permanent, so it stops being transient.
148
+ failureReason = failureReason || restartStartFailure;
149
+ }
126
150
  }
127
- }, RESTART_DELAY_MS);
151
+ }, delayMs);
128
152
  };
129
153
 
130
154
  const start = (): void => {
@@ -230,10 +254,18 @@ export function createNativeTranscriptionCapture(options?: {
230
254
  const resume = (): void => {
231
255
  if (!recognition || stopped || disposed) return;
232
256
  paused = false;
257
+ restartFailures = 0;
233
258
  try {
234
259
  recognition.start();
235
260
  } catch {
236
- failureReason = "Chrome Web Speech recognition could not resume.";
261
+ // Chrome throws a transient InvalidStateError if the previous session has
262
+ // not finished tearing down. Nothing started, so no `onend` will arrive to
263
+ // drive the bounded retry loop — schedule it here or transcription stays
264
+ // dead for the rest of the recording. The gap is recorded durably because
265
+ // audio is already live: `paused` is false above.
266
+ failureReason =
267
+ failureReason || "Chrome Web Speech recognition could not resume.";
268
+ scheduleRestart();
237
269
  }
238
270
  };
239
271
 
@@ -25,7 +25,7 @@ import {
25
25
  import { scheduleReadyChime } from "@shared/recording-audio";
26
26
  import {
27
27
  SCREEN_CAPTURE_FRAME_RATE,
28
- screenCaptureVideoConstraints,
28
+ screenCaptureDisplayOptions,
29
29
  type ScreenCaptureSurface,
30
30
  } from "@shared/recording-capture";
31
31
  import {
@@ -327,17 +327,17 @@ async function streamDimensions(
327
327
  };
328
328
  }
329
329
 
330
- function displayConstraints(
330
+ // Gates screen/tab audio on the same "Include microphone" toggle the popup
331
+ // shows for the mic stream — see screenCaptureDisplayOptions for why: mic off
332
+ // is the only signal the user gets to say "capture no audio at all".
333
+ export function displayConstraints(
331
334
  surface: ScreenCaptureSurface,
335
+ wantsMic: boolean,
332
336
  ): MediaStreamConstraints {
333
- return {
334
- video: screenCaptureVideoConstraints(surface),
335
- audio: {
336
- echoCancellation: false,
337
- noiseSuppression: false,
338
- autoGainControl: false,
339
- },
340
- } as MediaStreamConstraints;
337
+ return screenCaptureDisplayOptions(
338
+ surface,
339
+ wantsMic,
340
+ ) as MediaStreamConstraints;
341
341
  }
342
342
 
343
343
  // The user's chosen camera/mic devices (set in the popup, saved to storage).
@@ -1133,7 +1133,7 @@ async function acquire(message: AcquireMessage): Promise<{
1133
1133
  } else {
1134
1134
  // Native "Choose what to share" picker. This is the screenshot Steve showed.
1135
1135
  displayStream = await navigator.mediaDevices.getDisplayMedia(
1136
- displayConstraints(message.surface),
1136
+ displayConstraints(message.surface, message.includeMicrophone),
1137
1137
  );
1138
1138
  if (message.includeMicrophone) {
1139
1139
  const audioLabel = await lookupAudioDeviceLabel(devices.audio);
@@ -1456,20 +1456,16 @@ async function saveNativeTranscript(recording: ActiveRecording): Promise<void> {
1456
1456
  recording.nativeTranscript = captured.text.trim();
1457
1457
  recording.nativeTranscriptFailure = captured.failureReason;
1458
1458
 
1459
- const body = recording.nativeTranscript
1460
- ? {
1461
- recordingId: recording.recordingId,
1462
- fullText: recording.nativeTranscript,
1463
- source: "web-speech",
1464
- }
1465
- : {
1466
- recordingId: recording.recordingId,
1467
- fullText: "",
1468
- source: "web-speech",
1469
- failureReason:
1470
- recording.nativeTranscriptFailure ||
1471
- "Chrome Web Speech recognition returned no transcript.",
1472
- };
1459
+ // A partial capture must carry its reason too — the server treats text
1460
+ // without a reason as a complete transcript and skips the cloud fallback.
1461
+ const body = {
1462
+ recordingId: recording.recordingId,
1463
+ fullText: recording.nativeTranscript,
1464
+ source: "web-speech",
1465
+ ...(recording.nativeTranscriptFailure
1466
+ ? { failureReason: recording.nativeTranscriptFailure }
1467
+ : {}),
1468
+ };
1473
1469
  const headers: Record<string, string> = {
1474
1470
  "Content-Type": "application/json",
1475
1471
  "X-Agent-Native-Frontend": "1",