@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.
- package/corpus/README.md +2 -2
- package/corpus/core/CHANGELOG.md +21 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/a2a/client.ts +32 -10
- package/corpus/core/src/cli/index.ts +18 -10
- package/corpus/core/src/cli/multi-frontier-runs.ts +5 -3
- package/corpus/core/src/cli/templates-meta.ts +0 -12
- package/corpus/core/src/cli/workspace-dev.ts +1 -1
- package/corpus/core/src/client/AgentPanel.tsx +9 -1
- package/corpus/core/src/client/RunStuckBanner.tsx +39 -47
- package/corpus/core/src/client/chat/action-chat-ui-surface.tsx +32 -0
- package/corpus/core/src/client/chat/message-components.tsx +53 -8
- package/corpus/core/src/client/chat/tool-call-display.tsx +24 -9
- package/corpus/core/src/client/chat/widgets/InlineExtensionWidget.tsx +0 -1
- package/corpus/core/src/client/chat/widgets/builtin-tool-renderers.tsx +7 -3
- package/corpus/core/src/client/conversation/AgentConversation.tsx +12 -1
- package/corpus/core/src/client/error-capture.ts +2 -6
- package/corpus/core/src/client/resources/McpConnectionSuggestion.tsx +20 -14
- package/corpus/core/src/client/resources/ResourcesPanel.tsx +5 -1
- package/corpus/core/src/client/resources/mcp-integration-catalog.ts +7 -17
- package/corpus/core/src/client/resources/mcp-integration-logos.ts +2 -2
- package/corpus/core/src/client/route-chunk-recovery.ts +5 -3
- package/corpus/core/src/client/session-replay.ts +3 -3
- package/corpus/core/src/client/settings/SettingsPanel.tsx +15 -6
- package/corpus/core/src/client/transcription/use-live-transcription.ts +64 -45
- package/corpus/core/src/deploy/build.ts +2 -1
- package/corpus/core/src/guards/db-tool-scoping.ts +1 -1
- package/corpus/core/src/integrations/adapters/slack.ts +84 -5
- package/corpus/core/src/integrations/index.ts +5 -1
- package/corpus/core/src/localization/default-messages.ts +1 -1
- package/corpus/core/src/mcp-client/oauth-routes.ts +45 -12
- package/corpus/core/src/server/realtime-voice.ts +4 -1
- package/corpus/core/src/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +7 -5
- package/corpus/core/src/terminal/cli-registry.ts +1 -1
- package/corpus/core/src/vite/client.ts +166 -1
- package/corpus/templates/analytics/app/hooks/use-dashboard-views.ts +13 -1
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/ViewsMenu.tsx +10 -0
- package/corpus/templates/analytics/changelog/2026-07-22-show-a-useful-error-when-saving-a-dashboard-view-fails.md +6 -0
- package/corpus/templates/brain/app/routes/sources.tsx +181 -114
- package/corpus/templates/clips/AGENTS.md +8 -6
- package/corpus/templates/clips/actions/request-transcript.ts +90 -420
- package/corpus/templates/clips/actions/save-browser-transcript.ts +11 -28
- package/corpus/templates/clips/app/components/capture-install-options.tsx +5 -3
- package/corpus/templates/clips/app/components/player/transcript-panel.tsx +3 -136
- package/corpus/templates/clips/app/lib/capture-install-options.ts +15 -4
- package/corpus/templates/clips/app/routes/download.tsx +3 -2
- package/corpus/templates/clips/changelog/2026-07-22-fixed-an-intermittent-hydration-error-on-the-download-page.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-22-fixed-transcript-generation-when-native-speech-capture-retur.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-22-transcript-generation-now-retries-with-backup-speech-recogni.md +6 -0
- package/corpus/templates/clips/server/lib/public-agent-context.ts +2 -2
- package/corpus/templates/clips/server/register-secrets.ts +7 -6
- package/corpus/templates/content/app/components/editor/SlashCommandMenu.tsx +16 -84
- package/corpus/templates/content/app/components/editor/VisualEditor.tsx +13 -7
- package/corpus/templates/content/app/components/editor/extensions/ImageBlock.tsx +2 -2
- package/corpus/templates/content/app/i18n-data.ts +10 -0
- package/corpus/templates/design/server/lib/fig-file-decoder.ts +1 -0
- package/corpus/templates/plan/app/entry.client.tsx +3 -0
- package/corpus/templates/plan/changelog/2026-07-22-recover-stale-pages-after-a-deployment-refresh-automatically.md +6 -0
- package/corpus/templates/slides/app/hooks/use-agent-generating.ts +10 -3
- package/corpus/templates/slides/app/pages/Index.tsx +1 -0
- package/corpus/templates/slides/changelog/2026-07-22-new-deck-prompts-now-open-in-their-own-visible-chat-thread.md +6 -0
- package/corpus/toolkit/CHANGELOG.md +9 -0
- package/corpus/toolkit/package.json +1 -1
- package/corpus/toolkit/src/composer/PromptComposer.tsx +5 -1
- package/corpus/toolkit/src/composer/RealtimeVoiceMode.tsx +119 -31
- package/corpus/toolkit/src/composer/useRealtimeVoiceMode.tsx +30 -0
- package/corpus/toolkit/src/conformance/runner.tsx +153 -1
- package/corpus/toolkit/src/editor/SharedRichEditor.tsx +13 -2
- package/corpus/toolkit/src/editor/SlashCommandMenu.tsx +20 -0
- package/corpus/toolkit/src/editor/index.ts +2 -0
- package/corpus/toolkit/src/styles.css +52 -23
- package/dist/a2a/client.d.ts +3 -1
- package/dist/a2a/client.d.ts.map +1 -1
- package/dist/a2a/client.js +17 -12
- package/dist/a2a/client.js.map +1 -1
- package/dist/cli/index.js +18 -8
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/multi-frontier-runs.js +3 -3
- package/dist/cli/multi-frontier-runs.js.map +1 -1
- package/dist/cli/templates-meta.d.ts.map +1 -1
- package/dist/cli/templates-meta.js +0 -12
- package/dist/cli/templates-meta.js.map +1 -1
- package/dist/cli/workspace-dev.js +1 -1
- package/dist/cli/workspace-dev.js.map +1 -1
- package/dist/client/AgentPanel.d.ts.map +1 -1
- package/dist/client/AgentPanel.js +4 -1
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/RunStuckBanner.d.ts +3 -5
- package/dist/client/RunStuckBanner.d.ts.map +1 -1
- package/dist/client/RunStuckBanner.js +17 -18
- package/dist/client/RunStuckBanner.js.map +1 -1
- package/dist/client/chat/action-chat-ui-surface.d.ts +8 -0
- package/dist/client/chat/action-chat-ui-surface.d.ts.map +1 -0
- package/dist/client/chat/action-chat-ui-surface.js +10 -0
- package/dist/client/chat/action-chat-ui-surface.js.map +1 -0
- package/dist/client/chat/message-components.d.ts +3 -0
- package/dist/client/chat/message-components.d.ts.map +1 -1
- package/dist/client/chat/message-components.js +40 -7
- package/dist/client/chat/message-components.js.map +1 -1
- package/dist/client/chat/tool-call-display.d.ts +3 -1
- package/dist/client/chat/tool-call-display.d.ts.map +1 -1
- package/dist/client/chat/tool-call-display.js +13 -9
- package/dist/client/chat/tool-call-display.js.map +1 -1
- package/dist/client/chat/widgets/InlineExtensionWidget.d.ts.map +1 -1
- package/dist/client/chat/widgets/InlineExtensionWidget.js +1 -1
- package/dist/client/chat/widgets/InlineExtensionWidget.js.map +1 -1
- package/dist/client/chat/widgets/builtin-tool-renderers.d.ts.map +1 -1
- package/dist/client/chat/widgets/builtin-tool-renderers.js +5 -3
- package/dist/client/chat/widgets/builtin-tool-renderers.js.map +1 -1
- package/dist/client/conversation/AgentConversation.d.ts.map +1 -1
- package/dist/client/conversation/AgentConversation.js +3 -2
- package/dist/client/conversation/AgentConversation.js.map +1 -1
- package/dist/client/error-capture.d.ts.map +1 -1
- package/dist/client/error-capture.js +2 -2
- package/dist/client/error-capture.js.map +1 -1
- package/dist/client/resources/McpConnectionSuggestion.d.ts +1 -0
- package/dist/client/resources/McpConnectionSuggestion.d.ts.map +1 -1
- package/dist/client/resources/McpConnectionSuggestion.js +9 -8
- package/dist/client/resources/McpConnectionSuggestion.js.map +1 -1
- package/dist/client/resources/ResourcesPanel.d.ts.map +1 -1
- package/dist/client/resources/ResourcesPanel.js +3 -1
- package/dist/client/resources/ResourcesPanel.js.map +1 -1
- package/dist/client/resources/mcp-integration-catalog.d.ts +1 -0
- package/dist/client/resources/mcp-integration-catalog.d.ts.map +1 -1
- package/dist/client/resources/mcp-integration-catalog.js +4 -17
- package/dist/client/resources/mcp-integration-catalog.js.map +1 -1
- package/dist/client/resources/mcp-integration-logos.js +2 -2
- package/dist/client/resources/mcp-integration-logos.js.map +1 -1
- package/dist/client/route-chunk-recovery.d.ts.map +1 -1
- package/dist/client/route-chunk-recovery.js +5 -3
- package/dist/client/route-chunk-recovery.js.map +1 -1
- package/dist/client/session-replay.js +3 -3
- package/dist/client/session-replay.js.map +1 -1
- package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
- package/dist/client/settings/SettingsPanel.js +2 -2
- package/dist/client/settings/SettingsPanel.js.map +1 -1
- package/dist/client/transcription/use-live-transcription.d.ts +3 -3
- package/dist/client/transcription/use-live-transcription.d.ts.map +1 -1
- package/dist/client/transcription/use-live-transcription.js +22 -11
- package/dist/client/transcription/use-live-transcription.js.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/deploy/build.d.ts.map +1 -1
- package/dist/deploy/build.js +1 -1
- package/dist/deploy/build.js.map +1 -1
- package/dist/guards/db-tool-scoping.js +1 -1
- package/dist/guards/db-tool-scoping.js.map +1 -1
- package/dist/integrations/adapters/slack.d.ts +1 -0
- package/dist/integrations/adapters/slack.d.ts.map +1 -1
- package/dist/integrations/adapters/slack.js +61 -5
- package/dist/integrations/adapters/slack.js.map +1 -1
- package/dist/integrations/index.d.ts +1 -1
- package/dist/integrations/index.d.ts.map +1 -1
- package/dist/integrations/index.js +1 -1
- package/dist/integrations/index.js.map +1 -1
- package/dist/localization/default-messages.js +1 -1
- package/dist/localization/default-messages.js.map +1 -1
- package/dist/mcp-client/oauth-routes.d.ts +3 -0
- package/dist/mcp-client/oauth-routes.d.ts.map +1 -1
- package/dist/mcp-client/oauth-routes.js +41 -12
- package/dist/mcp-client/oauth-routes.js.map +1 -1
- package/dist/notifications/routes.d.ts +2 -2
- package/dist/observability/routes.d.ts +3 -3
- package/dist/provider-api/actions/custom-provider-registration.d.ts +8 -8
- package/dist/provider-api/actions/provider-api.d.ts +11 -11
- package/dist/secrets/routes.d.ts +6 -6
- package/dist/server/realtime-voice.d.ts.map +1 -1
- package/dist/server/realtime-voice.js +1 -1
- package/dist/server/realtime-voice.js.map +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +7 -5
- package/dist/terminal/cli-registry.js +1 -1
- package/dist/terminal/cli-registry.js.map +1 -1
- package/dist/vite/client.d.ts +8 -1
- package/dist/vite/client.d.ts.map +1 -1
- package/dist/vite/client.js +113 -2
- package/dist/vite/client.js.map +1 -1
- package/package.json +2 -2
- package/src/a2a/client.ts +32 -10
- package/src/cli/index.ts +18 -10
- package/src/cli/multi-frontier-runs.ts +5 -3
- package/src/cli/templates-meta.ts +0 -12
- package/src/cli/workspace-dev.ts +1 -1
- package/src/client/AgentPanel.tsx +9 -1
- package/src/client/RunStuckBanner.tsx +39 -47
- package/src/client/chat/action-chat-ui-surface.tsx +32 -0
- package/src/client/chat/message-components.tsx +53 -8
- package/src/client/chat/tool-call-display.tsx +24 -9
- package/src/client/chat/widgets/InlineExtensionWidget.tsx +0 -1
- package/src/client/chat/widgets/builtin-tool-renderers.tsx +7 -3
- package/src/client/conversation/AgentConversation.tsx +12 -1
- package/src/client/error-capture.ts +2 -6
- package/src/client/resources/McpConnectionSuggestion.tsx +20 -14
- package/src/client/resources/ResourcesPanel.tsx +5 -1
- package/src/client/resources/mcp-integration-catalog.ts +7 -17
- package/src/client/resources/mcp-integration-logos.ts +2 -2
- package/src/client/route-chunk-recovery.ts +5 -3
- package/src/client/session-replay.ts +3 -3
- package/src/client/settings/SettingsPanel.tsx +15 -6
- package/src/client/transcription/use-live-transcription.ts +64 -45
- package/src/deploy/build.ts +2 -1
- package/src/guards/db-tool-scoping.ts +1 -1
- package/src/integrations/adapters/slack.ts +84 -5
- package/src/integrations/index.ts +5 -1
- package/src/localization/default-messages.ts +1 -1
- package/src/mcp-client/oauth-routes.ts +45 -12
- package/src/server/realtime-voice.ts +4 -1
- package/src/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +7 -5
- package/src/terminal/cli-registry.ts +1 -1
- package/src/vite/client.ts +166 -1
- package/corpus/templates/crm/.agents/skills/agent-native-docs/SKILL.md +0 -115
- package/corpus/templates/crm/.agents/skills/agent-native-toolkit/SKILL.md +0 -177
- package/corpus/templates/crm/.agents/skills/crm/SKILL.md +0 -128
- package/corpus/templates/crm/.agents/skills/customizing-agent-native/SKILL.md +0 -220
- package/corpus/templates/crm/.agents/skills/feature-flags/SKILL.md +0 -169
- package/corpus/templates/crm/.agents/skills/upgrade-agent-native/SKILL.md +0 -100
- package/corpus/templates/crm/.env.example +0 -5
- package/corpus/templates/crm/AGENTS.md +0 -120
- package/corpus/templates/crm/CHANGELOG.md +0 -1
- package/corpus/templates/crm/DEVELOPING.md +0 -66
- package/corpus/templates/crm/README.md +0 -106
- package/corpus/templates/crm/_gitignore +0 -12
- package/corpus/templates/crm/actions/_crm-action-utils.ts +0 -87
- package/corpus/templates/crm/actions/_crm-dashboard.ts +0 -69
- package/corpus/templates/crm/actions/_crm-data-program-actions.ts +0 -15
- package/corpus/templates/crm/actions/_crm-signal-utils.ts +0 -79
- package/corpus/templates/crm/actions/apply-crm-proposals.ts +0 -203
- package/corpus/templates/crm/actions/attach-call-evidence.ts +0 -170
- package/corpus/templates/crm/actions/configure-crm-connection.ts +0 -126
- package/corpus/templates/crm/actions/configure-native-crm.ts +0 -44
- package/corpus/templates/crm/actions/create-crm-record.ts +0 -148
- package/corpus/templates/crm/actions/create-crm-signal-tracker.ts +0 -58
- package/corpus/templates/crm/actions/delete-staged-dataset.ts +0 -14
- package/corpus/templates/crm/actions/get-crm-automation-recipe.ts +0 -36
- package/corpus/templates/crm/actions/get-crm-dashboard-panel.ts +0 -35
- package/corpus/templates/crm/actions/get-crm-dashboard.ts +0 -14
- package/corpus/templates/crm/actions/get-crm-overview.ts +0 -13
- package/corpus/templates/crm/actions/get-crm-pipeline-data.ts +0 -61
- package/corpus/templates/crm/actions/get-crm-record.ts +0 -75
- package/corpus/templates/crm/actions/install-crm-pipeline-dashboard.ts +0 -81
- package/corpus/templates/crm/actions/list-crm-dashboard-revisions.ts +0 -15
- package/corpus/templates/crm/actions/list-crm-dashboards.ts +0 -15
- package/corpus/templates/crm/actions/list-crm-proposals.ts +0 -31
- package/corpus/templates/crm/actions/list-crm-records.ts +0 -79
- package/corpus/templates/crm/actions/list-crm-saved-views.ts +0 -15
- package/corpus/templates/crm/actions/list-crm-signal-hits.ts +0 -43
- package/corpus/templates/crm/actions/list-crm-signal-trackers.ts +0 -32
- package/corpus/templates/crm/actions/list-crm-tasks.ts +0 -22
- package/corpus/templates/crm/actions/list-staged-datasets.ts +0 -14
- package/corpus/templates/crm/actions/list-workspace-connections.ts +0 -47
- package/corpus/templates/crm/actions/manage-crm-signal-tracker.ts +0 -87
- package/corpus/templates/crm/actions/manage-crm-task.ts +0 -132
- package/corpus/templates/crm/actions/navigate.ts +0 -63
- package/corpus/templates/crm/actions/provider-api-catalog.ts +0 -22
- package/corpus/templates/crm/actions/provider-api-docs.ts +0 -54
- package/corpus/templates/crm/actions/provider-api-request.ts +0 -92
- package/corpus/templates/crm/actions/query-staged-dataset.ts +0 -44
- package/corpus/templates/crm/actions/record-crm-call-insight.ts +0 -160
- package/corpus/templates/crm/actions/record-crm-smart-signal.ts +0 -150
- package/corpus/templates/crm/actions/restore-crm-dashboard-revision.ts +0 -23
- package/corpus/templates/crm/actions/review-crm-signal.ts +0 -31
- package/corpus/templates/crm/actions/run-crm-saved-view-program.ts +0 -103
- package/corpus/templates/crm/actions/run-crm-signal-trackers.ts +0 -275
- package/corpus/templates/crm/actions/run.ts +0 -7
- package/corpus/templates/crm/actions/save-crm-dashboard.ts +0 -51
- package/corpus/templates/crm/actions/save-crm-saved-view.ts +0 -119
- package/corpus/templates/crm/actions/sync-crm.ts +0 -146
- package/corpus/templates/crm/actions/update-crm-record.ts +0 -510
- package/corpus/templates/crm/actions/view-screen.ts +0 -218
- package/corpus/templates/crm/agent-native.app-skill.json +0 -58
- package/corpus/templates/crm/app/components/crm/CreateCrmRecordDialog.tsx +0 -158
- package/corpus/templates/crm/app/components/crm/CrmDashboardPanel.tsx +0 -78
- package/corpus/templates/crm/app/components/crm/CrmSignalsPanel.tsx +0 -226
- package/corpus/templates/crm/app/components/crm/IntelligenceSettings.tsx +0 -414
- package/corpus/templates/crm/app/components/crm/RecordActions.tsx +0 -510
- package/corpus/templates/crm/app/components/crm/RecordGrid.tsx +0 -134
- package/corpus/templates/crm/app/components/crm/RecordWorkspace.tsx +0 -362
- package/corpus/templates/crm/app/components/crm/SavedViewDataProgram.tsx +0 -93
- package/corpus/templates/crm/app/components/crm/Surface.tsx +0 -97
- package/corpus/templates/crm/app/components/crm/WorkOverview.tsx +0 -180
- package/corpus/templates/crm/app/components/layout/CrmLayout.tsx +0 -112
- package/corpus/templates/crm/app/components/layout/CrmSidebar.tsx +0 -84
- package/corpus/templates/crm/app/components/ui/alert-dialog.tsx +0 -1
- package/corpus/templates/crm/app/components/ui/badge.tsx +0 -1
- package/corpus/templates/crm/app/components/ui/button.tsx +0 -1
- package/corpus/templates/crm/app/components/ui/card.tsx +0 -1
- package/corpus/templates/crm/app/components/ui/dialog.tsx +0 -1
- package/corpus/templates/crm/app/components/ui/input.tsx +0 -1
- package/corpus/templates/crm/app/components/ui/label.tsx +0 -1
- package/corpus/templates/crm/app/components/ui/select.tsx +0 -1
- package/corpus/templates/crm/app/components/ui/sheet.tsx +0 -1
- package/corpus/templates/crm/app/components/ui/skeleton.tsx +0 -1
- package/corpus/templates/crm/app/components/ui/switch.tsx +0 -1
- package/corpus/templates/crm/app/components/ui/table.tsx +0 -1
- package/corpus/templates/crm/app/components/ui/tabs.tsx +0 -1
- package/corpus/templates/crm/app/components/ui/textarea.tsx +0 -1
- package/corpus/templates/crm/app/components/ui/toolkit-provider.tsx +0 -17
- package/corpus/templates/crm/app/design-system.ts +0 -3
- package/corpus/templates/crm/app/entry.client.tsx +0 -16
- package/corpus/templates/crm/app/entry.server.tsx +0 -10
- package/corpus/templates/crm/app/global.css +0 -167
- package/corpus/templates/crm/app/hooks/use-navigation-state.ts +0 -60
- package/corpus/templates/crm/app/i18n/en-US.ts +0 -89
- package/corpus/templates/crm/app/i18n/index.ts +0 -34
- package/corpus/templates/crm/app/lib/dashboard.ts +0 -13
- package/corpus/templates/crm/app/lib/navigation.ts +0 -54
- package/corpus/templates/crm/app/lib/tab-id.ts +0 -1
- package/corpus/templates/crm/app/lib/types.ts +0 -172
- package/corpus/templates/crm/app/lib/utils.ts +0 -1
- package/corpus/templates/crm/app/root.tsx +0 -91
- package/corpus/templates/crm/app/routes/_index.tsx +0 -26
- package/corpus/templates/crm/app/routes/accounts.tsx +0 -41
- package/corpus/templates/crm/app/routes/agent.tsx +0 -5
- package/corpus/templates/crm/app/routes/ask.tsx +0 -53
- package/corpus/templates/crm/app/routes/dashboard.tsx +0 -118
- package/corpus/templates/crm/app/routes/opportunities.tsx +0 -41
- package/corpus/templates/crm/app/routes/people.tsx +0 -41
- package/corpus/templates/crm/app/routes/proposals.tsx +0 -266
- package/corpus/templates/crm/app/routes/records.$recordId.tsx +0 -35
- package/corpus/templates/crm/app/routes/settings.tsx +0 -63
- package/corpus/templates/crm/app/routes/setup.tsx +0 -308
- package/corpus/templates/crm/app/routes/tasks.tsx +0 -252
- package/corpus/templates/crm/app/routes/views.tsx +0 -333
- package/corpus/templates/crm/app/routes.ts +0 -17
- package/corpus/templates/crm/app/vite-env.d.ts +0 -6
- package/corpus/templates/crm/changelog/2026-07-21-agent-native-crm.md +0 -6
- package/corpus/templates/crm/changelog/2026-07-21-clips-call-evidence-review-recipe.md +0 -6
- package/corpus/templates/crm/changelog/2026-07-21-crm-can-now-find-and-review-evidence-grounded-call-signals-w.md +0 -6
- package/corpus/templates/crm/changelog/2026-07-21-crm-can-now-manage-keyword-and-smart-call-signal-trackers-fr.md +0 -6
- package/corpus/templates/crm/changelog/2026-07-21-delegated-local-crm-automation.md +0 -6
- package/corpus/templates/crm/changelog/2026-07-21-native-sql.md +0 -6
- package/corpus/templates/crm/changelog/2026-07-21-pipeline-dashboard.md +0 -6
- package/corpus/templates/crm/changelog/2026-07-21-pipeline-dashboards-now-install-correctly-from-the-crm-actio.md +0 -6
- package/corpus/templates/crm/changelog/2026-07-21-salesforce-connection.md +0 -6
- package/corpus/templates/crm/changelog/2026-07-22-crm-field-validation-now-explains-which-fields-cannot-be-edi.md +0 -6
- package/corpus/templates/crm/changelog/2026-07-22-fixed-hubspot-contact-syncs-missing-records-updated-after-th.md +0 -6
- package/corpus/templates/crm/changelog/2026-07-22-fixed-pipeline-dashboard-panels-so-their-opportunity-data-lo.md +0 -6
- package/corpus/templates/crm/changelog/2026-07-22-full-page-chat-keeps-the-active-conversation-when-moving-to-.md +0 -6
- package/corpus/templates/crm/components.json +0 -20
- package/corpus/templates/crm/docs/architecture/crm-contract.md +0 -180
- package/corpus/templates/crm/learnings.defaults.md +0 -5
- package/corpus/templates/crm/package.json +0 -59
- package/corpus/templates/crm/public/favicon.svg +0 -1
- package/corpus/templates/crm/public/icon-180.svg +0 -1
- package/corpus/templates/crm/public/manifest.json +0 -11
- package/corpus/templates/crm/react-router.config.ts +0 -7
- package/corpus/templates/crm/server/crm/adapter.ts +0 -32
- package/corpus/templates/crm/server/crm/crm-field-firewall.ts +0 -64
- package/corpus/templates/crm/server/crm/crm-mirror.ts +0 -711
- package/corpus/templates/crm/server/crm/hubspot-adapter.ts +0 -1146
- package/corpus/templates/crm/server/crm/native-adapter.ts +0 -1715
- package/corpus/templates/crm/server/crm/read-through.ts +0 -207
- package/corpus/templates/crm/server/crm/salesforce-adapter.ts +0 -1255
- package/corpus/templates/crm/server/db/crm-store.ts +0 -1170
- package/corpus/templates/crm/server/db/index.ts +0 -169
- package/corpus/templates/crm/server/db/schema.ts +0 -441
- package/corpus/templates/crm/server/lib/intelligence/default-detectors.ts +0 -65
- package/corpus/templates/crm/server/lib/intelligence/evidence.ts +0 -105
- package/corpus/templates/crm/server/lib/intelligence/keyword-detector.ts +0 -100
- package/corpus/templates/crm/server/lib/intelligence/smart-detector.ts +0 -144
- package/corpus/templates/crm/server/lib/intelligence/summary.ts +0 -141
- package/corpus/templates/crm/server/lib/provider-api.ts +0 -64
- package/corpus/templates/crm/server/middleware/auth.ts +0 -4
- package/corpus/templates/crm/server/plugins/agent-chat.ts +0 -68
- package/corpus/templates/crm/server/plugins/auth.ts +0 -14
- package/corpus/templates/crm/server/plugins/core-routes.ts +0 -17
- package/corpus/templates/crm/server/plugins/db.ts +0 -454
- package/corpus/templates/crm/server/routes/[...page].get.ts +0 -5
- package/corpus/templates/crm/shared/crm-automation-recipes.ts +0 -132
- package/corpus/templates/crm/shared/crm-contract.ts +0 -254
- package/corpus/templates/crm/shared/crm-sales-config.ts +0 -63
- package/corpus/templates/crm/ssr-entry.ts +0 -11
- package/corpus/templates/crm/tsconfig.json +0 -24
- package/corpus/templates/crm/vite.config.ts +0 -15
- 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
|
|
11
|
-
*
|
|
12
|
-
*
|
|
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
|
|
18
|
-
* Native macOS/Web Speech output is the primary source;
|
|
19
|
-
*
|
|
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
|
|
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
|
-
|
|
1380
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
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
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
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
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
152
|
-
|
|
153
|
-
|
|
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
|
|
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: "
|
|
168
|
-
|
|
169
|
-
failureReason,
|
|
151
|
+
status: "skipped" as const,
|
|
152
|
+
reason: "Empty native transcript; waiting for recording finalization",
|
|
170
153
|
};
|
|
171
154
|
}
|
|
172
155
|
|