@agent-native/core 0.103.1 → 0.104.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 (565) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +30 -0
  3. package/corpus/core/package.json +9 -2
  4. package/corpus/core/src/agent/engine/credential-errors.ts +2 -2
  5. package/corpus/core/src/agent/production-agent.ts +30 -0
  6. package/corpus/core/src/agent/run-store.ts +12 -2
  7. package/corpus/core/src/cli/create.ts +1 -0
  8. package/corpus/core/src/cli/skills-content/visual-plan-skill.ts +30 -11
  9. package/corpus/core/src/cli/templates-meta.ts +4 -3
  10. package/corpus/core/src/client/AgentAskPopover.tsx +89 -0
  11. package/corpus/core/src/client/AgentChatHome.tsx +4 -0
  12. package/corpus/core/src/client/AgentPanel.tsx +321 -223
  13. package/corpus/core/src/client/AssistantChat.tsx +6 -10
  14. package/corpus/core/src/client/MultiTabAssistantChat.tsx +53 -372
  15. package/corpus/core/src/client/agent-chat-adapter.ts +13 -1
  16. package/corpus/core/src/client/agent-page/AgentContextTab.tsx +133 -149
  17. package/corpus/core/src/client/agent-page/AgentEmptyState.tsx +44 -0
  18. package/corpus/core/src/client/agent-page/AgentJobsTab.tsx +153 -82
  19. package/corpus/core/src/client/agent-page/AgentTabFrame.tsx +39 -0
  20. package/corpus/core/src/client/agent-page/AgentTabsPage.tsx +368 -356
  21. package/corpus/core/src/client/agent-page/types.ts +1 -1
  22. package/corpus/core/src/client/analytics.ts +51 -5
  23. package/corpus/core/src/client/chat/run-recovery.tsx +12 -4
  24. package/corpus/core/src/client/composer/PromptComposer.tsx +5 -15
  25. package/corpus/core/src/client/composer/TiptapComposer.tsx +32 -3
  26. package/corpus/core/src/client/dynamic-suggestions.ts +3 -3
  27. package/corpus/core/src/client/index.ts +16 -1
  28. package/corpus/core/src/client/resources/ResourceTree.tsx +249 -38
  29. package/corpus/core/src/client/resources/ResourcesPanel.tsx +307 -140
  30. package/corpus/core/src/client/session-replay.ts +158 -5
  31. package/corpus/core/src/client/settings/AutomationsSection.tsx +30 -10
  32. package/corpus/core/src/client/settings/SettingsPanel.tsx +3 -2
  33. package/corpus/core/src/client/settings/SettingsTabsPage.tsx +2 -0
  34. package/corpus/core/src/client/settings/agent-settings-search.ts +2 -2
  35. package/corpus/core/src/client/use-chat-threads.ts +2 -0
  36. package/corpus/core/src/connections/catalog.ts +49 -7
  37. package/corpus/core/src/db/index.ts +6 -1
  38. package/corpus/core/src/extensions/actions.ts +106 -16
  39. package/corpus/core/src/extensions/content-patch.ts +22 -0
  40. package/corpus/core/src/extensions/routes.ts +18 -0
  41. package/corpus/core/src/extensions/store.ts +8 -0
  42. package/corpus/core/src/index.ts +3 -0
  43. package/corpus/core/src/ingestion/docx.ts +158 -0
  44. package/corpus/core/src/ingestion/figma-node-to-html.ts +1860 -0
  45. package/corpus/core/src/ingestion/figma.ts +548 -0
  46. package/corpus/core/src/ingestion/index.ts +107 -0
  47. package/corpus/core/src/ingestion/media.ts +264 -0
  48. package/corpus/core/src/ingestion/notion.ts +121 -0
  49. package/corpus/core/src/ingestion/office.ts +214 -0
  50. package/corpus/core/src/ingestion/orchestration.ts +160 -0
  51. package/corpus/core/src/ingestion/pptx.ts +333 -0
  52. package/corpus/core/src/ingestion/selection.ts +81 -0
  53. package/corpus/core/src/ingestion/website.ts +151 -0
  54. package/corpus/core/src/integrations/plugin.ts +45 -37
  55. package/corpus/core/src/localization/default-messages.ts +35 -12
  56. package/corpus/core/src/observability/hosted-model-experiment.ts +118 -0
  57. package/corpus/core/src/observability/traces.ts +5 -3
  58. package/corpus/core/src/provider-api/index.ts +452 -8
  59. package/corpus/core/src/scripts/agent-engines/list-agent-engines.ts +18 -1
  60. package/corpus/core/src/search-utils/index.ts +112 -0
  61. package/corpus/core/src/secrets/register-framework-secrets.ts +61 -0
  62. package/corpus/core/src/server/action-discovery.ts +33 -8
  63. package/corpus/core/src/server/agent-chat/framework-prompts.ts +2 -2
  64. package/corpus/core/src/server/agent-chat/prompt-resources.ts +144 -0
  65. package/corpus/core/src/server/agent-chat-plugin.ts +6 -0
  66. package/corpus/core/src/server/core-routes-plugin.ts +12 -0
  67. package/corpus/core/src/server/index.ts +30 -0
  68. package/corpus/core/src/server/workspace-provider-oauth.ts +583 -0
  69. package/corpus/core/src/styles/agent-native.css +9 -0
  70. package/corpus/core/src/templates/default/app/i18n/en-US.ts +3 -3
  71. package/corpus/core/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +14 -5
  72. package/corpus/core/src/templates/workspace-root/.env.example +12 -0
  73. package/corpus/core/src/workspace-connections/index.ts +6 -0
  74. package/corpus/core/src/workspace-connections/lifecycle.ts +43 -0
  75. package/corpus/core/src/workspace-connections/store.ts +16 -0
  76. package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +46 -3
  77. package/corpus/templates/analytics/AGENTS.md +9 -0
  78. package/corpus/templates/analytics/actions/dashboard-mutation-api.ts +120 -6
  79. package/corpus/templates/analytics/actions/mutate-dashboard.ts +92 -30
  80. package/corpus/templates/analytics/actions/update-dashboard.ts +5 -5
  81. package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +313 -220
  82. package/corpus/templates/analytics/app/components/layout/CommandPalette.tsx +2 -2
  83. package/corpus/templates/analytics/app/components/layout/Layout.tsx +9 -6
  84. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +90 -64
  85. package/corpus/templates/analytics/app/i18n-data.ts +3 -3
  86. package/corpus/templates/analytics/app/lib/dashboard-list-loading.ts +25 -0
  87. package/corpus/templates/analytics/app/lib/item-popularity.ts +13 -4
  88. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/DashboardFilterBar.tsx +1 -1
  89. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/pivot.ts +8 -3
  90. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/types.ts +1 -0
  91. package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +15 -3
  92. package/corpus/templates/analytics/changelog/2026-07-16-dashboard-filter-defaults-can-now-be-changed-reliably-withou.md +6 -0
  93. package/corpus/templates/analytics/changelog/2026-07-16-dashboard-lists-load-in-one-stable-order-without-popping-bet.md +6 -0
  94. package/corpus/templates/analytics/changelog/2026-07-16-fixed-dashboard-filter-dropdown-text-alignment.md +6 -0
  95. package/corpus/templates/analytics/changelog/2026-07-16-recurring-signed-in-users-can-now-be-viewed-as-weekly-bars-b.md +6 -0
  96. package/corpus/templates/analytics/changelog/2026-07-16-the-analytics-agent-can-now-duplicate-a-chart-and-place-the-.md +6 -0
  97. package/corpus/templates/analytics/seeds/dashboards/agent-native-templates-first-party.json +41 -1
  98. package/corpus/templates/analytics/server/lib/dashboard-catalog.ts +1 -1
  99. package/corpus/templates/analytics/server/lib/first-party-metric-catalog.ts +47 -1
  100. package/corpus/templates/analytics/server/lib/session-replay.ts +68 -0
  101. package/corpus/templates/analytics/server/plugins/agent-chat.ts +1 -0
  102. package/corpus/templates/analytics/server/routes/sessions/lookup.get.ts +47 -0
  103. package/corpus/templates/assets/.agents/skills/asset-generation/SKILL.md +16 -9
  104. package/corpus/templates/assets/.agents/skills/creative-context/SKILL.md +86 -0
  105. package/corpus/templates/assets/AGENTS.md +10 -0
  106. package/corpus/templates/assets/actions/create-generation-session.ts +59 -1
  107. package/corpus/templates/assets/actions/edit-image.ts +2 -0
  108. package/corpus/templates/assets/actions/generate-asset.ts +52 -0
  109. package/corpus/templates/assets/actions/generate-image-batch.ts +55 -0
  110. package/corpus/templates/assets/actions/generate-image.ts +219 -1
  111. package/corpus/templates/assets/actions/open-asset-picker.ts +14 -0
  112. package/corpus/templates/assets/actions/refine-image.ts +3 -0
  113. package/corpus/templates/assets/actions/restyle-image.ts +2 -0
  114. package/corpus/templates/assets/agent-native.app-skill.json +4 -0
  115. package/corpus/templates/assets/app/components/layout/Layout.tsx +2 -0
  116. package/corpus/templates/assets/app/i18n/zh-TW.ts +3 -0
  117. package/corpus/templates/assets/app/i18n-data.ts +29 -13
  118. package/corpus/templates/assets/app/routes/agent.tsx +7 -1
  119. package/corpus/templates/assets/app/routes/library.tsx +9 -0
  120. package/corpus/templates/assets/app/routes/settings.tsx +3 -0
  121. package/corpus/templates/assets/changelog/2026-07-16-creative-context-library.md +6 -0
  122. package/corpus/templates/assets/package.json +1 -0
  123. package/corpus/templates/assets/server/lib/image-processing.ts +1 -22
  124. package/corpus/templates/assets/server/plugins/creative-context.ts +205 -0
  125. package/corpus/templates/brain/app/i18n-data.ts +3 -3
  126. package/corpus/templates/brain/app/lib/brain.ts +1 -1
  127. package/corpus/templates/brain/server/lib/search.ts +17 -67
  128. package/corpus/templates/calendar/app/i18n-data.ts +3 -3
  129. package/corpus/templates/calendar/app/pages/BookingLinksPage.tsx +19 -18
  130. package/corpus/templates/chat/app/i18n/en-US.ts +3 -3
  131. package/corpus/templates/chat/app/lib/agent-page.tsx +10 -1
  132. package/corpus/templates/clips/.agents/skills/meetings/SKILL.md +1 -1
  133. package/corpus/templates/clips/AGENTS.md +4 -0
  134. package/corpus/templates/clips/actions/cleanup-transcript.ts +35 -4
  135. package/corpus/templates/clips/actions/create-meeting.ts +6 -2
  136. package/corpus/templates/clips/actions/create-organization.ts +3 -2
  137. package/corpus/templates/clips/actions/create-recording.ts +4 -1
  138. package/corpus/templates/clips/actions/finalize-recording.ts +44 -7
  139. package/corpus/templates/clips/actions/import-loom-recording.ts +4 -1
  140. package/corpus/templates/clips/actions/lib/create-recording-schema.ts +2 -2
  141. package/corpus/templates/clips/actions/list-meetings.ts +23 -5
  142. package/corpus/templates/clips/actions/list-organization-state.ts +1 -1
  143. package/corpus/templates/clips/actions/remove-silences.ts +10 -1
  144. package/corpus/templates/clips/actions/request-transcript.ts +53 -11
  145. package/corpus/templates/clips/actions/search-recordings-utils.ts +7 -0
  146. package/corpus/templates/clips/actions/search-recordings.ts +5 -8
  147. package/corpus/templates/clips/actions/set-organization-branding.ts +2 -2
  148. package/corpus/templates/clips/actions/stitch-recordings.ts +10 -5
  149. package/corpus/templates/clips/actions/update-ai-request-status.ts +38 -0
  150. package/corpus/templates/clips/app/components/editor/editor-layout.tsx +3 -4
  151. package/corpus/templates/clips/app/components/editor/editor-selection.ts +19 -0
  152. package/corpus/templates/clips/app/components/editor/stitch-manager.tsx +10 -10
  153. package/corpus/templates/clips/app/components/library/bulk-action-toolbar.tsx +1 -2
  154. package/corpus/templates/clips/app/components/library/library-grid.tsx +101 -93
  155. package/corpus/templates/clips/app/components/player/video-player.tsx +11 -0
  156. package/corpus/templates/clips/app/components/workspace/branding-editor.tsx +53 -1
  157. package/corpus/templates/clips/app/i18n/ar-SA.ts +7 -1
  158. package/corpus/templates/clips/app/i18n/de-DE.ts +7 -1
  159. package/corpus/templates/clips/app/i18n/en-US.ts +10 -4
  160. package/corpus/templates/clips/app/i18n/es-ES.ts +7 -1
  161. package/corpus/templates/clips/app/i18n/fr-FR.ts +7 -1
  162. package/corpus/templates/clips/app/i18n/hi-IN.ts +7 -1
  163. package/corpus/templates/clips/app/i18n/ja-JP.ts +7 -1
  164. package/corpus/templates/clips/app/i18n/ko-KR.ts +7 -1
  165. package/corpus/templates/clips/app/i18n/pt-BR.ts +7 -1
  166. package/corpus/templates/clips/app/i18n/zh-CN.ts +6 -1
  167. package/corpus/templates/clips/app/i18n/zh-TW.ts +6 -1
  168. package/corpus/templates/clips/app/routes/_app.settings._index.tsx +5 -9
  169. package/corpus/templates/clips/app/routes/_app.settings.organization.tsx +6 -2
  170. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +129 -27
  171. package/corpus/templates/clips/app/routes/record.tsx +23 -2
  172. package/corpus/templates/clips/app/routes/share.$shareId.tsx +6 -2
  173. package/corpus/templates/clips/changelog/2026-07-16-authorized-viewers-can-open-shared-private-clips-without-a-m.md +6 -0
  174. package/corpus/templates/clips/changelog/2026-07-16-camera-bubble-hover-controls-appear-above-the-face.md +6 -0
  175. package/corpus/templates/clips/changelog/2026-07-16-chrome-extension-recovers-stale-recorder-state.md +6 -0
  176. package/corpus/templates/clips/changelog/2026-07-16-clips-agent-chat-no-longer-shows-a-separate-workspace-header.md +6 -0
  177. package/corpus/templates/clips/changelog/2026-07-16-clips-search-now-matches-recording-content-without-requiring.md +6 -0
  178. package/corpus/templates/clips/changelog/2026-07-16-declined-meetings-no-longer-show-desktop-reminders.md +6 -0
  179. package/corpus/templates/clips/changelog/2026-07-16-failed-desktop-saves-now-show-their-local-recovery-copy.md +6 -0
  180. package/corpus/templates/clips/changelog/2026-07-16-long-desktop-clips-now-preserve-complete-playback-synced-aud.md +6 -0
  181. package/corpus/templates/clips/changelog/2026-07-16-long-recording-lists-keep-stitch-and-library-actions-visible.md +6 -0
  182. package/corpus/templates/clips/changelog/2026-07-16-organization-admins-can-choose-new-recording-visibility.md +6 -0
  183. package/corpus/templates/clips/changelog/2026-07-16-remove-silences-now-shows-queued-active-completed-and-failed.md +6 -0
  184. package/corpus/templates/clips/changelog/2026-07-16-settings-no-longer-repeats-the-builder-connection-status-in-.md +6 -0
  185. package/corpus/templates/clips/changelog/2026-07-16-transcripts-now-appear-from-native-browser-speech-sooner-wit.md +6 -0
  186. package/corpus/templates/clips/chrome-extension/public/manifest.json +1 -1
  187. package/corpus/templates/clips/chrome-extension/src/background.ts +73 -1
  188. package/corpus/templates/clips/chrome-extension/src/native-recording-state.ts +32 -0
  189. package/corpus/templates/clips/chrome-extension/src/native-transcription.ts +248 -0
  190. package/corpus/templates/clips/chrome-extension/src/offscreen.ts +106 -0
  191. package/corpus/templates/clips/desktop/src/app.tsx +66 -28
  192. package/corpus/templates/clips/desktop/src/lib/recorder.ts +335 -285
  193. package/corpus/templates/clips/desktop/src/lib/single-flight.ts +7 -0
  194. package/corpus/templates/clips/desktop/src/lib/upload-verification.ts +51 -0
  195. package/corpus/templates/clips/desktop/src/overlays/bubble.tsx +2 -2
  196. package/corpus/templates/clips/desktop/src/overlays/finalizing.tsx +10 -2
  197. package/corpus/templates/clips/desktop/src/styles.css +13 -11
  198. package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +8 -9
  199. package/corpus/templates/clips/desktop/src-tauri/src/meetings_watcher.rs +1 -0
  200. package/corpus/templates/clips/desktop/src-tauri/src/native_screen/custom_capture.rs +88 -13
  201. package/corpus/templates/clips/desktop/src-tauri/src/native_screen/live_upload.rs +13 -2
  202. package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +241 -6
  203. package/corpus/templates/clips/server/db/schema.ts +2 -2
  204. package/corpus/templates/clips/server/lib/builder-media-compression.ts +64 -67
  205. package/corpus/templates/clips/server/lib/calendar-event-classification.ts +19 -0
  206. package/corpus/templates/clips/server/lib/google-calendar-client.ts +1 -0
  207. package/corpus/templates/clips/server/lib/recordings.ts +40 -1
  208. package/corpus/templates/clips/server/plugins/agent-chat.ts +1 -0
  209. package/corpus/templates/clips/server/plugins/db.ts +2 -2
  210. package/corpus/templates/clips/server/routes/api/auth/google-calendar.get.ts +1 -1
  211. package/corpus/templates/clips/server/routes/api/public-recording.get.ts +14 -5
  212. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +51 -1
  213. package/corpus/templates/clips/server/routes/api/video/[recordingId].get.ts +1 -64
  214. package/corpus/templates/clips/shared/media-worker-contract.ts +32 -15
  215. package/corpus/templates/content/.agents/skills/content/SKILL.md +16 -0
  216. package/corpus/templates/content/.agents/skills/creative-context/SKILL.md +85 -0
  217. package/corpus/templates/content/.agents/skills/notion-integration/SKILL.md +8 -0
  218. package/corpus/templates/content/AGENTS.md +9 -0
  219. package/corpus/templates/content/actions/create-document.ts +94 -0
  220. package/corpus/templates/content/actions/edit-document.ts +144 -4
  221. package/corpus/templates/content/actions/update-document.ts +138 -0
  222. package/corpus/templates/content/agent-native.app-skill.json +4 -0
  223. package/corpus/templates/content/app/components/layout/Layout.tsx +2 -0
  224. package/corpus/templates/content/app/i18n/zh-TW.ts +3 -0
  225. package/corpus/templates/content/app/i18n-data.ts +13 -4
  226. package/corpus/templates/content/app/routes/_app.agent.tsx +2 -1
  227. package/corpus/templates/content/app/routes/_app.settings.tsx +3 -0
  228. package/corpus/templates/content/changelog/2026-07-16-creative-context-library.md +6 -0
  229. package/corpus/templates/content/package.json +1 -0
  230. package/corpus/templates/content/server/plugins/creative-context.ts +31 -0
  231. package/corpus/templates/design/.agents/skills/creative-context/SKILL.md +116 -0
  232. package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +16 -0
  233. package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +8 -0
  234. package/corpus/templates/design/AGENTS.md +6 -0
  235. package/corpus/templates/design/actions/clone-creative-context-design.ts +176 -0
  236. package/corpus/templates/design/actions/edit-design.ts +128 -0
  237. package/corpus/templates/design/actions/generate-design.ts +278 -14
  238. package/corpus/templates/design/actions/generate-screens.ts +42 -1
  239. package/corpus/templates/design/agent-native.app-skill.json +4 -0
  240. package/corpus/templates/design/app/components/layout/Layout.tsx +7 -3
  241. package/corpus/templates/design/app/i18n/zh-TW.ts +3 -0
  242. package/corpus/templates/design/app/i18n-data.ts +10 -4
  243. package/corpus/templates/design/app/routes/agent.tsx +2 -1
  244. package/corpus/templates/design/app/routes/settings.tsx +3 -0
  245. package/corpus/templates/design/changelog/2026-07-16-creative-context-library.md +6 -0
  246. package/corpus/templates/design/changelog/2026-07-16-design-agent-chat-no-longer-shows-a-separate-workspace-header.md +6 -0
  247. package/corpus/templates/design/package.json +1 -0
  248. package/corpus/templates/design/server/lib/figma-design-context.ts +5 -335
  249. package/corpus/templates/design/server/lib/figma-node-to-html.ts +1 -1859
  250. package/corpus/templates/design/server/lib/import-design-files.ts +6 -8
  251. package/corpus/templates/design/server/plugins/creative-context.ts +31 -0
  252. package/corpus/templates/design/shared/generation-session.ts +7 -0
  253. package/corpus/templates/dispatch/app/i18n-data.ts +3 -3
  254. package/corpus/templates/forms/app/i18n/en-US.ts +3 -3
  255. package/corpus/templates/macros/app/i18n/en-US.ts +3 -3
  256. package/corpus/templates/mail/app/components/email/EmailThread.tsx +35 -5
  257. package/corpus/templates/mail/app/i18n/en-US.ts +3 -3
  258. package/corpus/templates/mail/changelog/2026-07-16-mail-previews-now-expand-to-show-the-full-message-without-an.md +6 -0
  259. package/corpus/templates/plan/.agents/skills/visual-plan/SKILL.md +30 -11
  260. package/corpus/templates/plan/AGENTS.md +21 -1
  261. package/corpus/templates/plan/actions/get-visual-plan.ts +1 -1
  262. package/corpus/templates/plan/actions/patch-visual-plan-source.ts +161 -0
  263. package/corpus/templates/plan/actions/read-visual-plan-source.ts +1 -1
  264. package/corpus/templates/plan/actions/update-visual-plan.ts +126 -10
  265. package/corpus/templates/plan/app/hooks/use-plans.ts +2 -0
  266. package/corpus/templates/plan/app/i18n/en-US.ts +3 -3
  267. package/corpus/templates/plan/app/pages/PlansPage.tsx +65 -62
  268. package/corpus/templates/plan/changelog/2026-07-16-local-plan-previews-now-load-annotations-containing-html-lik.md +6 -0
  269. package/corpus/templates/plan/changelog/2026-07-16-plan-agents-now-prevent-stale-edits-from-overwriting-newer-p.md +6 -0
  270. package/corpus/templates/plan/server/plan-mdx.ts +75 -2
  271. package/corpus/templates/slides/.agents/skills/create-deck/SKILL.md +21 -4
  272. package/corpus/templates/slides/.agents/skills/creative-context/SKILL.md +122 -0
  273. package/corpus/templates/slides/.agents/skills/design-systems/SKILL.md +11 -0
  274. package/corpus/templates/slides/.agents/skills/slide-editing/SKILL.md +12 -3
  275. package/corpus/templates/slides/AGENTS.md +6 -0
  276. package/corpus/templates/slides/actions/add-slide.ts +230 -14
  277. package/corpus/templates/slides/actions/clone-context-slide.ts +302 -0
  278. package/corpus/templates/slides/actions/create-deck.ts +130 -0
  279. package/corpus/templates/slides/actions/extract-pdf.ts +6 -2
  280. package/corpus/templates/slides/actions/import-file.ts +5 -4
  281. package/corpus/templates/slides/actions/patch-deck.ts +200 -10
  282. package/corpus/templates/slides/actions/update-slide.ts +208 -15
  283. package/corpus/templates/slides/app/components/layout/Layout.tsx +2 -0
  284. package/corpus/templates/slides/app/components/visual-editor/CanvasCommentPins.tsx +16 -1
  285. package/corpus/templates/slides/app/i18n/ar-SA.ts +3 -0
  286. package/corpus/templates/slides/app/i18n/de-DE.ts +3 -0
  287. package/corpus/templates/slides/app/i18n/en-US.ts +6 -3
  288. package/corpus/templates/slides/app/i18n/es-ES.ts +3 -0
  289. package/corpus/templates/slides/app/i18n/fr-FR.ts +3 -0
  290. package/corpus/templates/slides/app/i18n/hi-IN.ts +3 -0
  291. package/corpus/templates/slides/app/i18n/ja-JP.ts +3 -0
  292. package/corpus/templates/slides/app/i18n/ko-KR.ts +3 -0
  293. package/corpus/templates/slides/app/i18n/pt-BR.ts +3 -0
  294. package/corpus/templates/slides/app/i18n/zh-CN.ts +3 -0
  295. package/corpus/templates/slides/app/i18n/zh-TW.ts +3 -0
  296. package/corpus/templates/slides/app/lib/validate-native-slide-html.ts +176 -0
  297. package/corpus/templates/slides/app/routes/agent.tsx +7 -1
  298. package/corpus/templates/slides/app/routes/settings.tsx +3 -0
  299. package/corpus/templates/slides/changelog/2026-07-16-creative-context-library.md +6 -0
  300. package/corpus/templates/slides/changelog/2026-07-16-google-slides-native-cloning.md +6 -0
  301. package/corpus/templates/slides/package.json +1 -0
  302. package/corpus/templates/slides/server/handlers/import/docx-parser.ts +4 -85
  303. package/corpus/templates/slides/server/handlers/import/pptx-parser.ts +7 -421
  304. package/corpus/templates/slides/server/plugins/creative-context.ts +166 -0
  305. package/dist/agent/engine/credential-errors.d.ts +1 -1
  306. package/dist/agent/engine/credential-errors.d.ts.map +1 -1
  307. package/dist/agent/engine/credential-errors.js +2 -2
  308. package/dist/agent/engine/credential-errors.js.map +1 -1
  309. package/dist/agent/production-agent.d.ts.map +1 -1
  310. package/dist/agent/production-agent.js +25 -0
  311. package/dist/agent/production-agent.js.map +1 -1
  312. package/dist/agent/run-store.d.ts.map +1 -1
  313. package/dist/agent/run-store.js +12 -2
  314. package/dist/agent/run-store.js.map +1 -1
  315. package/dist/cli/create.d.ts.map +1 -1
  316. package/dist/cli/create.js +1 -0
  317. package/dist/cli/create.js.map +1 -1
  318. package/dist/cli/skills-content/visual-plan-skill.d.ts +1 -1
  319. package/dist/cli/skills-content/visual-plan-skill.d.ts.map +1 -1
  320. package/dist/cli/skills-content/visual-plan-skill.js +30 -11
  321. package/dist/cli/skills-content/visual-plan-skill.js.map +1 -1
  322. package/dist/cli/templates-meta.d.ts.map +1 -1
  323. package/dist/cli/templates-meta.js +4 -3
  324. package/dist/cli/templates-meta.js.map +1 -1
  325. package/dist/client/AgentAskPopover.d.ts +11 -0
  326. package/dist/client/AgentAskPopover.d.ts.map +1 -0
  327. package/dist/client/AgentAskPopover.js +30 -0
  328. package/dist/client/AgentAskPopover.js.map +1 -0
  329. package/dist/client/AgentChatHome.d.ts +1 -1
  330. package/dist/client/AgentChatHome.d.ts.map +1 -1
  331. package/dist/client/AgentChatHome.js +2 -2
  332. package/dist/client/AgentChatHome.js.map +1 -1
  333. package/dist/client/AgentPanel.d.ts +9 -19
  334. package/dist/client/AgentPanel.d.ts.map +1 -1
  335. package/dist/client/AgentPanel.js +119 -80
  336. package/dist/client/AgentPanel.js.map +1 -1
  337. package/dist/client/AssistantChat.d.ts.map +1 -1
  338. package/dist/client/AssistantChat.js +4 -4
  339. package/dist/client/AssistantChat.js.map +1 -1
  340. package/dist/client/MultiTabAssistantChat.d.ts +3 -9
  341. package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
  342. package/dist/client/MultiTabAssistantChat.js +49 -189
  343. package/dist/client/MultiTabAssistantChat.js.map +1 -1
  344. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  345. package/dist/client/agent-chat-adapter.js +12 -1
  346. package/dist/client/agent-chat-adapter.js.map +1 -1
  347. package/dist/client/agent-page/AgentContextTab.d.ts.map +1 -1
  348. package/dist/client/agent-page/AgentContextTab.js +26 -28
  349. package/dist/client/agent-page/AgentContextTab.js.map +1 -1
  350. package/dist/client/agent-page/AgentEmptyState.d.ts +13 -0
  351. package/dist/client/agent-page/AgentEmptyState.d.ts.map +1 -0
  352. package/dist/client/agent-page/AgentEmptyState.js +6 -0
  353. package/dist/client/agent-page/AgentEmptyState.js.map +1 -0
  354. package/dist/client/agent-page/AgentJobsTab.d.ts +1 -1
  355. package/dist/client/agent-page/AgentJobsTab.d.ts.map +1 -1
  356. package/dist/client/agent-page/AgentJobsTab.js +90 -58
  357. package/dist/client/agent-page/AgentJobsTab.js.map +1 -1
  358. package/dist/client/agent-page/AgentTabFrame.d.ts +12 -0
  359. package/dist/client/agent-page/AgentTabFrame.d.ts.map +1 -0
  360. package/dist/client/agent-page/AgentTabFrame.js +7 -0
  361. package/dist/client/agent-page/AgentTabFrame.js.map +1 -0
  362. package/dist/client/agent-page/AgentTabsPage.d.ts +9 -1
  363. package/dist/client/agent-page/AgentTabsPage.d.ts.map +1 -1
  364. package/dist/client/agent-page/AgentTabsPage.js +135 -99
  365. package/dist/client/agent-page/AgentTabsPage.js.map +1 -1
  366. package/dist/client/agent-page/types.d.ts +1 -1
  367. package/dist/client/agent-page/types.d.ts.map +1 -1
  368. package/dist/client/agent-page/types.js.map +1 -1
  369. package/dist/client/analytics.d.ts +10 -3
  370. package/dist/client/analytics.d.ts.map +1 -1
  371. package/dist/client/analytics.js +37 -2
  372. package/dist/client/analytics.js.map +1 -1
  373. package/dist/client/chat/run-recovery.d.ts.map +1 -1
  374. package/dist/client/chat/run-recovery.js +10 -3
  375. package/dist/client/chat/run-recovery.js.map +1 -1
  376. package/dist/client/composer/PromptComposer.d.ts.map +1 -1
  377. package/dist/client/composer/PromptComposer.js +2 -2
  378. package/dist/client/composer/PromptComposer.js.map +1 -1
  379. package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
  380. package/dist/client/composer/TiptapComposer.js +15 -3
  381. package/dist/client/composer/TiptapComposer.js.map +1 -1
  382. package/dist/client/dynamic-suggestions.js +3 -3
  383. package/dist/client/dynamic-suggestions.js.map +1 -1
  384. package/dist/client/index.d.ts +4 -3
  385. package/dist/client/index.d.ts.map +1 -1
  386. package/dist/client/index.js +3 -2
  387. package/dist/client/index.js.map +1 -1
  388. package/dist/client/resources/ResourceTree.d.ts +5 -1
  389. package/dist/client/resources/ResourceTree.d.ts.map +1 -1
  390. package/dist/client/resources/ResourceTree.js +74 -5
  391. package/dist/client/resources/ResourceTree.js.map +1 -1
  392. package/dist/client/resources/ResourcesPanel.d.ts +11 -2
  393. package/dist/client/resources/ResourcesPanel.d.ts.map +1 -1
  394. package/dist/client/resources/ResourcesPanel.js +105 -31
  395. package/dist/client/resources/ResourcesPanel.js.map +1 -1
  396. package/dist/client/session-replay.d.ts +28 -0
  397. package/dist/client/session-replay.d.ts.map +1 -1
  398. package/dist/client/session-replay.js +122 -8
  399. package/dist/client/session-replay.js.map +1 -1
  400. package/dist/client/settings/AutomationsSection.d.ts +3 -1
  401. package/dist/client/settings/AutomationsSection.d.ts.map +1 -1
  402. package/dist/client/settings/AutomationsSection.js +16 -6
  403. package/dist/client/settings/AutomationsSection.js.map +1 -1
  404. package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
  405. package/dist/client/settings/SettingsPanel.js +3 -3
  406. package/dist/client/settings/SettingsPanel.js.map +1 -1
  407. package/dist/client/settings/SettingsTabsPage.d.ts +2 -0
  408. package/dist/client/settings/SettingsTabsPage.d.ts.map +1 -1
  409. package/dist/client/settings/SettingsTabsPage.js.map +1 -1
  410. package/dist/client/settings/agent-settings-search.js +2 -2
  411. package/dist/client/settings/agent-settings-search.js.map +1 -1
  412. package/dist/client/use-chat-threads.d.ts +2 -0
  413. package/dist/client/use-chat-threads.d.ts.map +1 -1
  414. package/dist/client/use-chat-threads.js.map +1 -1
  415. package/dist/collab/routes.d.ts +1 -1
  416. package/dist/connections/catalog.d.ts +41 -7
  417. package/dist/connections/catalog.d.ts.map +1 -1
  418. package/dist/connections/catalog.js +40 -7
  419. package/dist/connections/catalog.js.map +1 -1
  420. package/dist/db/index.d.ts +2 -2
  421. package/dist/db/index.d.ts.map +1 -1
  422. package/dist/db/index.js +2 -2
  423. package/dist/db/index.js.map +1 -1
  424. package/dist/extensions/actions.d.ts.map +1 -1
  425. package/dist/extensions/actions.js +85 -15
  426. package/dist/extensions/actions.js.map +1 -1
  427. package/dist/extensions/content-patch.d.ts +4 -0
  428. package/dist/extensions/content-patch.d.ts.map +1 -1
  429. package/dist/extensions/content-patch.js +18 -0
  430. package/dist/extensions/content-patch.js.map +1 -1
  431. package/dist/extensions/routes.d.ts.map +1 -1
  432. package/dist/extensions/routes.js +14 -0
  433. package/dist/extensions/routes.js.map +1 -1
  434. package/dist/extensions/store.d.ts +1 -0
  435. package/dist/extensions/store.d.ts.map +1 -1
  436. package/dist/extensions/store.js +4 -1
  437. package/dist/extensions/store.js.map +1 -1
  438. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  439. package/dist/index.d.ts +1 -1
  440. package/dist/index.d.ts.map +1 -1
  441. package/dist/index.js +1 -1
  442. package/dist/index.js.map +1 -1
  443. package/dist/ingestion/docx.d.ts +16 -0
  444. package/dist/ingestion/docx.d.ts.map +1 -0
  445. package/dist/ingestion/docx.js +129 -0
  446. package/dist/ingestion/docx.js.map +1 -0
  447. package/dist/ingestion/figma-node-to-html.d.ts +304 -0
  448. package/dist/ingestion/figma-node-to-html.d.ts.map +1 -0
  449. package/dist/ingestion/figma-node-to-html.js +1292 -0
  450. package/dist/ingestion/figma-node-to-html.js.map +1 -0
  451. package/dist/ingestion/figma.d.ts +115 -0
  452. package/dist/ingestion/figma.d.ts.map +1 -0
  453. package/dist/ingestion/figma.js +399 -0
  454. package/dist/ingestion/figma.js.map +1 -0
  455. package/dist/ingestion/index.d.ts +11 -0
  456. package/dist/ingestion/index.d.ts.map +1 -0
  457. package/dist/ingestion/index.js +11 -0
  458. package/dist/ingestion/index.js.map +1 -0
  459. package/dist/ingestion/media.d.ts +36 -0
  460. package/dist/ingestion/media.d.ts.map +1 -0
  461. package/dist/ingestion/media.js +178 -0
  462. package/dist/ingestion/media.js.map +1 -0
  463. package/dist/ingestion/notion.d.ts +12 -0
  464. package/dist/ingestion/notion.d.ts.map +1 -0
  465. package/dist/ingestion/notion.js +104 -0
  466. package/dist/ingestion/notion.js.map +1 -0
  467. package/dist/ingestion/office.d.ts +45 -0
  468. package/dist/ingestion/office.d.ts.map +1 -0
  469. package/dist/ingestion/office.js +141 -0
  470. package/dist/ingestion/office.js.map +1 -0
  471. package/dist/ingestion/orchestration.d.ts +52 -0
  472. package/dist/ingestion/orchestration.d.ts.map +1 -0
  473. package/dist/ingestion/orchestration.js +89 -0
  474. package/dist/ingestion/orchestration.js.map +1 -0
  475. package/dist/ingestion/pptx.d.ts +28 -0
  476. package/dist/ingestion/pptx.d.ts.map +1 -0
  477. package/dist/ingestion/pptx.js +255 -0
  478. package/dist/ingestion/pptx.js.map +1 -0
  479. package/dist/ingestion/selection.d.ts +20 -0
  480. package/dist/ingestion/selection.d.ts.map +1 -0
  481. package/dist/ingestion/selection.js +52 -0
  482. package/dist/ingestion/selection.js.map +1 -0
  483. package/dist/ingestion/website.d.ts +27 -0
  484. package/dist/ingestion/website.d.ts.map +1 -0
  485. package/dist/ingestion/website.js +100 -0
  486. package/dist/ingestion/website.js.map +1 -0
  487. package/dist/integrations/plugin.d.ts.map +1 -1
  488. package/dist/integrations/plugin.js +32 -27
  489. package/dist/integrations/plugin.js.map +1 -1
  490. package/dist/localization/default-messages.d.ts +22 -0
  491. package/dist/localization/default-messages.d.ts.map +1 -1
  492. package/dist/localization/default-messages.js +31 -9
  493. package/dist/localization/default-messages.js.map +1 -1
  494. package/dist/notifications/routes.d.ts +1 -1
  495. package/dist/observability/hosted-model-experiment.d.ts +39 -0
  496. package/dist/observability/hosted-model-experiment.d.ts.map +1 -0
  497. package/dist/observability/hosted-model-experiment.js +90 -0
  498. package/dist/observability/hosted-model-experiment.js.map +1 -0
  499. package/dist/observability/traces.d.ts.map +1 -1
  500. package/dist/observability/traces.js +5 -3
  501. package/dist/observability/traces.js.map +1 -1
  502. package/dist/progress/routes.d.ts +1 -1
  503. package/dist/provider-api/index.d.ts +35 -4
  504. package/dist/provider-api/index.d.ts.map +1 -1
  505. package/dist/provider-api/index.js +339 -8
  506. package/dist/provider-api/index.js.map +1 -1
  507. package/dist/scripts/agent-engines/list-agent-engines.d.ts.map +1 -1
  508. package/dist/scripts/agent-engines/list-agent-engines.js +18 -1
  509. package/dist/scripts/agent-engines/list-agent-engines.js.map +1 -1
  510. package/dist/search-utils/index.d.ts +12 -0
  511. package/dist/search-utils/index.d.ts.map +1 -0
  512. package/dist/search-utils/index.js +95 -0
  513. package/dist/search-utils/index.js.map +1 -0
  514. package/dist/secrets/register-framework-secrets.d.ts.map +1 -1
  515. package/dist/secrets/register-framework-secrets.js +57 -0
  516. package/dist/secrets/register-framework-secrets.js.map +1 -1
  517. package/dist/server/action-discovery.d.ts +8 -0
  518. package/dist/server/action-discovery.d.ts.map +1 -1
  519. package/dist/server/action-discovery.js +26 -9
  520. package/dist/server/action-discovery.js.map +1 -1
  521. package/dist/server/agent-chat/framework-prompts.js +2 -2
  522. package/dist/server/agent-chat/framework-prompts.js.map +1 -1
  523. package/dist/server/agent-chat/prompt-resources.d.ts +19 -0
  524. package/dist/server/agent-chat/prompt-resources.d.ts.map +1 -1
  525. package/dist/server/agent-chat/prompt-resources.js +95 -0
  526. package/dist/server/agent-chat/prompt-resources.js.map +1 -1
  527. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  528. package/dist/server/agent-chat-plugin.js +7 -0
  529. package/dist/server/agent-chat-plugin.js.map +1 -1
  530. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  531. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  532. package/dist/server/core-routes-plugin.js +5 -0
  533. package/dist/server/core-routes-plugin.js.map +1 -1
  534. package/dist/server/index.d.ts +5 -1
  535. package/dist/server/index.d.ts.map +1 -1
  536. package/dist/server/index.js +5 -1
  537. package/dist/server/index.js.map +1 -1
  538. package/dist/server/workspace-provider-oauth.d.ts +48 -0
  539. package/dist/server/workspace-provider-oauth.d.ts.map +1 -0
  540. package/dist/server/workspace-provider-oauth.js +415 -0
  541. package/dist/server/workspace-provider-oauth.js.map +1 -0
  542. package/dist/styles/agent-native.css +9 -0
  543. package/dist/templates/chat/app/i18n/en-US.ts +3 -3
  544. package/dist/templates/chat/app/lib/agent-page.tsx +10 -1
  545. package/dist/templates/default/app/i18n/en-US.ts +3 -3
  546. package/dist/templates/workspace-core/.agents/skills/extensions/SKILL.md +14 -5
  547. package/dist/templates/workspace-root/.env.example +12 -0
  548. package/dist/workspace-connections/index.d.ts +1 -0
  549. package/dist/workspace-connections/index.d.ts.map +1 -1
  550. package/dist/workspace-connections/index.js +1 -0
  551. package/dist/workspace-connections/index.js.map +1 -1
  552. package/dist/workspace-connections/lifecycle.d.ts +16 -0
  553. package/dist/workspace-connections/lifecycle.d.ts.map +1 -0
  554. package/dist/workspace-connections/lifecycle.js +14 -0
  555. package/dist/workspace-connections/lifecycle.js.map +1 -0
  556. package/dist/workspace-connections/store.d.ts.map +1 -1
  557. package/dist/workspace-connections/store.js +16 -0
  558. package/dist/workspace-connections/store.js.map +1 -1
  559. package/package.json +10 -3
  560. package/src/templates/chat/app/i18n/en-US.ts +3 -3
  561. package/src/templates/chat/app/lib/agent-page.spec.tsx +10 -1
  562. package/src/templates/chat/app/lib/agent-page.tsx +10 -1
  563. package/src/templates/default/app/i18n/en-US.ts +3 -3
  564. package/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +14 -5
  565. package/src/templates/workspace-root/.env.example +12 -0
@@ -1318,13 +1318,68 @@ pub async fn native_fullscreen_recording_stop_and_upload(
1318
1318
  // final post instead of re-uploading the whole file.
1319
1319
  #[cfg(target_os = "macos")]
1320
1320
  if let Some(live) = session.live_upload.take() {
1321
+ let verified_duration_ms = match probe_local_media_duration_ms(&saved.file_path) {
1322
+ Ok(media_duration_ms)
1323
+ if media_durations_materially_match(duration_ms, media_duration_ms) =>
1324
+ {
1325
+ media_duration_ms
1326
+ }
1327
+ Ok(media_duration_ms) => {
1328
+ let error = format!(
1329
+ "Clip may be incomplete. The local media duration ({media_duration_ms} ms) did not match the recorded duration ({duration_ms} ms)."
1330
+ );
1331
+ live.ctrl.cancelled.store(true, Ordering::SeqCst);
1332
+ let _ = live.result_rx.await;
1333
+ saved.last_attempt_at = Some(now_iso());
1334
+ saved.last_error = Some(error.clone());
1335
+ saved.retry_count = saved.retry_count.saturating_add(1);
1336
+ let _ = write_saved_recording_metadata(&app, &saved);
1337
+ emit_native_upload_progress(&app, "failed", "Upload paused", None, None);
1338
+ let error = format!(
1339
+ "{error} The clip was saved locally and can be retried from the Clips menu."
1340
+ );
1341
+ emit_native_upload_finished(
1342
+ &app,
1343
+ &server_url,
1344
+ &recording_id,
1345
+ false,
1346
+ Some(error.clone()),
1347
+ Some(&saved.file_path),
1348
+ );
1349
+ return Err(error);
1350
+ }
1351
+ Err(probe_error) => {
1352
+ let error = format!(
1353
+ "Clip may be incomplete. The local media duration could not be verified ({probe_error})."
1354
+ );
1355
+ live.ctrl.cancelled.store(true, Ordering::SeqCst);
1356
+ let _ = live.result_rx.await;
1357
+ saved.last_attempt_at = Some(now_iso());
1358
+ saved.last_error = Some(error.clone());
1359
+ saved.retry_count = saved.retry_count.saturating_add(1);
1360
+ let _ = write_saved_recording_metadata(&app, &saved);
1361
+ emit_native_upload_progress(&app, "failed", "Upload paused", None, None);
1362
+ let error = format!(
1363
+ "{error} The clip was saved locally and can be retried from the Clips menu."
1364
+ );
1365
+ emit_native_upload_finished(
1366
+ &app,
1367
+ &server_url,
1368
+ &recording_id,
1369
+ false,
1370
+ Some(error.clone()),
1371
+ Some(&saved.file_path),
1372
+ );
1373
+ return Err(error);
1374
+ }
1375
+ };
1321
1376
  eprintln!(
1322
- "[live-upload] stop: signalling finalize for {recording_id} (duration_ms={duration_ms})"
1377
+ "[live-upload] stop: signalling finalize for {recording_id} (measured_duration_ms={verified_duration_ms})"
1323
1378
  );
1324
1379
  emit_native_upload_progress(&app, "uploading", "Uploading clip", None, None);
1325
1380
  live.ctrl
1326
1381
  .duration_ms
1327
- .store(duration_ms as u64, Ordering::SeqCst);
1382
+ .store(verified_duration_ms as u64, Ordering::SeqCst);
1328
1383
  live.ctrl.finalize.store(true, Ordering::SeqCst);
1329
1384
  let result = match live.result_rx.await {
1330
1385
  Ok(inner) => inner,
@@ -1343,7 +1398,7 @@ pub async fn native_fullscreen_recording_stop_and_upload(
1343
1398
  emit_native_upload_finished(&app, &server_url, &recording_id, true, None, None);
1344
1399
  Ok(NativeFullscreenUploadResult {
1345
1400
  recording_id,
1346
- duration_ms,
1401
+ duration_ms: verified_duration_ms,
1347
1402
  width: session.width,
1348
1403
  height: session.height,
1349
1404
  bytes,
@@ -3830,6 +3885,75 @@ fn concat_saved_recording_segments(_segments: &[PathBuf], _output: &Path) -> Res
3830
3885
  Err("Segment concat is only available on macOS.".into())
3831
3886
  }
3832
3887
 
3888
+ #[cfg(target_os = "macos")]
3889
+ fn probe_local_media_duration_ms(path: &Path) -> Result<u128, String> {
3890
+ use std::ffi::CString;
3891
+
3892
+ use objc2::encode::{Encode, Encoding, RefEncode};
3893
+ use objc2::rc::Retained;
3894
+ use objc2::runtime::{AnyClass, AnyObject};
3895
+ use objc2::{class, msg_send};
3896
+
3897
+ #[repr(C)]
3898
+ #[derive(Copy, Clone)]
3899
+ struct CMTime {
3900
+ value: i64,
3901
+ timescale: i32,
3902
+ flags: u32,
3903
+ epoch: i64,
3904
+ }
3905
+
3906
+ unsafe impl RefEncode for CMTime {
3907
+ const ENCODING_REF: Encoding = Encoding::Pointer(&Self::ENCODING);
3908
+ }
3909
+ unsafe impl Encode for CMTime {
3910
+ const ENCODING: Encoding = Encoding::Struct(
3911
+ "CMTime",
3912
+ &[i64::ENCODING, i32::ENCODING, u32::ENCODING, i64::ENCODING],
3913
+ );
3914
+ }
3915
+
3916
+ let path_str = path
3917
+ .to_str()
3918
+ .ok_or_else(|| format!("recording path is not valid UTF-8: {}", path.display()))?;
3919
+ let path_cstr = CString::new(path_str)
3920
+ .map_err(|_| format!("recording path contains a null byte: {}", path.display()))?;
3921
+ let asset_class_name = CString::new("AVURLAsset").expect("static class name");
3922
+ let asset_class =
3923
+ AnyClass::get(&asset_class_name).ok_or_else(|| "AVURLAsset is unavailable".to_string())?;
3924
+
3925
+ unsafe {
3926
+ let string_allocated: *mut AnyObject = msg_send![class!(NSString), alloc];
3927
+ let string_raw: *mut AnyObject =
3928
+ msg_send![string_allocated, initWithUTF8String: path_cstr.as_ptr()];
3929
+ let path_string = Retained::<AnyObject>::from_raw(string_raw)
3930
+ .ok_or_else(|| format!("could not represent recording path: {}", path.display()))?;
3931
+ let url_raw: *mut AnyObject = msg_send![class!(NSURL), fileURLWithPath: &*path_string];
3932
+ let url = Retained::<AnyObject>::retain(url_raw)
3933
+ .ok_or_else(|| format!("could not open recording path: {}", path.display()))?;
3934
+ let asset_raw: *mut AnyObject =
3935
+ msg_send![asset_class, URLAssetWithURL: &*url, options: std::ptr::null::<AnyObject>()];
3936
+ let asset = Retained::<AnyObject>::retain(asset_raw)
3937
+ .ok_or_else(|| format!("could not inspect local recording: {}", path.display()))?;
3938
+ let duration: CMTime = msg_send![&*asset, duration];
3939
+ if duration.flags & 1 == 0 || duration.timescale <= 0 || duration.value <= 0 {
3940
+ return Err(format!(
3941
+ "Clip may be incomplete. The local media duration could not be verified; the local backup was kept ({})",
3942
+ path.display()
3943
+ ));
3944
+ }
3945
+ let duration_ms = (i128::from(duration.value) * 1_000)
3946
+ .checked_div(i128::from(duration.timescale))
3947
+ .and_then(|value| u128::try_from(value).ok())
3948
+ .filter(|value| *value > 0)
3949
+ .ok_or_else(|| {
3950
+ "Clip may be incomplete. The local media duration was invalid; the local backup was kept."
3951
+ .to_string()
3952
+ })?;
3953
+ Ok(duration_ms)
3954
+ }
3955
+ }
3956
+
3833
3957
  async fn upload_prepared_recording_file(
3834
3958
  app: &AppHandle,
3835
3959
  prepared: &PreparedRecordingFile,
@@ -3844,6 +3968,19 @@ async fn upload_prepared_recording_file(
3844
3968
  has_audio: bool,
3845
3969
  has_camera: bool,
3846
3970
  ) -> Result<NativeFullscreenUploadResult, String> {
3971
+ #[cfg(target_os = "macos")]
3972
+ let verified_local_duration_ms = {
3973
+ let media_duration_ms = probe_local_media_duration_ms(&prepared.path)?;
3974
+ if !media_durations_materially_match(duration_ms, media_duration_ms) {
3975
+ return Err(format!(
3976
+ "Clip may be incomplete. The local media duration ({media_duration_ms} ms) did not match the recorded duration ({duration_ms} ms); the local backup was kept."
3977
+ ));
3978
+ }
3979
+ media_duration_ms
3980
+ };
3981
+ #[cfg(not(target_os = "macos"))]
3982
+ let verified_local_duration_ms = duration_ms;
3983
+
3847
3984
  let total_bytes = prepared.bytes;
3848
3985
  let total_bytes_usize = usize::try_from(total_bytes)
3849
3986
  .map_err(|_| "Native recording is too large to upload on this system.".to_string())?;
@@ -3892,6 +4029,7 @@ async fn upload_prepared_recording_file(
3892
4029
  has_camera,
3893
4030
  upload_mode,
3894
4031
  false,
4032
+ None,
3895
4033
  buffer,
3896
4034
  )
3897
4035
  .await?;
@@ -3926,7 +4064,7 @@ async fn upload_prepared_recording_file(
3926
4064
  streaming_full_chunks,
3927
4065
  total_posts,
3928
4066
  true,
3929
- Some(duration_ms),
4067
+ Some(verified_local_duration_ms),
3930
4068
  &prepared.mime_type,
3931
4069
  width,
3932
4070
  height,
@@ -3934,6 +4072,7 @@ async fn upload_prepared_recording_file(
3934
4072
  has_camera,
3935
4073
  upload_mode,
3936
4074
  prepared.locally_transcoded,
4075
+ Some(total_bytes),
3937
4076
  final_body,
3938
4077
  )
3939
4078
  .await?;
@@ -3964,6 +4103,7 @@ async fn upload_prepared_recording_file(
3964
4103
  has_camera,
3965
4104
  upload_mode,
3966
4105
  false,
4106
+ None,
3967
4107
  buffer,
3968
4108
  )
3969
4109
  .await?;
@@ -3992,7 +4132,7 @@ async fn upload_prepared_recording_file(
3992
4132
  total_chunks,
3993
4133
  total_posts,
3994
4134
  true,
3995
- Some(duration_ms),
4135
+ Some(verified_local_duration_ms),
3996
4136
  &prepared.mime_type,
3997
4137
  width,
3998
4138
  height,
@@ -4000,6 +4140,7 @@ async fn upload_prepared_recording_file(
4000
4140
  has_camera,
4001
4141
  upload_mode,
4002
4142
  prepared.locally_transcoded,
4143
+ Some(total_bytes),
4003
4144
  Vec::new(),
4004
4145
  )
4005
4146
  .await?;
@@ -4008,7 +4149,7 @@ async fn upload_prepared_recording_file(
4008
4149
  emit_native_upload_progress(app, "opening", "Uploading clip", None, Some(1.0));
4009
4150
  Ok(NativeFullscreenUploadResult {
4010
4151
  recording_id,
4011
- duration_ms,
4152
+ duration_ms: verified_local_duration_ms,
4012
4153
  width,
4013
4154
  height,
4014
4155
  bytes: total_bytes,
@@ -4127,6 +4268,7 @@ async fn send_upload_post(
4127
4268
  has_camera: bool,
4128
4269
  upload_mode: NativeUploadMode,
4129
4270
  locally_transcoded: bool,
4271
+ expected_source_bytes: Option<u64>,
4130
4272
  body: Vec<u8>,
4131
4273
  ) -> Result<(), String> {
4132
4274
  let body_len = body.len();
@@ -4177,6 +4319,17 @@ async fn send_upload_post(
4177
4319
  body.chars().take(400).collect::<String>()
4178
4320
  ));
4179
4321
  }
4322
+ if is_final {
4323
+ verify_native_finalize_receipt(
4324
+ &body,
4325
+ expected_source_bytes.ok_or_else(|| {
4326
+ "Clip may be incomplete: final upload had no local byte count".to_string()
4327
+ })?,
4328
+ duration_ms.ok_or_else(|| {
4329
+ "Clip may be incomplete: final upload had no local duration".to_string()
4330
+ })?,
4331
+ )?;
4332
+ }
4180
4333
  eprintln!(
4181
4334
  "[clips-tray] native upload post ok recording={recording_id} mode={} index={index}/{total} final={is_final}",
4182
4335
  upload_mode.label()
@@ -4184,6 +4337,88 @@ async fn send_upload_post(
4184
4337
  Ok(())
4185
4338
  }
4186
4339
 
4340
+ #[derive(Deserialize)]
4341
+ #[serde(rename_all = "camelCase")]
4342
+ struct NativeFinalizeReceipt {
4343
+ ok: bool,
4344
+ finalized: bool,
4345
+ status: Option<String>,
4346
+ source_size_bytes: Option<u64>,
4347
+ duration_ms: Option<u128>,
4348
+ }
4349
+
4350
+ fn verify_native_finalize_receipt(
4351
+ body: &str,
4352
+ expected_source_bytes: u64,
4353
+ expected_duration_ms: u128,
4354
+ ) -> Result<(), String> {
4355
+ let receipt: NativeFinalizeReceipt = serde_json::from_str(body).map_err(|_| {
4356
+ "Clip may be incomplete. The final upload receipt was unreadable; the local backup was kept."
4357
+ .to_string()
4358
+ })?;
4359
+ if !receipt.ok || !receipt.finalized || receipt.status.as_deref() != Some("ready") {
4360
+ return Err(
4361
+ "Clip may be incomplete. Finalization was not confirmed; the local backup was kept."
4362
+ .to_string(),
4363
+ );
4364
+ }
4365
+ if receipt.source_size_bytes != Some(expected_source_bytes) {
4366
+ return Err(format!(
4367
+ "Clip may be incomplete. The server did not confirm all {expected_source_bytes} source bytes; the local backup was kept."
4368
+ ));
4369
+ }
4370
+ let actual_duration_ms = receipt.duration_ms.unwrap_or(0);
4371
+ if !media_durations_materially_match(expected_duration_ms, actual_duration_ms) {
4372
+ return Err(
4373
+ "Clip may be incomplete. The uploaded duration did not match the local recording; the local backup was kept."
4374
+ .to_string(),
4375
+ );
4376
+ }
4377
+ Ok(())
4378
+ }
4379
+
4380
+ fn media_durations_materially_match(expected_ms: u128, actual_ms: u128) -> bool {
4381
+ if expected_ms == 0 || actual_ms == 0 {
4382
+ return false;
4383
+ }
4384
+ let tolerance_ms = 5_000_u128.max(expected_ms / 50);
4385
+ actual_ms.abs_diff(expected_ms) <= tolerance_ms
4386
+ }
4387
+
4388
+ #[cfg(test)]
4389
+ mod native_finalize_receipt_tests {
4390
+ use super::{media_durations_materially_match, verify_native_finalize_receipt};
4391
+
4392
+ #[test]
4393
+ fn compares_measured_media_duration_with_a_bounded_tolerance() {
4394
+ assert!(media_durations_materially_match(1_592_773, 1_593_259));
4395
+ assert!(media_durations_materially_match(10_000, 15_000));
4396
+ assert!(!media_durations_materially_match(10_000, 15_001));
4397
+ assert!(!media_durations_materially_match(0, 10_000));
4398
+ assert!(!media_durations_materially_match(10_000, 0));
4399
+ }
4400
+
4401
+ #[test]
4402
+ fn accepts_only_matching_ready_receipts() {
4403
+ let ready = r#"{"ok":true,"finalized":true,"status":"ready","sourceSizeBytes":581614005,"durationMs":1593259}"#;
4404
+ assert!(verify_native_finalize_receipt(ready, 581_614_005, 1_592_773).is_ok());
4405
+
4406
+ let short = r#"{"ok":true,"finalized":true,"status":"ready","sourceSizeBytes":581614005,"durationMs":483000}"#;
4407
+ assert!(
4408
+ verify_native_finalize_receipt(short, 581_614_005, 1_592_773)
4409
+ .unwrap_err()
4410
+ .contains("duration")
4411
+ );
4412
+
4413
+ let partial = r#"{"ok":true,"finalized":true,"status":"ready","sourceSizeBytes":183000000,"durationMs":1592773}"#;
4414
+ assert!(
4415
+ verify_native_finalize_receipt(partial, 581_614_005, 1_592_773)
4416
+ .unwrap_err()
4417
+ .contains("581614005")
4418
+ );
4419
+ }
4420
+ }
4421
+
4187
4422
  fn upload_url(
4188
4423
  server_url: &str,
4189
4424
  recording_id: &str,
@@ -37,7 +37,7 @@ export const organizationSettings = table("organization_settings", {
37
37
  enum: ["private", "org", "public"],
38
38
  })
39
39
  .notNull()
40
- .default("private"),
40
+ .default("public"),
41
41
  createdAt: text("created_at").notNull().default(now()),
42
42
  updatedAt: text("updated_at").notNull().default(now()),
43
43
  });
@@ -52,7 +52,7 @@ export const workspaces = table("workspaces", {
52
52
  enum: ["private", "org", "public"],
53
53
  })
54
54
  .notNull()
55
- .default("private"),
55
+ .default("public"),
56
56
  createdAt: text("created_at").notNull().default(now()),
57
57
  updatedAt: text("updated_at").notNull().default(now()),
58
58
  ...ownableColumns(),
@@ -25,7 +25,6 @@ import { ownerEmailMatches } from "./recordings.js";
25
25
 
26
26
  const BUILDER_CDN_HOST_RE = /^cdn(?:-qa)?\.builder\.io$/i;
27
27
  const DEFAULT_TRIGGER_TIMEOUT_MS = 45_000;
28
- const DEFAULT_PROBE_TIMEOUT_MS = 10_000;
29
28
  const DEFAULT_MAX_SOURCE_BYTES = 512 * 1024 * 1024;
30
29
  const MAX_TRIGGER_ATTEMPTS = 5;
31
30
  const TRIGGERED_POLL_DELAY_MS = 5 * 60 * 1000;
@@ -102,13 +101,6 @@ function triggerTimeoutMs(): number {
102
101
  );
103
102
  }
104
103
 
105
- function probeTimeoutMs(): number {
106
- return envNumber(
107
- "CLIPS_BUILDER_COMPRESSION_PROBE_TIMEOUT_MS",
108
- DEFAULT_PROBE_TIMEOUT_MS,
109
- );
110
- }
111
-
112
104
  function maxSourceBytes(): number {
113
105
  return envNumber(
114
106
  "CLIPS_BUILDER_BACKGROUND_COMPRESSION_MAX_BYTES",
@@ -162,14 +154,6 @@ function parsePositiveNumber(value: unknown): number | undefined {
162
154
  return undefined;
163
155
  }
164
156
 
165
- function headerByteLength(headers: Headers): number | undefined {
166
- const length = parsePositiveNumber(headers.get("content-length"));
167
- if (length) return length;
168
- const range = headers.get("content-range") ?? "";
169
- const total = range.match(/\/(\d+)$/)?.[1];
170
- return parsePositiveNumber(total);
171
- }
172
-
173
157
  function timeoutError(label: string, timeoutMs: number): Error {
174
158
  const err = new Error(`${label} timed out after ${timeoutMs}ms`);
175
159
  err.name = "AbortError";
@@ -358,38 +342,13 @@ export function builderCompressedMediaUrl(sourceUrl: string): string | null {
358
342
  return extractBuilderMediaTarget(sourceUrl)?.compressedUrl ?? null;
359
343
  }
360
344
 
361
- async function probeCompressedMedia(
362
- compressedUrl: string,
363
- ): Promise<{ exists: boolean; sizeBytes?: number }> {
364
- const timeoutMs = probeTimeoutMs();
365
- let res = await fetchWithTimeout(
366
- compressedUrl,
367
- { method: "HEAD" },
368
- timeoutMs,
369
- "Builder compressed-media probe",
370
- );
371
- if (res.status === 405) {
372
- res = await fetchWithTimeout(
373
- compressedUrl,
374
- { method: "GET", headers: { Range: "bytes=0-0" } },
375
- timeoutMs,
376
- "Builder compressed-media probe",
377
- );
378
- }
379
- if (
380
- (res.status >= 200 && res.status < 300) ||
381
- res.status === 304 ||
382
- res.status === 206
383
- ) {
384
- return { exists: true, sizeBytes: headerByteLength(res.headers) };
385
- }
386
- if ([403, 404, 416].includes(res.status)) return { exists: false };
387
- throw new Error(`Compressed media probe failed (${res.status})`);
388
- }
389
-
390
345
  async function triggerBuilderCompression(
391
346
  state: BuilderMediaCompressionState,
392
- ): Promise<string | null> {
347
+ ): Promise<{
348
+ url: string;
349
+ durationMs?: number;
350
+ sizeBytes?: number;
351
+ } | null> {
393
352
  const privateKey = await resolveBuilderPrivateKey();
394
353
  if (!privateKey) {
395
354
  throw new Error("Builder private key is not configured");
@@ -414,8 +373,18 @@ async function triggerBuilderCompression(
414
373
  `Builder media compression trigger failed (${res.status}): ${body || res.statusText}`,
415
374
  );
416
375
  }
417
- const json = (await res.json().catch(() => null)) as { url?: string } | null;
418
- return typeof json?.url === "string" ? json.url : null;
376
+ const json = (await res.json().catch(() => null)) as {
377
+ url?: string;
378
+ durationMs?: unknown;
379
+ sizeBytes?: unknown;
380
+ } | null;
381
+ return typeof json?.url === "string"
382
+ ? {
383
+ url: json.url,
384
+ durationMs: parsePositiveNumber(json.durationMs),
385
+ sizeBytes: parsePositiveNumber(json.sizeBytes),
386
+ }
387
+ : null;
419
388
  }
420
389
 
421
390
  async function enqueueWorkerCompression(
@@ -499,10 +468,14 @@ async function swapRecordingToCompressed(
499
468
  state: BuilderMediaCompressionState,
500
469
  compressedUrl: string,
501
470
  sizeBytes?: number,
471
+ durationMs?: number,
502
472
  ): Promise<BuilderMediaCompressionState> {
503
473
  const db = getDb();
504
474
  const [row] = await db
505
- .select({ videoUrl: schema.recordings.videoUrl })
475
+ .select({
476
+ videoUrl: schema.recordings.videoUrl,
477
+ durationMs: schema.recordings.durationMs,
478
+ })
506
479
  .from(schema.recordings)
507
480
  .where(
508
481
  and(
@@ -516,6 +489,18 @@ async function swapRecordingToCompressed(
516
489
  return markCompressionFailed(state, "Recording row no longer exists");
517
490
  }
518
491
 
492
+ if (
493
+ !durationMs ||
494
+ !mediaDurationsMateriallyMatch(row.durationMs, durationMs)
495
+ ) {
496
+ return markCompressionFailed(
497
+ state,
498
+ durationMs
499
+ ? `Compressed media duration mismatch (${durationMs}ms output vs ${row.durationMs}ms source)`
500
+ : "Compressed media completion did not include a verified duration",
501
+ );
502
+ }
503
+
519
504
  if (row.videoUrl === compressedUrl) {
520
505
  return writeCompressionState(state, {
521
506
  status: "ready",
@@ -587,11 +572,6 @@ async function processMediaWorkerState(
587
572
  if (!worker) return state;
588
573
  if (!isDue(state)) return state;
589
574
 
590
- const probe = await probeCompressedMedia(worker.outputUrl).catch(() => null);
591
- if (probe?.exists) {
592
- return swapRecordingToCompressed(state, worker.outputUrl, probe.sizeBytes);
593
- }
594
-
595
575
  if (worker.attempts >= MEDIA_WORKER_MAX_ENQUEUE_ATTEMPTS) {
596
576
  return markCompressionFailed(
597
577
  state,
@@ -652,15 +632,6 @@ async function processCompressionState(
652
632
  });
653
633
  }
654
634
 
655
- const probe = await probeCompressedMedia(state.compressedUrl);
656
- if (probe.exists) {
657
- return swapRecordingToCompressed(
658
- state,
659
- state.compressedUrl,
660
- probe.sizeBytes,
661
- );
662
- }
663
-
664
635
  if (state.status === "triggered" && state.lastTriggeredAt) {
665
636
  const lastTriggeredAt = new Date(state.lastTriggeredAt).getTime();
666
637
  if (
@@ -675,9 +646,14 @@ async function processCompressionState(
675
646
 
676
647
  const attempts = state.attempts + 1;
677
648
  try {
678
- const compressedUrl = await triggerBuilderCompression(state);
679
- if (compressedUrl) {
680
- return swapRecordingToCompressed(state, compressedUrl);
649
+ const compressed = await triggerBuilderCompression(state);
650
+ if (compressed) {
651
+ return swapRecordingToCompressed(
652
+ state,
653
+ compressed.url,
654
+ compressed.sizeBytes,
655
+ compressed.durationMs,
656
+ );
681
657
  }
682
658
  return writeCompressionState(state, {
683
659
  status: "triggered",
@@ -920,7 +896,12 @@ export async function applyMediaWorkerCallback(
920
896
  };
921
897
  }
922
898
 
923
- const next = await swapRecordingToCompressed(state, outputUrl);
899
+ const next = await swapRecordingToCompressed(
900
+ state,
901
+ outputUrl,
902
+ undefined,
903
+ callback.durationMs,
904
+ );
924
905
  if (next.status !== "ready") {
925
906
  return {
926
907
  ok: false,
@@ -934,6 +915,22 @@ export async function applyMediaWorkerCallback(
934
915
  );
935
916
  }
936
917
 
918
+ export function mediaDurationsMateriallyMatch(
919
+ expectedDurationMs: number,
920
+ actualDurationMs: number,
921
+ ): boolean {
922
+ if (
923
+ !Number.isFinite(expectedDurationMs) ||
924
+ !Number.isFinite(actualDurationMs) ||
925
+ expectedDurationMs <= 0 ||
926
+ actualDurationMs <= 0
927
+ ) {
928
+ return false;
929
+ }
930
+ const toleranceMs = Math.max(5_000, expectedDurationMs * 0.02);
931
+ return Math.abs(expectedDurationMs - actualDurationMs) <= toleranceMs;
932
+ }
933
+
937
934
  export async function runBuilderMediaCompressionSweepOnce(): Promise<void> {
938
935
  const exec = getDbExec();
939
936
  const { rows } = await exec.execute({
@@ -55,6 +55,25 @@ function normalizeEmail(email: string | null | undefined): string | null {
55
55
  return trimmed || null;
56
56
  }
57
57
 
58
+ export function isDeclinedCalendarEvent(args: {
59
+ account: CalendarAccountForEventClassification;
60
+ event: CalendarEvent;
61
+ currentUserEmail?: string | null;
62
+ }): boolean {
63
+ const selfEmails = new Set(
64
+ [args.currentUserEmail, args.account.email, args.account.ownerEmail]
65
+ .map(normalizeEmail)
66
+ .filter((email): email is string => Boolean(email)),
67
+ );
68
+
69
+ return (args.event.attendees ?? []).some((attendee) => {
70
+ if (attendee.responseStatus !== "declined") return false;
71
+ if (attendee.self === true) return true;
72
+ const email = normalizeEmail(attendee.email);
73
+ return Boolean(email && selfEmails.has(email));
74
+ });
75
+ }
76
+
58
77
  export function isPersonalSoloCalendarEvent(args: {
59
78
  account: CalendarAccountForEventClassification;
60
79
  event: CalendarEvent;
@@ -63,6 +63,7 @@ export interface CalendarEvent {
63
63
  email?: string;
64
64
  displayName?: string;
65
65
  responseStatus?: string;
66
+ self?: boolean;
66
67
  }>;
67
68
  conferenceData?: {
68
69
  entryPoints?: Array<{
@@ -60,6 +60,45 @@ export async function getEventOwnerEmail(event: H3Event): Promise<string> {
60
60
 
61
61
  export type OrganizationAccessRole = "owner" | "admin" | "member";
62
62
 
63
+ export type RecordingVisibility = "private" | "org" | "public";
64
+
65
+ export const DEFAULT_RECORDING_VISIBILITY: RecordingVisibility = "public";
66
+
67
+ export function isRecordingVisibility(
68
+ value: unknown,
69
+ ): value is RecordingVisibility {
70
+ return value === "private" || value === "org" || value === "public";
71
+ }
72
+
73
+ export function resolveRecordingVisibility(
74
+ explicit: RecordingVisibility | null | undefined,
75
+ configured: unknown,
76
+ ): RecordingVisibility {
77
+ if (explicit) return explicit;
78
+ return isRecordingVisibility(configured)
79
+ ? configured
80
+ : DEFAULT_RECORDING_VISIBILITY;
81
+ }
82
+
83
+ export async function getOrganizationDefaultVisibility(
84
+ organizationId: string | null | undefined,
85
+ ): Promise<RecordingVisibility> {
86
+ if (!organizationId) return DEFAULT_RECORDING_VISIBILITY;
87
+
88
+ try {
89
+ const [row] = await getDb()
90
+ .select({
91
+ defaultVisibility: schema.organizationSettings.defaultVisibility,
92
+ })
93
+ .from(schema.organizationSettings)
94
+ .where(eq(schema.organizationSettings.organizationId, organizationId))
95
+ .limit(1);
96
+ return resolveRecordingVisibility(undefined, row?.defaultVisibility);
97
+ } catch {
98
+ return DEFAULT_RECORDING_VISIBILITY;
99
+ }
100
+ }
101
+
63
102
  const ORG_ROLE_RANK: Record<OrganizationAccessRole, number> = {
64
103
  member: 1,
65
104
  admin: 2,
@@ -231,7 +270,7 @@ export interface RecordingRow {
231
270
  durationMs: number;
232
271
  videoUrl: string | null;
233
272
  status: "uploading" | "processing" | "ready" | "failed";
234
- visibility: "private" | "org" | "public";
273
+ visibility: RecordingVisibility;
235
274
  ownerEmail: string;
236
275
  folderId: string | null;
237
276
  spaceIds: string[];
@@ -29,6 +29,7 @@ const INITIAL_TOOL_NAMES = [
29
29
  "regenerate-chapters",
30
30
  "trim-recording",
31
31
  "remove-silences",
32
+ "update-ai-request-status",
32
33
  "remove-filler-words",
33
34
  "export-to-brain",
34
35
  "navigate",