@agent-native/core 0.118.0 → 0.118.1

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 (375) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +21 -0
  3. package/corpus/core/package.json +1 -1
  4. package/corpus/core/src/a2a/client.ts +32 -10
  5. package/corpus/core/src/cli/index.ts +18 -10
  6. package/corpus/core/src/cli/multi-frontier-runs.ts +5 -3
  7. package/corpus/core/src/cli/templates-meta.ts +0 -12
  8. package/corpus/core/src/cli/workspace-dev.ts +1 -1
  9. package/corpus/core/src/client/AgentPanel.tsx +9 -1
  10. package/corpus/core/src/client/RunStuckBanner.tsx +39 -47
  11. package/corpus/core/src/client/chat/action-chat-ui-surface.tsx +32 -0
  12. package/corpus/core/src/client/chat/message-components.tsx +53 -8
  13. package/corpus/core/src/client/chat/tool-call-display.tsx +24 -9
  14. package/corpus/core/src/client/chat/widgets/InlineExtensionWidget.tsx +0 -1
  15. package/corpus/core/src/client/chat/widgets/builtin-tool-renderers.tsx +7 -3
  16. package/corpus/core/src/client/conversation/AgentConversation.tsx +12 -1
  17. package/corpus/core/src/client/error-capture.ts +2 -6
  18. package/corpus/core/src/client/resources/McpConnectionSuggestion.tsx +20 -14
  19. package/corpus/core/src/client/resources/ResourcesPanel.tsx +5 -1
  20. package/corpus/core/src/client/resources/mcp-integration-catalog.ts +7 -17
  21. package/corpus/core/src/client/resources/mcp-integration-logos.ts +2 -2
  22. package/corpus/core/src/client/route-chunk-recovery.ts +5 -3
  23. package/corpus/core/src/client/session-replay.ts +3 -3
  24. package/corpus/core/src/client/settings/SettingsPanel.tsx +15 -6
  25. package/corpus/core/src/client/transcription/use-live-transcription.ts +64 -45
  26. package/corpus/core/src/deploy/build.ts +2 -1
  27. package/corpus/core/src/guards/db-tool-scoping.ts +1 -1
  28. package/corpus/core/src/integrations/adapters/slack.ts +84 -5
  29. package/corpus/core/src/integrations/index.ts +5 -1
  30. package/corpus/core/src/localization/default-messages.ts +1 -1
  31. package/corpus/core/src/mcp-client/oauth-routes.ts +45 -12
  32. package/corpus/core/src/server/realtime-voice.ts +4 -1
  33. package/corpus/core/src/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +7 -5
  34. package/corpus/core/src/terminal/cli-registry.ts +1 -1
  35. package/corpus/core/src/vite/client.ts +166 -1
  36. package/corpus/templates/analytics/app/hooks/use-dashboard-views.ts +13 -1
  37. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/ViewsMenu.tsx +10 -0
  38. package/corpus/templates/analytics/changelog/2026-07-22-show-a-useful-error-when-saving-a-dashboard-view-fails.md +6 -0
  39. package/corpus/templates/brain/app/routes/sources.tsx +181 -114
  40. package/corpus/templates/clips/AGENTS.md +8 -6
  41. package/corpus/templates/clips/actions/request-transcript.ts +90 -420
  42. package/corpus/templates/clips/actions/save-browser-transcript.ts +11 -28
  43. package/corpus/templates/clips/app/components/capture-install-options.tsx +5 -3
  44. package/corpus/templates/clips/app/components/player/transcript-panel.tsx +3 -136
  45. package/corpus/templates/clips/app/lib/capture-install-options.ts +15 -4
  46. package/corpus/templates/clips/app/routes/download.tsx +3 -2
  47. package/corpus/templates/clips/changelog/2026-07-22-fixed-an-intermittent-hydration-error-on-the-download-page.md +6 -0
  48. package/corpus/templates/clips/changelog/2026-07-22-fixed-transcript-generation-when-native-speech-capture-retur.md +6 -0
  49. package/corpus/templates/clips/changelog/2026-07-22-transcript-generation-now-retries-with-backup-speech-recogni.md +6 -0
  50. package/corpus/templates/clips/server/lib/public-agent-context.ts +2 -2
  51. package/corpus/templates/clips/server/register-secrets.ts +7 -6
  52. package/corpus/templates/content/app/components/editor/SlashCommandMenu.tsx +16 -84
  53. package/corpus/templates/content/app/components/editor/VisualEditor.tsx +13 -7
  54. package/corpus/templates/content/app/components/editor/extensions/ImageBlock.tsx +2 -2
  55. package/corpus/templates/content/app/i18n-data.ts +10 -0
  56. package/corpus/templates/design/server/lib/fig-file-decoder.ts +1 -0
  57. package/corpus/templates/plan/app/entry.client.tsx +3 -0
  58. package/corpus/templates/plan/changelog/2026-07-22-recover-stale-pages-after-a-deployment-refresh-automatically.md +6 -0
  59. package/corpus/templates/slides/app/hooks/use-agent-generating.ts +10 -3
  60. package/corpus/templates/slides/app/pages/Index.tsx +1 -0
  61. package/corpus/templates/slides/changelog/2026-07-22-new-deck-prompts-now-open-in-their-own-visible-chat-thread.md +6 -0
  62. package/corpus/toolkit/CHANGELOG.md +9 -0
  63. package/corpus/toolkit/package.json +1 -1
  64. package/corpus/toolkit/src/composer/PromptComposer.tsx +5 -1
  65. package/corpus/toolkit/src/composer/RealtimeVoiceMode.tsx +119 -31
  66. package/corpus/toolkit/src/composer/useRealtimeVoiceMode.tsx +30 -0
  67. package/corpus/toolkit/src/conformance/runner.tsx +153 -1
  68. package/corpus/toolkit/src/editor/SharedRichEditor.tsx +13 -2
  69. package/corpus/toolkit/src/editor/SlashCommandMenu.tsx +20 -0
  70. package/corpus/toolkit/src/editor/index.ts +2 -0
  71. package/corpus/toolkit/src/styles.css +52 -23
  72. package/dist/a2a/client.d.ts +3 -1
  73. package/dist/a2a/client.d.ts.map +1 -1
  74. package/dist/a2a/client.js +17 -12
  75. package/dist/a2a/client.js.map +1 -1
  76. package/dist/cli/index.js +18 -8
  77. package/dist/cli/index.js.map +1 -1
  78. package/dist/cli/multi-frontier-runs.js +3 -3
  79. package/dist/cli/multi-frontier-runs.js.map +1 -1
  80. package/dist/cli/templates-meta.d.ts.map +1 -1
  81. package/dist/cli/templates-meta.js +0 -12
  82. package/dist/cli/templates-meta.js.map +1 -1
  83. package/dist/cli/workspace-dev.js +1 -1
  84. package/dist/cli/workspace-dev.js.map +1 -1
  85. package/dist/client/AgentPanel.d.ts.map +1 -1
  86. package/dist/client/AgentPanel.js +4 -1
  87. package/dist/client/AgentPanel.js.map +1 -1
  88. package/dist/client/RunStuckBanner.d.ts +3 -5
  89. package/dist/client/RunStuckBanner.d.ts.map +1 -1
  90. package/dist/client/RunStuckBanner.js +17 -18
  91. package/dist/client/RunStuckBanner.js.map +1 -1
  92. package/dist/client/chat/action-chat-ui-surface.d.ts +8 -0
  93. package/dist/client/chat/action-chat-ui-surface.d.ts.map +1 -0
  94. package/dist/client/chat/action-chat-ui-surface.js +10 -0
  95. package/dist/client/chat/action-chat-ui-surface.js.map +1 -0
  96. package/dist/client/chat/message-components.d.ts +3 -0
  97. package/dist/client/chat/message-components.d.ts.map +1 -1
  98. package/dist/client/chat/message-components.js +40 -7
  99. package/dist/client/chat/message-components.js.map +1 -1
  100. package/dist/client/chat/tool-call-display.d.ts +3 -1
  101. package/dist/client/chat/tool-call-display.d.ts.map +1 -1
  102. package/dist/client/chat/tool-call-display.js +13 -9
  103. package/dist/client/chat/tool-call-display.js.map +1 -1
  104. package/dist/client/chat/widgets/InlineExtensionWidget.d.ts.map +1 -1
  105. package/dist/client/chat/widgets/InlineExtensionWidget.js +1 -1
  106. package/dist/client/chat/widgets/InlineExtensionWidget.js.map +1 -1
  107. package/dist/client/chat/widgets/builtin-tool-renderers.d.ts.map +1 -1
  108. package/dist/client/chat/widgets/builtin-tool-renderers.js +5 -3
  109. package/dist/client/chat/widgets/builtin-tool-renderers.js.map +1 -1
  110. package/dist/client/conversation/AgentConversation.d.ts.map +1 -1
  111. package/dist/client/conversation/AgentConversation.js +3 -2
  112. package/dist/client/conversation/AgentConversation.js.map +1 -1
  113. package/dist/client/error-capture.d.ts.map +1 -1
  114. package/dist/client/error-capture.js +2 -2
  115. package/dist/client/error-capture.js.map +1 -1
  116. package/dist/client/resources/McpConnectionSuggestion.d.ts +1 -0
  117. package/dist/client/resources/McpConnectionSuggestion.d.ts.map +1 -1
  118. package/dist/client/resources/McpConnectionSuggestion.js +9 -8
  119. package/dist/client/resources/McpConnectionSuggestion.js.map +1 -1
  120. package/dist/client/resources/ResourcesPanel.d.ts.map +1 -1
  121. package/dist/client/resources/ResourcesPanel.js +3 -1
  122. package/dist/client/resources/ResourcesPanel.js.map +1 -1
  123. package/dist/client/resources/mcp-integration-catalog.d.ts +1 -0
  124. package/dist/client/resources/mcp-integration-catalog.d.ts.map +1 -1
  125. package/dist/client/resources/mcp-integration-catalog.js +4 -17
  126. package/dist/client/resources/mcp-integration-catalog.js.map +1 -1
  127. package/dist/client/resources/mcp-integration-logos.js +2 -2
  128. package/dist/client/resources/mcp-integration-logos.js.map +1 -1
  129. package/dist/client/route-chunk-recovery.d.ts.map +1 -1
  130. package/dist/client/route-chunk-recovery.js +5 -3
  131. package/dist/client/route-chunk-recovery.js.map +1 -1
  132. package/dist/client/session-replay.js +3 -3
  133. package/dist/client/session-replay.js.map +1 -1
  134. package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
  135. package/dist/client/settings/SettingsPanel.js +2 -2
  136. package/dist/client/settings/SettingsPanel.js.map +1 -1
  137. package/dist/client/transcription/use-live-transcription.d.ts +3 -3
  138. package/dist/client/transcription/use-live-transcription.d.ts.map +1 -1
  139. package/dist/client/transcription/use-live-transcription.js +22 -11
  140. package/dist/client/transcription/use-live-transcription.js.map +1 -1
  141. package/dist/collab/routes.d.ts +1 -1
  142. package/dist/collab/struct-routes.d.ts +1 -1
  143. package/dist/deploy/build.d.ts.map +1 -1
  144. package/dist/deploy/build.js +1 -1
  145. package/dist/deploy/build.js.map +1 -1
  146. package/dist/guards/db-tool-scoping.js +1 -1
  147. package/dist/guards/db-tool-scoping.js.map +1 -1
  148. package/dist/integrations/adapters/slack.d.ts +1 -0
  149. package/dist/integrations/adapters/slack.d.ts.map +1 -1
  150. package/dist/integrations/adapters/slack.js +61 -5
  151. package/dist/integrations/adapters/slack.js.map +1 -1
  152. package/dist/integrations/index.d.ts +1 -1
  153. package/dist/integrations/index.d.ts.map +1 -1
  154. package/dist/integrations/index.js +1 -1
  155. package/dist/integrations/index.js.map +1 -1
  156. package/dist/localization/default-messages.js +1 -1
  157. package/dist/localization/default-messages.js.map +1 -1
  158. package/dist/mcp-client/oauth-routes.d.ts +3 -0
  159. package/dist/mcp-client/oauth-routes.d.ts.map +1 -1
  160. package/dist/mcp-client/oauth-routes.js +41 -12
  161. package/dist/mcp-client/oauth-routes.js.map +1 -1
  162. package/dist/notifications/routes.d.ts +2 -2
  163. package/dist/observability/routes.d.ts +3 -3
  164. package/dist/provider-api/actions/custom-provider-registration.d.ts +8 -8
  165. package/dist/provider-api/actions/provider-api.d.ts +11 -11
  166. package/dist/secrets/routes.d.ts +6 -6
  167. package/dist/server/realtime-voice.d.ts.map +1 -1
  168. package/dist/server/realtime-voice.js +1 -1
  169. package/dist/server/realtime-voice.js.map +1 -1
  170. package/dist/server/transcribe-voice.d.ts +1 -1
  171. package/dist/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +7 -5
  172. package/dist/terminal/cli-registry.js +1 -1
  173. package/dist/terminal/cli-registry.js.map +1 -1
  174. package/dist/vite/client.d.ts +8 -1
  175. package/dist/vite/client.d.ts.map +1 -1
  176. package/dist/vite/client.js +113 -2
  177. package/dist/vite/client.js.map +1 -1
  178. package/package.json +2 -2
  179. package/src/a2a/client.ts +32 -10
  180. package/src/cli/index.ts +18 -10
  181. package/src/cli/multi-frontier-runs.ts +5 -3
  182. package/src/cli/templates-meta.ts +0 -12
  183. package/src/cli/workspace-dev.ts +1 -1
  184. package/src/client/AgentPanel.tsx +9 -1
  185. package/src/client/RunStuckBanner.tsx +39 -47
  186. package/src/client/chat/action-chat-ui-surface.tsx +32 -0
  187. package/src/client/chat/message-components.tsx +53 -8
  188. package/src/client/chat/tool-call-display.tsx +24 -9
  189. package/src/client/chat/widgets/InlineExtensionWidget.tsx +0 -1
  190. package/src/client/chat/widgets/builtin-tool-renderers.tsx +7 -3
  191. package/src/client/conversation/AgentConversation.tsx +12 -1
  192. package/src/client/error-capture.ts +2 -6
  193. package/src/client/resources/McpConnectionSuggestion.tsx +20 -14
  194. package/src/client/resources/ResourcesPanel.tsx +5 -1
  195. package/src/client/resources/mcp-integration-catalog.ts +7 -17
  196. package/src/client/resources/mcp-integration-logos.ts +2 -2
  197. package/src/client/route-chunk-recovery.ts +5 -3
  198. package/src/client/session-replay.ts +3 -3
  199. package/src/client/settings/SettingsPanel.tsx +15 -6
  200. package/src/client/transcription/use-live-transcription.ts +64 -45
  201. package/src/deploy/build.ts +2 -1
  202. package/src/guards/db-tool-scoping.ts +1 -1
  203. package/src/integrations/adapters/slack.ts +84 -5
  204. package/src/integrations/index.ts +5 -1
  205. package/src/localization/default-messages.ts +1 -1
  206. package/src/mcp-client/oauth-routes.ts +45 -12
  207. package/src/server/realtime-voice.ts +4 -1
  208. package/src/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +7 -5
  209. package/src/terminal/cli-registry.ts +1 -1
  210. package/src/vite/client.ts +166 -1
  211. package/corpus/templates/crm/.agents/skills/agent-native-docs/SKILL.md +0 -115
  212. package/corpus/templates/crm/.agents/skills/agent-native-toolkit/SKILL.md +0 -177
  213. package/corpus/templates/crm/.agents/skills/crm/SKILL.md +0 -128
  214. package/corpus/templates/crm/.agents/skills/customizing-agent-native/SKILL.md +0 -220
  215. package/corpus/templates/crm/.agents/skills/feature-flags/SKILL.md +0 -169
  216. package/corpus/templates/crm/.agents/skills/upgrade-agent-native/SKILL.md +0 -100
  217. package/corpus/templates/crm/.env.example +0 -5
  218. package/corpus/templates/crm/AGENTS.md +0 -120
  219. package/corpus/templates/crm/CHANGELOG.md +0 -1
  220. package/corpus/templates/crm/DEVELOPING.md +0 -66
  221. package/corpus/templates/crm/README.md +0 -106
  222. package/corpus/templates/crm/_gitignore +0 -12
  223. package/corpus/templates/crm/actions/_crm-action-utils.ts +0 -87
  224. package/corpus/templates/crm/actions/_crm-dashboard.ts +0 -69
  225. package/corpus/templates/crm/actions/_crm-data-program-actions.ts +0 -15
  226. package/corpus/templates/crm/actions/_crm-signal-utils.ts +0 -79
  227. package/corpus/templates/crm/actions/apply-crm-proposals.ts +0 -203
  228. package/corpus/templates/crm/actions/attach-call-evidence.ts +0 -170
  229. package/corpus/templates/crm/actions/configure-crm-connection.ts +0 -126
  230. package/corpus/templates/crm/actions/configure-native-crm.ts +0 -44
  231. package/corpus/templates/crm/actions/create-crm-record.ts +0 -148
  232. package/corpus/templates/crm/actions/create-crm-signal-tracker.ts +0 -58
  233. package/corpus/templates/crm/actions/delete-staged-dataset.ts +0 -14
  234. package/corpus/templates/crm/actions/get-crm-automation-recipe.ts +0 -36
  235. package/corpus/templates/crm/actions/get-crm-dashboard-panel.ts +0 -35
  236. package/corpus/templates/crm/actions/get-crm-dashboard.ts +0 -14
  237. package/corpus/templates/crm/actions/get-crm-overview.ts +0 -13
  238. package/corpus/templates/crm/actions/get-crm-pipeline-data.ts +0 -61
  239. package/corpus/templates/crm/actions/get-crm-record.ts +0 -75
  240. package/corpus/templates/crm/actions/install-crm-pipeline-dashboard.ts +0 -81
  241. package/corpus/templates/crm/actions/list-crm-dashboard-revisions.ts +0 -15
  242. package/corpus/templates/crm/actions/list-crm-dashboards.ts +0 -15
  243. package/corpus/templates/crm/actions/list-crm-proposals.ts +0 -31
  244. package/corpus/templates/crm/actions/list-crm-records.ts +0 -79
  245. package/corpus/templates/crm/actions/list-crm-saved-views.ts +0 -15
  246. package/corpus/templates/crm/actions/list-crm-signal-hits.ts +0 -43
  247. package/corpus/templates/crm/actions/list-crm-signal-trackers.ts +0 -32
  248. package/corpus/templates/crm/actions/list-crm-tasks.ts +0 -22
  249. package/corpus/templates/crm/actions/list-staged-datasets.ts +0 -14
  250. package/corpus/templates/crm/actions/list-workspace-connections.ts +0 -47
  251. package/corpus/templates/crm/actions/manage-crm-signal-tracker.ts +0 -87
  252. package/corpus/templates/crm/actions/manage-crm-task.ts +0 -132
  253. package/corpus/templates/crm/actions/navigate.ts +0 -63
  254. package/corpus/templates/crm/actions/provider-api-catalog.ts +0 -22
  255. package/corpus/templates/crm/actions/provider-api-docs.ts +0 -54
  256. package/corpus/templates/crm/actions/provider-api-request.ts +0 -92
  257. package/corpus/templates/crm/actions/query-staged-dataset.ts +0 -44
  258. package/corpus/templates/crm/actions/record-crm-call-insight.ts +0 -160
  259. package/corpus/templates/crm/actions/record-crm-smart-signal.ts +0 -150
  260. package/corpus/templates/crm/actions/restore-crm-dashboard-revision.ts +0 -23
  261. package/corpus/templates/crm/actions/review-crm-signal.ts +0 -31
  262. package/corpus/templates/crm/actions/run-crm-saved-view-program.ts +0 -103
  263. package/corpus/templates/crm/actions/run-crm-signal-trackers.ts +0 -275
  264. package/corpus/templates/crm/actions/run.ts +0 -7
  265. package/corpus/templates/crm/actions/save-crm-dashboard.ts +0 -51
  266. package/corpus/templates/crm/actions/save-crm-saved-view.ts +0 -119
  267. package/corpus/templates/crm/actions/sync-crm.ts +0 -146
  268. package/corpus/templates/crm/actions/update-crm-record.ts +0 -510
  269. package/corpus/templates/crm/actions/view-screen.ts +0 -218
  270. package/corpus/templates/crm/agent-native.app-skill.json +0 -58
  271. package/corpus/templates/crm/app/components/crm/CreateCrmRecordDialog.tsx +0 -158
  272. package/corpus/templates/crm/app/components/crm/CrmDashboardPanel.tsx +0 -78
  273. package/corpus/templates/crm/app/components/crm/CrmSignalsPanel.tsx +0 -226
  274. package/corpus/templates/crm/app/components/crm/IntelligenceSettings.tsx +0 -414
  275. package/corpus/templates/crm/app/components/crm/RecordActions.tsx +0 -510
  276. package/corpus/templates/crm/app/components/crm/RecordGrid.tsx +0 -134
  277. package/corpus/templates/crm/app/components/crm/RecordWorkspace.tsx +0 -362
  278. package/corpus/templates/crm/app/components/crm/SavedViewDataProgram.tsx +0 -93
  279. package/corpus/templates/crm/app/components/crm/Surface.tsx +0 -97
  280. package/corpus/templates/crm/app/components/crm/WorkOverview.tsx +0 -180
  281. package/corpus/templates/crm/app/components/layout/CrmLayout.tsx +0 -112
  282. package/corpus/templates/crm/app/components/layout/CrmSidebar.tsx +0 -84
  283. package/corpus/templates/crm/app/components/ui/alert-dialog.tsx +0 -1
  284. package/corpus/templates/crm/app/components/ui/badge.tsx +0 -1
  285. package/corpus/templates/crm/app/components/ui/button.tsx +0 -1
  286. package/corpus/templates/crm/app/components/ui/card.tsx +0 -1
  287. package/corpus/templates/crm/app/components/ui/dialog.tsx +0 -1
  288. package/corpus/templates/crm/app/components/ui/input.tsx +0 -1
  289. package/corpus/templates/crm/app/components/ui/label.tsx +0 -1
  290. package/corpus/templates/crm/app/components/ui/select.tsx +0 -1
  291. package/corpus/templates/crm/app/components/ui/sheet.tsx +0 -1
  292. package/corpus/templates/crm/app/components/ui/skeleton.tsx +0 -1
  293. package/corpus/templates/crm/app/components/ui/switch.tsx +0 -1
  294. package/corpus/templates/crm/app/components/ui/table.tsx +0 -1
  295. package/corpus/templates/crm/app/components/ui/tabs.tsx +0 -1
  296. package/corpus/templates/crm/app/components/ui/textarea.tsx +0 -1
  297. package/corpus/templates/crm/app/components/ui/toolkit-provider.tsx +0 -17
  298. package/corpus/templates/crm/app/design-system.ts +0 -3
  299. package/corpus/templates/crm/app/entry.client.tsx +0 -16
  300. package/corpus/templates/crm/app/entry.server.tsx +0 -10
  301. package/corpus/templates/crm/app/global.css +0 -167
  302. package/corpus/templates/crm/app/hooks/use-navigation-state.ts +0 -60
  303. package/corpus/templates/crm/app/i18n/en-US.ts +0 -89
  304. package/corpus/templates/crm/app/i18n/index.ts +0 -34
  305. package/corpus/templates/crm/app/lib/dashboard.ts +0 -13
  306. package/corpus/templates/crm/app/lib/navigation.ts +0 -54
  307. package/corpus/templates/crm/app/lib/tab-id.ts +0 -1
  308. package/corpus/templates/crm/app/lib/types.ts +0 -172
  309. package/corpus/templates/crm/app/lib/utils.ts +0 -1
  310. package/corpus/templates/crm/app/root.tsx +0 -91
  311. package/corpus/templates/crm/app/routes/_index.tsx +0 -26
  312. package/corpus/templates/crm/app/routes/accounts.tsx +0 -41
  313. package/corpus/templates/crm/app/routes/agent.tsx +0 -5
  314. package/corpus/templates/crm/app/routes/ask.tsx +0 -53
  315. package/corpus/templates/crm/app/routes/dashboard.tsx +0 -118
  316. package/corpus/templates/crm/app/routes/opportunities.tsx +0 -41
  317. package/corpus/templates/crm/app/routes/people.tsx +0 -41
  318. package/corpus/templates/crm/app/routes/proposals.tsx +0 -266
  319. package/corpus/templates/crm/app/routes/records.$recordId.tsx +0 -35
  320. package/corpus/templates/crm/app/routes/settings.tsx +0 -63
  321. package/corpus/templates/crm/app/routes/setup.tsx +0 -308
  322. package/corpus/templates/crm/app/routes/tasks.tsx +0 -252
  323. package/corpus/templates/crm/app/routes/views.tsx +0 -333
  324. package/corpus/templates/crm/app/routes.ts +0 -17
  325. package/corpus/templates/crm/app/vite-env.d.ts +0 -6
  326. package/corpus/templates/crm/changelog/2026-07-21-agent-native-crm.md +0 -6
  327. package/corpus/templates/crm/changelog/2026-07-21-clips-call-evidence-review-recipe.md +0 -6
  328. package/corpus/templates/crm/changelog/2026-07-21-crm-can-now-find-and-review-evidence-grounded-call-signals-w.md +0 -6
  329. package/corpus/templates/crm/changelog/2026-07-21-crm-can-now-manage-keyword-and-smart-call-signal-trackers-fr.md +0 -6
  330. package/corpus/templates/crm/changelog/2026-07-21-delegated-local-crm-automation.md +0 -6
  331. package/corpus/templates/crm/changelog/2026-07-21-native-sql.md +0 -6
  332. package/corpus/templates/crm/changelog/2026-07-21-pipeline-dashboard.md +0 -6
  333. package/corpus/templates/crm/changelog/2026-07-21-pipeline-dashboards-now-install-correctly-from-the-crm-actio.md +0 -6
  334. package/corpus/templates/crm/changelog/2026-07-21-salesforce-connection.md +0 -6
  335. package/corpus/templates/crm/changelog/2026-07-22-crm-field-validation-now-explains-which-fields-cannot-be-edi.md +0 -6
  336. package/corpus/templates/crm/changelog/2026-07-22-fixed-hubspot-contact-syncs-missing-records-updated-after-th.md +0 -6
  337. package/corpus/templates/crm/changelog/2026-07-22-fixed-pipeline-dashboard-panels-so-their-opportunity-data-lo.md +0 -6
  338. package/corpus/templates/crm/changelog/2026-07-22-full-page-chat-keeps-the-active-conversation-when-moving-to-.md +0 -6
  339. package/corpus/templates/crm/components.json +0 -20
  340. package/corpus/templates/crm/docs/architecture/crm-contract.md +0 -180
  341. package/corpus/templates/crm/learnings.defaults.md +0 -5
  342. package/corpus/templates/crm/package.json +0 -59
  343. package/corpus/templates/crm/public/favicon.svg +0 -1
  344. package/corpus/templates/crm/public/icon-180.svg +0 -1
  345. package/corpus/templates/crm/public/manifest.json +0 -11
  346. package/corpus/templates/crm/react-router.config.ts +0 -7
  347. package/corpus/templates/crm/server/crm/adapter.ts +0 -32
  348. package/corpus/templates/crm/server/crm/crm-field-firewall.ts +0 -64
  349. package/corpus/templates/crm/server/crm/crm-mirror.ts +0 -711
  350. package/corpus/templates/crm/server/crm/hubspot-adapter.ts +0 -1146
  351. package/corpus/templates/crm/server/crm/native-adapter.ts +0 -1715
  352. package/corpus/templates/crm/server/crm/read-through.ts +0 -207
  353. package/corpus/templates/crm/server/crm/salesforce-adapter.ts +0 -1255
  354. package/corpus/templates/crm/server/db/crm-store.ts +0 -1170
  355. package/corpus/templates/crm/server/db/index.ts +0 -169
  356. package/corpus/templates/crm/server/db/schema.ts +0 -441
  357. package/corpus/templates/crm/server/lib/intelligence/default-detectors.ts +0 -65
  358. package/corpus/templates/crm/server/lib/intelligence/evidence.ts +0 -105
  359. package/corpus/templates/crm/server/lib/intelligence/keyword-detector.ts +0 -100
  360. package/corpus/templates/crm/server/lib/intelligence/smart-detector.ts +0 -144
  361. package/corpus/templates/crm/server/lib/intelligence/summary.ts +0 -141
  362. package/corpus/templates/crm/server/lib/provider-api.ts +0 -64
  363. package/corpus/templates/crm/server/middleware/auth.ts +0 -4
  364. package/corpus/templates/crm/server/plugins/agent-chat.ts +0 -68
  365. package/corpus/templates/crm/server/plugins/auth.ts +0 -14
  366. package/corpus/templates/crm/server/plugins/core-routes.ts +0 -17
  367. package/corpus/templates/crm/server/plugins/db.ts +0 -454
  368. package/corpus/templates/crm/server/routes/[...page].get.ts +0 -5
  369. package/corpus/templates/crm/shared/crm-automation-recipes.ts +0 -132
  370. package/corpus/templates/crm/shared/crm-contract.ts +0 -254
  371. package/corpus/templates/crm/shared/crm-sales-config.ts +0 -63
  372. package/corpus/templates/crm/ssr-entry.ts +0 -11
  373. package/corpus/templates/crm/tsconfig.json +0 -24
  374. package/corpus/templates/crm/vite.config.ts +0 -15
  375. package/corpus/templates/crm/vitest.config.ts +0 -5
@@ -7,16 +7,14 @@
7
7
  * existing native transcript, then only falls back to cloud transcription when
8
8
  * no native transcript exists.
9
9
  *
10
- * Cloud fallback provider selection:
11
- * 1. Builder.io transcription (Gemini 3.1 Flash-Lite behind the Builder
12
- * proxy) when Builder is connected; if that model is unavailable in the
13
- * deployment region, retry the Builder gateway's default model.
14
- * 2. `GROQ_API_KEY` → Groq's fast speech-to-text fallback.
15
- * 3. Neither → keep any native transcript or fail with a clear reason.
10
+ * Cloud fallback: Builder.io transcription (Gemini 3.1 Flash-Lite behind the
11
+ * Builder proxy) when Builder is connected; if that model is unavailable in
12
+ * the deployment region, retry the Builder gateway's default model.
16
13
  *
17
- * Clips intentionally does not route recording transcription to OpenAI.
18
- * Native macOS/Web Speech output is the primary source; Gemini is reserved
19
- * for cleanup/title generation after native text exists.
14
+ * Clips intentionally does not route recording transcription to third-party
15
+ * BYOK speech providers. Native macOS/Web Speech output is the primary source;
16
+ * Builder only transcribes the original recording when native text is
17
+ * unavailable.
20
18
  *
21
19
  * Native transcription: the browser's Web Speech API and desktop macOS Speech
22
20
  * run during recording and save an instant transcript via
@@ -38,14 +36,9 @@ import {
38
36
  readAppState,
39
37
  writeAppState,
40
38
  } from "@agent-native/core/application-state";
41
- import { resolveCredential } from "@agent-native/core/credentials";
42
39
  import { ssrfSafeFetch } from "@agent-native/core/extensions/url-safety";
43
- import { readAppSecret } from "@agent-native/core/secrets";
44
40
  import { resolveHasBuilderPrivateKey } from "@agent-native/core/server";
45
- import {
46
- getRequestUserEmail,
47
- getCredentialContext,
48
- } from "@agent-native/core/server/request-context";
41
+ import { getRequestUserEmail } from "@agent-native/core/server/request-context";
49
42
  import { getSetting, getUserSetting } from "@agent-native/core/settings";
50
43
  import { assertAccess } from "@agent-native/core/sharing";
51
44
  import { transcribeWithBuilder } from "@agent-native/core/transcription/builder";
@@ -93,25 +86,6 @@ import { isAutoTitleReplaceable } from "./lib/title-source.js";
93
86
  import regenerateSummary from "./regenerate-summary.js";
94
87
  import regenerateTitle from "./regenerate-title.js";
95
88
 
96
- interface SpeechToTextSegment {
97
- start: number; // seconds
98
- end: number; // seconds
99
- text: string;
100
- }
101
-
102
- interface SpeechToTextResponse {
103
- text: string;
104
- language?: string;
105
- segments?: SpeechToTextSegment[];
106
- }
107
-
108
- type TranscriptionProvider = {
109
- name: "groq";
110
- endpoint: string;
111
- model: string;
112
- apiKey: string;
113
- };
114
-
115
89
  type RecordingMediaRow = {
116
90
  videoUrl: string | null;
117
91
  videoFormat?: "webm" | "mp4" | null;
@@ -122,8 +96,6 @@ type RecordingMediaRow = {
122
96
  durationMs?: number | null;
123
97
  };
124
98
 
125
- const GROQ_ENDPOINT = "https://api.groq.com/openai/v1/audio/transcriptions";
126
- const GROQ_MODEL = "whisper-large-v3-turbo";
127
99
  const BUILDER_GEMINI_TRANSCRIPTION_MODEL = "gemini-3-1-flash-lite";
128
100
  const SPEECH_ONLY_TRANSCRIPTION_INSTRUCTIONS =
129
101
  "Auto-detect the spoken language from the audio. Transcribe only words spoken in the audio, in the same language they were spoken. Do not translate. Do not infer language from screen text, filenames, account settings, browser locale, or these instructions. Do not describe screen activity, UI changes, silence, music, or non-speech sounds. Return an empty transcript when there are no spoken words.";
@@ -477,37 +449,6 @@ export function isSafeTranscriptCleanupReplacement(
477
449
  return retention >= 0.6 && retention <= 1.25;
478
450
  }
479
451
 
480
- async function failEmptyProviderTranscript({
481
- db,
482
- recordingId,
483
- ownerEmail,
484
- providerName,
485
- now,
486
- }: {
487
- db: ReturnType<typeof getDb>;
488
- recordingId: string;
489
- ownerEmail: string;
490
- providerName: string;
491
- now: string;
492
- }) {
493
- const reason = `No speech was detected by ${providerName} transcription. Check microphone and speech permissions, then retry transcription.`;
494
- await upsertTranscriptRow(db, {
495
- recordingId,
496
- ownerEmail,
497
- status: "failed",
498
- failureReason: reason,
499
- segmentsJson: "[]",
500
- fullText: "",
501
- now,
502
- });
503
- await writeAppState("refresh-signal", { ts: Date.now() });
504
- return {
505
- recordingId,
506
- status: "failed" as const,
507
- failureReason: reason,
508
- };
509
- }
510
-
511
452
  function resolveLoomTranscriptShareUrl(
512
453
  recording: RecordingMediaRow,
513
454
  ): string | null {
@@ -1037,54 +978,9 @@ async function preserveReadyTranscriptIfAvailable({
1037
978
  return null;
1038
979
  }
1039
980
 
1040
- /**
1041
- * Resolve a secret from (in order):
1042
- * 1. Per-user secret store (sidebar settings UI, encrypted at rest)
1043
- * 2. `resolveCredential` (per-user / per-org SQL settings rows)
1044
- */
1045
- async function resolveKey(
1046
- key: string,
1047
- userEmail: string | null,
1048
- ): Promise<string | undefined> {
1049
- if (userEmail) {
1050
- const userSecret = await readAppSecret({
1051
- key,
1052
- scope: "user",
1053
- scopeId: userEmail,
1054
- }).catch(() => null);
1055
- if (userSecret?.value) return userSecret.value;
1056
- }
1057
- const credCtx = getCredentialContext();
1058
- if (!credCtx) {
1059
- // No active request context — refuse to fall back to a global lookup
1060
- // because there is no user/org to scope the credential read to.
1061
- return undefined;
1062
- }
1063
- const fromCreds = await resolveCredential(key, credCtx);
1064
- return fromCreds ?? undefined;
1065
- }
1066
-
1067
- async function pickProvider(
1068
- userEmail: string | null,
1069
- ): Promise<TranscriptionProvider | null> {
1070
- // Prefer Groq when Builder/native are unavailable — it is the fast
1071
- // Whisper-compatible speech-to-text fallback. Clips no longer falls back
1072
- // to OpenAI for recording transcription.
1073
- const groqKey = await resolveKey("GROQ_API_KEY", userEmail);
1074
- if (groqKey) {
1075
- return {
1076
- name: "groq",
1077
- endpoint: GROQ_ENDPOINT,
1078
- model: GROQ_MODEL,
1079
- apiKey: groqKey,
1080
- };
1081
- }
1082
- return null;
1083
- }
1084
-
1085
981
  const requestTranscriptAction = defineAction({
1086
982
  description:
1087
- "Ensure a recording has a transcript, or explicitly regenerate it from the recording media. Preserves native Web Speech/macOS Speech transcripts unless regenerate is true, then uses Builder.io managed transcription or the configured Groq fallback.",
983
+ "Ensure a recording has a transcript, or explicitly regenerate it from the recording media. Preserves native Web Speech/macOS Speech transcripts unless regenerate is true, then uses Builder.io managed transcription as the cloud fallback.",
1088
984
  schema: z.object({
1089
985
  recordingId: z.string().describe("Recording ID"),
1090
986
  force: z
@@ -1154,7 +1050,6 @@ const requestTranscriptAction = defineAction({
1154
1050
  const ownerEmail = getCurrentOwnerEmail();
1155
1051
  const now = new Date().toISOString();
1156
1052
 
1157
- const userEmail = getRequestUserEmail() ?? ownerEmail;
1158
1053
  let builderError: string | null = null;
1159
1054
  let audioMediaPromise: Promise<AudioOnlyTranscriptionMedia> | null = null;
1160
1055
  let audioSignalPromise: Promise<void> | null = null;
@@ -1376,67 +1271,68 @@ const requestTranscriptAction = defineAction({
1376
1271
  }
1377
1272
 
1378
1273
  if (!fullText) {
1379
- return failEmptyProviderTranscript({
1380
- db,
1274
+ builderError = "Builder transcription returned no speech.";
1275
+ await writeTranscriptCleanupState(args.recordingId, {
1276
+ status: "builder-transcription-empty",
1277
+ provider: BUILDER_GEMINI_TRANSCRIPTION_MODEL,
1278
+ failureReason: builderError,
1279
+ });
1280
+ }
1281
+
1282
+ if (fullText) {
1283
+ await upsertTranscriptRow(db, {
1381
1284
  recordingId: args.recordingId,
1382
1285
  ownerEmail,
1383
- providerName: "Builder",
1286
+ status: "ready",
1287
+ failureReason: null,
1288
+ language: builderResult.language ?? "en",
1289
+ segmentsJson: JSON.stringify(normalizedTranscript.segments),
1290
+ fullText,
1384
1291
  now,
1385
1292
  });
1386
- }
1387
-
1388
- await upsertTranscriptRow(db, {
1389
- recordingId: args.recordingId,
1390
- ownerEmail,
1391
- status: "ready",
1392
- failureReason: null,
1393
- language: builderResult.language ?? "en",
1394
- segmentsJson: JSON.stringify(normalizedTranscript.segments),
1395
- fullText,
1396
- now,
1397
- });
1398
- await writeAppState("refresh-signal", { ts: Date.now() });
1399
- await queueBrainExport(args.recordingId);
1400
- await clearBuilderCreditsExhausted();
1401
-
1402
- // Re-read title fresh — `rec.title` was fetched before the 30+ s
1403
- // transcription and may be stale if the user renamed during that window.
1404
- const [freshRec] = await db
1405
- .select({
1406
- title: schema.recordings.title,
1407
- titleSource: schema.recordings.titleSource,
1408
- description: schema.recordings.description,
1409
- })
1410
- .from(schema.recordings)
1411
- .where(eq(schema.recordings.id, args.recordingId))
1412
- .limit(1);
1413
- if (freshRec) {
1414
- try {
1415
- await generateRecordingMetadata({
1416
- recordingId: args.recordingId,
1417
- title: freshRec.title,
1418
- titleSource: freshRec.titleSource,
1419
- description: freshRec.description,
1420
- transcriptText: fullText,
1421
- });
1422
- } catch (delegateErr) {
1423
- console.warn(
1424
- `[clips] automatic metadata generation failed for ${args.recordingId}:`,
1425
- (delegateErr as Error).message,
1426
- );
1293
+ await writeAppState("refresh-signal", { ts: Date.now() });
1294
+ await queueBrainExport(args.recordingId);
1295
+ await clearBuilderCreditsExhausted();
1296
+
1297
+ // Re-read title fresh — `rec.title` was fetched before the 30+ s
1298
+ // transcription and may be stale if the user renamed during that window.
1299
+ const [freshRec] = await db
1300
+ .select({
1301
+ title: schema.recordings.title,
1302
+ titleSource: schema.recordings.titleSource,
1303
+ description: schema.recordings.description,
1304
+ })
1305
+ .from(schema.recordings)
1306
+ .where(eq(schema.recordings.id, args.recordingId))
1307
+ .limit(1);
1308
+ if (freshRec) {
1309
+ try {
1310
+ await generateRecordingMetadata({
1311
+ recordingId: args.recordingId,
1312
+ title: freshRec.title,
1313
+ titleSource: freshRec.titleSource,
1314
+ description: freshRec.description,
1315
+ transcriptText: fullText,
1316
+ });
1317
+ } catch (delegateErr) {
1318
+ console.warn(
1319
+ `[clips] automatic metadata generation failed for ${args.recordingId}:`,
1320
+ (delegateErr as Error).message,
1321
+ );
1322
+ }
1427
1323
  }
1428
- }
1429
1324
 
1430
- const elapsedMs = Date.now() - startedAt;
1431
- console.log(
1432
- `Transcribed recording ${args.recordingId} via builder in ${elapsedMs}ms (${normalizedTranscript.segments.length} segments)`,
1433
- );
1434
- return {
1435
- recordingId: args.recordingId,
1436
- status: "ready" as const,
1437
- segments: normalizedTranscript.segments.length,
1438
- provider: "builder",
1439
- };
1325
+ const elapsedMs = Date.now() - startedAt;
1326
+ console.log(
1327
+ `Transcribed recording ${args.recordingId} via builder in ${elapsedMs}ms (${normalizedTranscript.segments.length} segments)`,
1328
+ );
1329
+ return {
1330
+ recordingId: args.recordingId,
1331
+ status: "ready" as const,
1332
+ segments: normalizedTranscript.segments.length,
1333
+ provider: "builder",
1334
+ };
1335
+ }
1440
1336
  } catch (err) {
1441
1337
  const reason = (err as Error).message;
1442
1338
  const details = serializeError(err);
@@ -1447,12 +1343,12 @@ const requestTranscriptAction = defineAction({
1447
1343
  });
1448
1344
  builderError = reason;
1449
1345
  console.warn(
1450
- `[clips] Builder credits exhausted for ${args.recordingId}; preserving native transcript if present and falling back to Groq if configured.`,
1346
+ `[clips] Builder credits exhausted for ${args.recordingId}; preserving native transcript if present.`,
1451
1347
  );
1452
1348
  } else {
1453
1349
  builderError = reason;
1454
1350
  console.warn(
1455
- `[clips] Builder transcription failed for ${args.recordingId}: ${summarizeError(err)}. Preserving native transcript if present and falling back to Groq if configured.`,
1351
+ `[clips] Builder transcription failed for ${args.recordingId}: ${summarizeError(err)}. Preserving native transcript if present.`,
1456
1352
  );
1457
1353
  if (verboseTranscriptErrors()) {
1458
1354
  console.warn(
@@ -1470,256 +1366,30 @@ const requestTranscriptAction = defineAction({
1470
1366
  }
1471
1367
  }
1472
1368
 
1473
- // ── Groq fallback ─────────────────────────────────────────────────
1474
- // Resolve the provider BEFORE overwriting the transcript row — if no
1475
- // key is configured but a native transcript already exists
1476
- // (from Web Speech API or macOS Speech during recording), preserve it instead of
1477
- // clobbering it with "pending" then "failed".
1478
- const provider = await pickProvider(userEmail);
1479
- if (!provider) {
1480
- const preserved = await preserveReadyTranscriptIfAvailable({
1481
- db,
1482
- recordingId: args.recordingId,
1483
- ownerEmail,
1484
- });
1485
- if (preserved) return preserved;
1486
-
1487
- const reason = builderError
1488
- ? "No native transcript was captured, and backup transcription could not finish. Retry transcription or check microphone and speech permissions."
1489
- : "No transcript was captured by native speech recognition, and no backup transcription provider is configured.";
1490
- await upsertTranscriptRow(db, {
1491
- recordingId: args.recordingId,
1492
- ownerEmail,
1493
- status: "failed",
1494
- failureReason: reason,
1495
- now,
1496
- });
1497
- await writeAppState("refresh-signal", { ts: Date.now() });
1498
- console.warn(`[clips] ${reason}`);
1499
- return {
1500
- recordingId: args.recordingId,
1501
- status: "failed" as const,
1502
- failureReason: reason,
1503
- };
1504
- }
1505
-
1506
- // Upsert a pending row so the UI can show "Transcribing…".
1507
- if (!regeneratingReadyTranscript) {
1508
- await upsertTranscriptRow(db, {
1509
- recordingId: args.recordingId,
1510
- ownerEmail,
1511
- status: "pending",
1512
- failureReason: null,
1513
- now,
1514
- });
1515
-
1516
- await writeAppState("refresh-signal", { ts: Date.now() });
1517
- }
1518
-
1519
- // Load the recording's media URL and prepare audio-only bytes. We never
1520
- // send video frames to a transcription provider; screen-only recordings
1521
- // without speech should become an empty/no-speech transcript instead of a
1522
- // visual narration.
1523
- const [rec] = await db
1524
- .select({
1525
- videoUrl: schema.recordings.videoUrl,
1526
- videoFormat: schema.recordings.videoFormat,
1527
- videoSizeBytes: schema.recordings.videoSizeBytes,
1528
- hasAudio: schema.recordings.hasAudio,
1529
- sourceAppName: schema.recordings.sourceAppName,
1530
- sourceWindowTitle: schema.recordings.sourceWindowTitle,
1531
- durationMs: schema.recordings.durationMs,
1532
- title: schema.recordings.title,
1533
- titleSource: schema.recordings.titleSource,
1534
- description: schema.recordings.description,
1535
- })
1536
- .from(schema.recordings)
1537
- .where(eq(schema.recordings.id, args.recordingId))
1538
- .limit(1);
1539
- if (!rec || !rec.videoUrl) {
1540
- const reason = "Recording has no videoUrl";
1541
- const preserved = await preserveReadyTranscriptIfAvailable({
1542
- db,
1543
- recordingId: args.recordingId,
1544
- ownerEmail,
1545
- });
1546
- if (preserved) return preserved;
1547
- await upsertTranscriptRow(db, {
1548
- recordingId: args.recordingId,
1549
- ownerEmail,
1550
- status: "failed",
1551
- failureReason: reason,
1552
- now,
1553
- });
1554
- await writeAppState("refresh-signal", { ts: Date.now() });
1555
- throw new Error(reason);
1556
- }
1557
- if (isLoomRecording(rec)) {
1558
- return importLoomTranscriptForRecording({
1559
- db,
1560
- recordingId: args.recordingId,
1561
- ownerEmail,
1562
- recording: rec,
1563
- now,
1564
- });
1565
- }
1566
-
1567
- let audioMedia: AudioOnlyTranscriptionMedia;
1568
- try {
1569
- audioMedia = await getAudioMedia(rec);
1570
- await ensureAudioHasSignal(audioMedia);
1571
- } catch (err) {
1572
- return failAudioOnlyPreparation({
1573
- db,
1574
- recordingId: args.recordingId,
1575
- ownerEmail,
1576
- err,
1577
- now,
1578
- currentRetryCount,
1579
- });
1580
- }
1581
-
1582
- // Post to the provider. Groq accepts the OpenAI-compatible form shape.
1583
- const form = new FormData();
1584
- form.append(
1585
- "file",
1586
- new Blob([audioMedia.audioBytes as BlobPart], {
1587
- type: audioMedia.mimeType,
1588
- }),
1589
- audioMedia.filename,
1590
- );
1591
- form.append("model", provider.model);
1592
- form.append("response_format", "verbose_json");
1593
- form.append("timestamp_granularities[]", "segment");
1594
- form.append("prompt", SPEECH_ONLY_TRANSCRIPTION_INSTRUCTIONS);
1595
-
1596
- const controller = new AbortController();
1597
- const timeout = setTimeout(() => controller.abort(), 45_000);
1598
- try {
1599
- const startedAt = Date.now();
1600
- const res = await fetch(provider.endpoint, {
1601
- method: "POST",
1602
- headers: { Authorization: `Bearer ${provider.apiKey}` },
1603
- body: form,
1604
- signal: controller.signal,
1605
- });
1606
- if (!res.ok) {
1607
- const text = await res.text().catch(() => "");
1608
- throw new Error(
1609
- res.status === 401
1610
- ? `${provider.name} rejected the API key. Update it in Settings → API Keys.`
1611
- : `${provider.name} transcription error ${res.status}: ${text.slice(0, 300)}`,
1612
- );
1613
- }
1614
- const data = (await res.json()) as SpeechToTextResponse;
1615
-
1616
- const segments = (data.segments ?? [])
1617
- .map((s) => ({
1618
- startMs: Math.max(0, Math.round(s.start * 1000)),
1619
- endMs: Math.max(0, Math.round(s.end * 1000)),
1620
- text: s.text.trim(),
1621
- }))
1622
- .filter((segment) => segment.text);
1623
- const normalizedTranscript = normalizeProviderTranscript(
1624
- data.text,
1625
- segments,
1626
- );
1627
- const fullText = normalizedTranscript.fullText;
1628
-
1629
- if (!regeneratingReadyTranscript) {
1630
- const preserved = await preserveReadyTranscriptIfAvailable({
1631
- db,
1632
- recordingId: args.recordingId,
1633
- ownerEmail,
1634
- allowLikelyLanguageMismatch: false,
1635
- });
1636
- if (preserved) return preserved;
1637
- }
1638
-
1639
- if (!fullText) {
1640
- return failEmptyProviderTranscript({
1641
- db,
1642
- recordingId: args.recordingId,
1643
- ownerEmail,
1644
- providerName: provider.name,
1645
- now,
1646
- });
1647
- }
1648
-
1649
- await upsertTranscriptRow(db, {
1650
- recordingId: args.recordingId,
1651
- ownerEmail,
1652
- status: "ready",
1653
- failureReason: null,
1654
- language: data.language ?? "en",
1655
- segmentsJson: JSON.stringify(normalizedTranscript.segments),
1656
- fullText,
1657
- now,
1658
- });
1659
-
1660
- await writeAppState("refresh-signal", { ts: Date.now() });
1661
- await queueBrainExport(args.recordingId);
1662
-
1663
- // Generate transcript-backed metadata without replacing a human title or
1664
- // description. The title action keeps any local heuristic replaceable
1665
- // while its agent refinement runs.
1666
- try {
1667
- await generateRecordingMetadata({
1668
- recordingId: args.recordingId,
1669
- title: rec.title,
1670
- titleSource: rec.titleSource,
1671
- description: rec.description,
1672
- transcriptText: fullText,
1673
- });
1674
- } catch (delegateErr) {
1675
- console.warn(
1676
- `[clips] automatic metadata generation failed for ${args.recordingId}:`,
1677
- (delegateErr as Error).message,
1678
- );
1679
- }
1369
+ const preserved = await preserveReadyTranscriptIfAvailable({
1370
+ db,
1371
+ recordingId: args.recordingId,
1372
+ ownerEmail,
1373
+ });
1374
+ if (preserved) return preserved;
1680
1375
 
1681
- const elapsedMs = Date.now() - startedAt;
1682
- console.log(
1683
- `Transcribed recording ${args.recordingId} via ${provider.name} (${provider.model}) in ${elapsedMs}ms (${normalizedTranscript.segments.length} segments)`,
1684
- );
1685
- return {
1686
- recordingId: args.recordingId,
1687
- status: "ready" as const,
1688
- segments: normalizedTranscript.segments.length,
1689
- provider: provider.name,
1690
- };
1691
- } catch (err) {
1692
- const reason =
1693
- (err as Error)?.name === "AbortError"
1694
- ? `${provider.name} transcription timed out after 45 seconds.`
1695
- : (err as Error).message;
1696
- const preserved = await preserveReadyTranscriptIfAvailable({
1697
- db,
1698
- recordingId: args.recordingId,
1699
- ownerEmail,
1700
- });
1701
- if (preserved) return preserved;
1702
- const transient = isTransientTranscriptionError(err);
1703
- const nextRetryCount = currentRetryCount + 1;
1704
- await upsertTranscriptRow(db, {
1705
- recordingId: args.recordingId,
1706
- ownerEmail,
1707
- status: "failed",
1708
- failureReason: reason,
1709
- now,
1710
- ...(transient ? { retryCount: nextRetryCount } : {}),
1711
- });
1712
- await writeAppState("refresh-signal", { ts: Date.now() });
1713
- if (transient) {
1714
- scheduleAutoTranscriptRetry({
1715
- recordingId: args.recordingId,
1716
- nextRetryCount,
1717
- });
1718
- }
1719
- throw err;
1720
- } finally {
1721
- clearTimeout(timeout);
1722
- }
1376
+ const reason = builderError
1377
+ ? "No native transcript was captured, and Builder transcription could not finish. Retry transcription or check Builder connection and recording audio."
1378
+ : "No transcript was captured by native speech recognition, and Builder transcription is not configured.";
1379
+ await upsertTranscriptRow(db, {
1380
+ recordingId: args.recordingId,
1381
+ ownerEmail,
1382
+ status: "failed",
1383
+ failureReason: reason,
1384
+ now,
1385
+ });
1386
+ await writeAppState("refresh-signal", { ts: Date.now() });
1387
+ console.warn(`[clips] ${reason}`);
1388
+ return {
1389
+ recordingId: args.recordingId,
1390
+ status: "failed" as const,
1391
+ failureReason: reason,
1392
+ };
1723
1393
  },
1724
1394
  });
1725
1395
 
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Called by the web client (Web Speech API) and desktop client (whispher).
5
5
  * Native transcripts are available instantly with no API-key requirement
6
- * and are the primary transcript source. Always replaces the stored transcript with `fullText`. If `segments` are
6
+ * and are the primary transcript source. A non-empty result replaces the stored transcript with `fullText`. If `segments` are
7
7
  * supplied (real timestamps, e.g. from the desktop Whisper engine) they're
8
8
  * stored verbatim; otherwise evenly-paced segments are synthesized from the
9
9
  * text. Live capture that OWNS the transcript (meeting flushes re-sending the
@@ -133,40 +133,23 @@ export default defineAction({
133
133
  reason: "Transcript already exists",
134
134
  };
135
135
  }
136
+ // An empty native result is only a diagnostic. Never create a terminal
137
+ // transcript row here: finalization owns creating the pending row that
138
+ // lets the Builder fallback run against the saved recording.
136
139
  if (current) {
137
- await db
138
- .update(schema.recordingTranscripts)
139
- .set({
140
- ownerEmail,
141
- fullText: "",
142
- segmentsJson: "[]",
143
- status: "failed",
144
- failureReason,
145
- updatedAt: now,
146
- })
147
- .where(eq(schema.recordingTranscripts.recordingId, args.recordingId));
148
- } else {
149
- await db.insert(schema.recordingTranscripts).values({
140
+ return {
150
141
  recordingId: args.recordingId,
151
- ownerEmail,
152
- language: "en",
153
- segmentsJson: "[]",
154
- fullText: "",
155
- status: "failed",
156
- failureReason,
157
- createdAt: now,
158
- updatedAt: now,
159
- });
142
+ status: "skipped" as const,
143
+ reason: "Transcript attempt already exists",
144
+ };
160
145
  }
161
- await writeAppState("refresh-signal", { ts: Date.now() });
162
146
  console.warn(
163
- `[clips] Native transcript failed for ${args.recordingId} via ${args.source ?? "web-speech"}: ${failureReason}`,
147
+ `[clips] Native transcript unavailable for ${args.recordingId} via ${args.source ?? "web-speech"}; finalization will queue Builder fallback: ${failureReason}`,
164
148
  );
165
149
  return {
166
150
  recordingId: args.recordingId,
167
- status: "failed" as const,
168
- provider: args.source ?? "web-speech",
169
- failureReason,
151
+ status: "skipped" as const,
152
+ reason: "Empty native transcript; waiting for recording finalization",
170
153
  };
171
154
  }
172
155