@agent-native/core 0.141.7 → 0.144.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/agent-native.eject.json +9 -0
- package/corpus/README.md +1 -1
- package/corpus/templates/analytics/.agents/skills/provider-api/SKILL.md +19 -0
- package/corpus/templates/analytics/.agents/skills/turn-into-app/SKILL.md +296 -0
- package/corpus/templates/analytics/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
- package/corpus/templates/analytics/.agents/skills/turn-into-skill/SKILL.md +112 -0
- package/corpus/templates/analytics/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
- package/corpus/templates/analytics/AGENTS.md +35 -38
- package/corpus/templates/analytics/actions/ensure-native-v2-dashboards.ts +1 -1
- package/corpus/templates/analytics/actions/list-dashboard-usage-stats.ts +17 -22
- package/corpus/templates/analytics/actions/migrate-first-party-analytics-to-bigquery.ts +1 -1
- package/corpus/templates/analytics/actions/navigate.ts +4 -5
- package/corpus/templates/analytics/actions/provider-api-register.ts +50 -0
- package/corpus/templates/analytics/actions/query-agent-native-analytics.ts +44 -11
- package/corpus/templates/analytics/actions/test-custom-api-connection.ts +106 -0
- package/corpus/templates/analytics/app/components/CustomApiCard.tsx +430 -0
- package/corpus/templates/analytics/app/i18n-data.ts +680 -0
- package/corpus/templates/analytics/app/lib/native-mcp-exclusions.ts +6 -0
- package/corpus/templates/analytics/app/lib/sql-query.ts +33 -6
- package/corpus/templates/analytics/app/pages/DataSources.tsx +4 -0
- package/corpus/templates/analytics/changelog/2026-08-06-analytics-dashboards-load-reliably-during-background-data-sw.md +6 -0
- package/corpus/templates/analytics/changelog/2026-08-06-analytics-dashboards-remain-responsive-while-historical-data.md +6 -0
- package/corpus/templates/analytics/changelog/2026-08-06-analytics-query-results-now-render-as-a-table-with-csv-downl.md +6 -0
- package/corpus/templates/analytics/changelog/2026-08-06-connect-public-https-custom-apis-and-hand-tested-endpoints-t.md +6 -0
- package/corpus/templates/analytics/changelog/2026-08-06-durable-agent-worker-memory.md +6 -0
- package/corpus/templates/analytics/changelog/2026-08-06-live-dashboard-panels-use-the-shared-query-timeout.md +6 -0
- package/corpus/templates/analytics/changelog/2026-08-06-on-demand-billing-is-available-as-a-native-dashboard-with-ta.md +6 -0
- package/corpus/templates/analytics/changelog/2026-08-06-visible-dashboard-names-are-unique.md +6 -0
- package/corpus/templates/analytics/netlify.toml +7 -0
- package/corpus/templates/analytics/scripts/emit-netlify-dashboard-report-cron.ts +4 -0
- package/corpus/templates/analytics/server/db/index.ts +27 -0
- package/corpus/templates/analytics/server/db/schema.ts +10 -0
- package/corpus/templates/analytics/server/lib/dashboards-store.ts +227 -60
- package/corpus/templates/analytics/server/lib/first-party-analytics-backend.ts +156 -45
- package/corpus/templates/analytics/server/lib/native-v2-dashboards.ts +227 -1
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +5 -0
- package/corpus/templates/analytics/server/plugins/analytics-rollup-backfill-jobs.ts +1 -0
- package/corpus/templates/analytics/server/plugins/db.ts +52 -18
- package/corpus/templates/assets/.agents/skills/asset-generation/SKILL.md +28 -2
- package/corpus/templates/assets/.agents/skills/image-generation/SKILL.md +16 -1
- package/corpus/templates/assets/.agents/skills/turn-into-app/SKILL.md +296 -0
- package/corpus/templates/assets/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
- package/corpus/templates/assets/.agents/skills/turn-into-skill/SKILL.md +112 -0
- package/corpus/templates/assets/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
- package/corpus/templates/brain/.agents/skills/turn-into-app/SKILL.md +296 -0
- package/corpus/templates/brain/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
- package/corpus/templates/brain/.agents/skills/turn-into-skill/SKILL.md +112 -0
- package/corpus/templates/brain/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
- package/corpus/templates/brain/actions/list-connection-providers.ts +24 -3
- package/corpus/templates/brain/app/lib/dispatch-links.ts +44 -0
- package/corpus/templates/brain/app/routes/sources.tsx +15 -7
- package/corpus/templates/brain/changelog/2026-08-06-brain-provider-setup-links-open-dispatch-correctly-in-standa.md +6 -0
- package/corpus/templates/brain/server/plugins/agent-chat.ts +1 -0
- package/corpus/templates/calendar/.agents/skills/event-management/SKILL.md +26 -0
- package/corpus/templates/calendar/.agents/skills/turn-into-app/SKILL.md +296 -0
- package/corpus/templates/calendar/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
- package/corpus/templates/calendar/.agents/skills/turn-into-skill/SKILL.md +112 -0
- package/corpus/templates/calendar/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
- package/corpus/templates/calendar/AGENTS.md +11 -32
- package/corpus/templates/calendar/actions/update-event.ts +107 -1
- package/corpus/templates/calendar/app/components/calendar/EventCalendarSelect.tsx +83 -0
- package/corpus/templates/calendar/app/components/calendar/EventDetailPanel.tsx +57 -0
- package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +59 -73
- package/corpus/templates/calendar/app/hooks/use-events.ts +4 -1
- package/corpus/templates/calendar/app/i18n-data.ts +20 -0
- package/corpus/templates/calendar/changelog/2026-08-06-delegated-requests-continue-in-the-background.md +6 -0
- package/corpus/templates/calendar/changelog/2026-08-06-existing-events-can-move-between-connected-google-account-ca.md +6 -0
- package/corpus/templates/calendar/server/lib/google-calendar.ts +101 -0
- package/corpus/templates/calendar/server/plugins/agent-chat.ts +7 -1
- package/corpus/templates/chat/.agents/skills/turn-into-app/SKILL.md +296 -0
- package/corpus/templates/chat/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
- package/corpus/templates/chat/.agents/skills/turn-into-skill/SKILL.md +112 -0
- package/corpus/templates/chat/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
- package/corpus/templates/clips/.agents/skills/turn-into-app/SKILL.md +296 -0
- package/corpus/templates/clips/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
- package/corpus/templates/clips/.agents/skills/turn-into-skill/SKILL.md +112 -0
- package/corpus/templates/clips/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
- package/corpus/templates/clips/app/components/editor/editor-layout.tsx +9 -24
- package/corpus/templates/clips/app/components/editor/timeline-geometry.ts +15 -0
- package/corpus/templates/clips/app/components/editor/waveform.tsx +4 -17
- package/corpus/templates/clips/app/components/player/delete-recording-menu.tsx +14 -3
- package/corpus/templates/clips/app/components/player/player-controls.tsx +0 -3
- package/corpus/templates/clips/app/components/player/scrubber.tsx +2 -33
- package/corpus/templates/clips/app/components/player/video-player.tsx +58 -11
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +28 -38
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +21 -27
- package/corpus/templates/clips/changelog/2026-08-06-clips-recovers-from-stalled-recording-setup-without-requirin.md +6 -0
- package/corpus/templates/clips/changelog/2026-08-06-clips-starts-faster-after-a-deploy-or-idle-period-startup-no.md +6 -0
- package/corpus/templates/clips/changelog/2026-08-06-deleting-a-recording-returns-cleanly-to-the-library-and-fit-.md +6 -0
- package/corpus/templates/clips/changelog/2026-08-06-hide-clip-insights-from-viewers-without-analytics-access.md +6 -0
- package/corpus/templates/clips/changelog/2026-08-06-trimmed-clips-play-on-a-clean-timeline-without-cut-markers.md +6 -0
- package/corpus/templates/clips/desktop/src/app.tsx +48 -20
- package/corpus/templates/clips/desktop/src/lib/recorder.ts +275 -74
- package/corpus/templates/clips/desktop/src/lib/recording-start-guard.ts +84 -0
- package/corpus/templates/clips/desktop/src/overlays/toolbar.tsx +14 -4
- package/corpus/templates/clips/server/plugins/db.ts +214 -26
- package/corpus/templates/content/.agents/skills/turn-into-app/SKILL.md +296 -0
- package/corpus/templates/content/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
- package/corpus/templates/content/.agents/skills/turn-into-skill/SKILL.md +112 -0
- package/corpus/templates/content/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
- package/corpus/templates/crm/.agents/skills/turn-into-app/SKILL.md +296 -0
- package/corpus/templates/crm/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
- package/corpus/templates/crm/.agents/skills/turn-into-skill/SKILL.md +112 -0
- package/corpus/templates/crm/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
- package/corpus/templates/crm/changelog/2026-08-06-crm-starts-faster-after-a-deploy-or-idle-period-one-time-dat.md +6 -0
- package/corpus/templates/crm/server/plugins/db.ts +11 -8
- package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +36 -1
- package/corpus/templates/design/.agents/skills/turn-into-app/SKILL.md +296 -0
- package/corpus/templates/design/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
- package/corpus/templates/design/.agents/skills/turn-into-skill/SKILL.md +112 -0
- package/corpus/templates/design/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
- package/corpus/templates/design/actions/import-from-url.ts +1 -1
- package/corpus/templates/design/app/components/design/LayersPanel.tsx +27 -18
- package/corpus/templates/design/app/pages/Index.tsx +6 -2
- package/corpus/templates/design/changelog/2026-08-05-deleting-a-design-from-the-home-page-no-longer-leaves-the-ap.md +6 -0
- package/corpus/templates/design/changelog/2026-08-05-renaming-a-layer-now-focuses-its-name-field-automatically.md +6 -0
- package/corpus/templates/design/server/plugins/agent-chat.ts +2 -0
- package/corpus/templates/dispatch/.agents/skills/turn-into-app/SKILL.md +296 -0
- package/corpus/templates/dispatch/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
- package/corpus/templates/dispatch/.agents/skills/turn-into-skill/SKILL.md +112 -0
- package/corpus/templates/dispatch/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
- package/corpus/templates/dispatch/AGENTS.md +8 -1
- package/corpus/templates/dispatch/app/dispatch-extensions.tsx +1 -0
- package/corpus/templates/dispatch/app/hooks/use-navigation-state.ts +30 -15
- package/corpus/templates/dispatch/app/i18n-data.ts +131 -0
- package/corpus/templates/dispatch/app/routes/admin._index.tsx +4 -0
- package/corpus/templates/dispatch/app/routes/admin.agents.tsx +1 -0
- package/corpus/templates/dispatch/app/routes/admin.approvals.tsx +1 -0
- package/corpus/templates/dispatch/app/routes/admin.apps.$appId.tsx +1 -0
- package/corpus/templates/dispatch/app/routes/admin.apps.tsx +1 -0
- package/corpus/templates/dispatch/app/routes/admin.audit.tsx +1 -0
- package/corpus/templates/dispatch/app/routes/admin.automations.tsx +1 -0
- package/corpus/templates/dispatch/app/routes/admin.destinations.tsx +4 -0
- package/corpus/templates/dispatch/app/routes/admin.dreams.tsx +1 -0
- package/corpus/templates/dispatch/app/routes/admin.identities.tsx +1 -0
- package/corpus/templates/dispatch/app/routes/admin.integrations.tsx +4 -0
- package/corpus/templates/dispatch/app/routes/admin.messaging.tsx +1 -0
- package/corpus/templates/dispatch/app/routes/admin.metrics.tsx +1 -0
- package/corpus/templates/dispatch/app/routes/admin.new-app.tsx +1 -0
- package/corpus/templates/dispatch/app/routes/admin.operations.tsx +1 -0
- package/corpus/templates/dispatch/app/routes/admin.thread-debug.tsx +4 -0
- package/corpus/templates/dispatch/app/routes/admin.transactional-email.$appId.$id.tsx +4 -0
- package/corpus/templates/dispatch/app/routes/admin.transactional-email.tsx +4 -0
- package/corpus/templates/dispatch/app/routes/admin.tsx +1 -0
- package/corpus/templates/dispatch/app/routes/admin.vault.tsx +1 -0
- package/corpus/templates/dispatch/app/routes/admin.workspace.tsx +1 -0
- package/corpus/templates/dispatch/changelog/2026-08-06-dispatch-keeps-everyday-navigation-focused-while-management-.md +6 -0
- package/corpus/templates/dispatch/changelog/2026-08-06-ready-workspace-apps-stay-one-click-away-in-a-compact-dispat.md +6 -0
- package/corpus/templates/dispatch/changelog/2026-08-06-shared-vault-values-and-management-are-now-restricted-to-wor.md +6 -0
- package/corpus/templates/factory/.agents/skills/turn-into-app/SKILL.md +296 -0
- package/corpus/templates/factory/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
- package/corpus/templates/factory/.agents/skills/turn-into-skill/SKILL.md +112 -0
- package/corpus/templates/factory/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
- package/corpus/templates/forms/.agents/skills/turn-into-app/SKILL.md +296 -0
- package/corpus/templates/forms/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
- package/corpus/templates/forms/.agents/skills/turn-into-skill/SKILL.md +112 -0
- package/corpus/templates/forms/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
- package/corpus/templates/forms/changelog/2026-08-06-delegated-requests-continue-in-the-background.md +6 -0
- package/corpus/templates/forms/server/plugins/agent-chat.ts +4 -0
- package/corpus/templates/macros/.agents/skills/turn-into-app/SKILL.md +296 -0
- package/corpus/templates/macros/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
- package/corpus/templates/macros/.agents/skills/turn-into-skill/SKILL.md +112 -0
- package/corpus/templates/macros/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
- package/corpus/templates/mail/.agents/skills/turn-into-app/SKILL.md +296 -0
- package/corpus/templates/mail/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
- package/corpus/templates/mail/.agents/skills/turn-into-skill/SKILL.md +112 -0
- package/corpus/templates/mail/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
- package/corpus/templates/mail/changelog/2026-08-06-delegated-requests-continue-in-the-background.md +6 -0
- package/corpus/templates/mail/server/plugins/agent-chat.ts +6 -0
- package/corpus/templates/plan/.agents/skills/turn-into-app/SKILL.md +296 -0
- package/corpus/templates/plan/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
- package/corpus/templates/plan/.agents/skills/turn-into-skill/SKILL.md +112 -0
- package/corpus/templates/plan/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
- package/corpus/templates/slides/.agents/skills/create-deck/SKILL.md +48 -5
- package/corpus/templates/slides/.agents/skills/design-systems/SKILL.md +37 -1
- package/corpus/templates/slides/.agents/skills/image-generation-via-a2a/SKILL.md +6 -0
- package/corpus/templates/slides/.agents/skills/slide-editing/SKILL.md +15 -2
- package/corpus/templates/slides/.agents/skills/slide-images/SKILL.md +29 -24
- package/corpus/templates/slides/.agents/skills/turn-into-app/SKILL.md +296 -0
- package/corpus/templates/slides/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
- package/corpus/templates/slides/.agents/skills/turn-into-skill/SKILL.md +112 -0
- package/corpus/templates/slides/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
- package/corpus/templates/slides/AGENTS.md +17 -21
- package/corpus/templates/slides/_gitignore +1 -0
- package/corpus/templates/slides/actions/export-pptx.ts +470 -36
- package/corpus/templates/slides/actions/get-deck.ts +12 -0
- package/corpus/templates/slides/actions/import-file.ts +175 -139
- package/corpus/templates/slides/actions/import-google-slides-reference.ts +271 -12
- package/corpus/templates/slides/actions/import-pptx.ts +193 -68
- package/corpus/templates/slides/actions/patch-deck.ts +51 -0
- package/corpus/templates/slides/actions/update-slide.ts +29 -3
- package/corpus/templates/slides/app/components/deck/DeckCard.tsx +10 -13
- package/corpus/templates/slides/app/components/editor/AddSlidePopover.tsx +5 -1
- package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +25 -12
- package/corpus/templates/slides/app/components/editor/ExportMenu.tsx +112 -57
- package/corpus/templates/slides/app/components/editor/GoogleDriveConnectionCta.tsx +202 -0
- package/corpus/templates/slides/app/components/editor/GoogleSlidesReferenceImport.tsx +26 -14
- package/corpus/templates/slides/app/components/editor/PromptDialog.tsx +173 -0
- package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +182 -141
- package/corpus/templates/slides/app/components/editor/canvas-interactions/index.ts +1 -0
- package/corpus/templates/slides/app/components/editor/canvas-interactions/slides-canvas-adapter.ts +18 -0
- package/corpus/templates/slides/app/components/editor/slide-text-targets.ts +85 -0
- package/corpus/templates/slides/app/components/layout/Layout.tsx +3 -0
- package/corpus/templates/slides/app/components/onboarding/FirstDeckOnboardingFlow.tsx +15 -0
- package/corpus/templates/slides/app/components/presentation/PresentationView.tsx +27 -1
- package/corpus/templates/slides/app/i18n/en-US.ts +1 -0
- package/corpus/templates/slides/app/lib/create-deck-generation.ts +72 -11
- package/corpus/templates/slides/app/lib/deck-preview-frame.ts +15 -0
- package/corpus/templates/slides/app/lib/deck-share-links.ts +9 -0
- package/corpus/templates/slides/app/lib/export-pptx-client.ts +89 -0
- package/corpus/templates/slides/app/lib/import-uploaded-deck.ts +89 -0
- package/corpus/templates/slides/app/lib/slide-animation-elements.ts +3 -0
- package/corpus/templates/slides/app/pages/Index.tsx +172 -15
- package/corpus/templates/slides/changelog/2026-08-06-clicking-the-gray-canvas-around-a-slide-clears-its-selection.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-06-deck-thumbnails-keep-a-consistent-listing-frame-across-aspec.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-06-deep-linked-decks-open-directly-instead-of-being-interrupted.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-06-google-slides-exports-now-show-clear-import-steps-when-direc.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-06-import-pdf-google-slides-and-powerpoint-decks-directly-from-.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-06-imported-powerpoint-exports-keep-text-and-images-editable-an.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-06-presentation-prompts-continue-adding-rendered-slides-until-t.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-06-public-decks-now-share-read-only-presentation-links-by-defau.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-06-slides-offers-a-direct-connect-google-button-when-a-drive-co.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-06-slides-preserve-uploaded-pdf-layouts-accept-larger-reference.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-06-styled-words-stay-part-of-their-text-box-instead-of-becoming.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-06-uploaded-decks-keep-their-slides-and-visuals-when-you-improv.md +6 -0
- package/corpus/templates/slides/server/handlers/google-docs-auth.ts +7 -20
- package/corpus/templates/slides/server/handlers/import/html-converter.ts +247 -13
- package/corpus/templates/slides/server/handlers/import/pptx-assets.ts +67 -0
- package/corpus/templates/slides/server/handlers/import/pptx-parser.ts +2 -0
- package/corpus/templates/slides/server/handlers/uploads.ts +7 -3
- package/corpus/templates/slides/server/lib/chat-attachments.ts +7 -4
- package/corpus/templates/slides/server/lib/emails.ts +7 -2
- package/corpus/templates/slides/server/lib/google-docs-error.ts +50 -0
- package/corpus/templates/slides/server/lib/google-docs-oauth.ts +1 -0
- package/corpus/templates/slides/server/lib/import-asset-storage.ts +127 -0
- package/corpus/templates/slides/server/lib/source-import.ts +167 -0
- package/corpus/templates/slides/server/plugins/agent-chat.ts +12 -3
- package/corpus/templates/slides/server/routes/api/import-assets/[token].get.ts +70 -0
- package/corpus/templates/slides/shared/upload-types.ts +3 -0
- package/corpus/templates/tasks/.agents/skills/turn-into-app/SKILL.md +296 -0
- package/corpus/templates/tasks/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
- package/corpus/templates/tasks/.agents/skills/turn-into-skill/SKILL.md +112 -0
- package/corpus/templates/tasks/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
- package/dist/agent/production-agent.d.ts +17 -0
- package/dist/agent/production-agent.js +72 -1
- package/dist/cli/agent-plugin.d.ts +112 -0
- package/dist/cli/agent-plugin.js +1081 -0
- package/dist/cli/app-skill.js +39 -1
- package/dist/cli/code-agent-executor.d.ts +3 -0
- package/dist/cli/code-agent-executor.js +51 -13
- package/dist/cli/index.js +13 -0
- package/dist/cli/templates-meta.js +1 -1
- package/dist/client/AgentPanel.d.ts +3 -1
- package/dist/client/AgentPanel.js +9 -2
- package/dist/client/AssistantChat.d.ts +8 -0
- package/dist/client/AssistantChat.js +23 -8
- package/dist/client/NewWorkspaceAppFlow.js +1 -0
- package/dist/client/agent-chat-adapter.js +123 -4
- package/dist/client/assistant-ui-recovery.d.ts +2 -1
- package/dist/client/assistant-ui-recovery.js +7 -3
- package/dist/client/onboarding/FirstRunOnboarding.js +11 -4
- package/dist/client/org/TeamPage.js +1 -1
- package/dist/client/require-session.js +1 -1
- package/dist/client/resources/McpConnectionSuggestion.d.ts +1 -0
- package/dist/client/resources/McpConnectionSuggestion.js +27 -10
- package/dist/client/resources/McpIntegrationDialog.js +77 -45
- package/dist/client/resources/mcp-integration-catalog.d.ts +13 -1
- package/dist/client/resources/mcp-integration-catalog.js +255 -2
- package/dist/client/resources/mcp-integration-logos.js +18 -2
- package/dist/client/settings/AccountSettingsCard.js +2 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/db/client.d.ts +9 -0
- package/dist/db/client.js +51 -16
- package/dist/db/create-get-db.js +2 -2
- package/dist/db/ddl-guard.d.ts +11 -10
- package/dist/db/ddl-guard.js +40 -12
- package/dist/deploy/build.d.ts +2 -0
- package/dist/deploy/build.js +97 -4
- package/dist/deploy/workspace-deploy.js +0 -2
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/guards/no-env-credentials.js +9 -0
- package/dist/ingestion/index.d.ts +1 -1
- package/dist/ingestion/pptx.d.ts +68 -0
- package/dist/ingestion/pptx.js +660 -213
- package/dist/integrations/plugin.js +9 -6
- package/dist/jobs/scheduler.js +79 -23
- package/dist/localization/default-messages.d.ts +54 -0
- package/dist/localization/default-messages.js +57 -2
- package/dist/mcp/screen-memory-stdio.d.ts +7 -7
- package/dist/mcp-client/oauth-routes.d.ts +2 -0
- package/dist/mcp-client/oauth-routes.js +73 -6
- package/dist/observability/routes.d.ts +5 -5
- package/dist/onboarding/app-profile.js +11 -0
- package/dist/progress/routes.d.ts +1 -1
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/resources/store.js +15 -1
- package/dist/scripts/call-agent.js +34 -4
- package/dist/secrets/routes.d.ts +3 -3
- package/dist/server/agent-chat/action-filters-a2a.d.ts +1 -1
- package/dist/server/agent-chat/action-filters-a2a.js +36 -1
- package/dist/server/agent-chat-plugin.js +29 -8
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/auth.js +47 -11
- package/dist/server/better-auth-instance.js +5 -3
- package/dist/server/core-routes-plugin.d.ts +8 -1
- package/dist/server/core-routes-plugin.js +28 -3
- package/dist/server/credential-provider.d.ts +3 -3
- package/dist/server/credential-provider.js +90 -4
- package/dist/server/onboarding-html.d.ts +2 -0
- package/dist/server/onboarding-html.js +115 -29
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/server/self-dispatch.d.ts +5 -4
- package/dist/server/self-dispatch.js +25 -14
- package/dist/server/ssr-handler.d.ts +1 -1
- package/dist/server/ssr-handler.js +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/shared/cache-control.d.ts +26 -2
- package/dist/shared/cache-control.js +43 -2
- package/dist/shared/password-policy.d.ts +3 -0
- package/dist/shared/password-policy.js +3 -0
- package/dist/sharing/actions/set-resource-visibility.js +16 -4
- package/dist/sharing/registry.d.ts +14 -0
- package/dist/styles/agent-native.css +6 -0
- package/dist/templates/chat/.agents/skills/turn-into-app/SKILL.md +296 -0
- package/dist/templates/chat/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
- package/dist/templates/chat/.agents/skills/turn-into-skill/SKILL.md +112 -0
- package/dist/templates/chat/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
- package/dist/templates/default/.agents/skills/turn-into-app/SKILL.md +296 -0
- package/dist/templates/default/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
- package/dist/templates/default/.agents/skills/turn-into-skill/SKILL.md +112 -0
- package/dist/templates/default/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
- package/dist/templates/headless/.agents/skills/turn-into-app/SKILL.md +296 -0
- package/dist/templates/headless/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
- package/dist/templates/headless/.agents/skills/turn-into-skill/SKILL.md +112 -0
- package/dist/templates/headless/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
- package/dist/templates/workspace-core/.agents/skills/adding-workspace-apps/SKILL.md +6 -0
- package/dist/templates/workspace-core/.agents/skills/performance/SKILL.md +51 -0
- package/dist/templates/workspace-core/.agents/skills/turn-into-app/SKILL.md +296 -0
- package/dist/templates/workspace-core/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
- package/dist/templates/workspace-core/.agents/skills/turn-into-skill/SKILL.md +112 -0
- package/dist/templates/workspace-core/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
- package/dist/templates/workspace-root/AGENTS.md +7 -0
- package/dist/user-profile/actions/change-password.js +8 -3
- package/dist/user-profile/actions/set-password.js +5 -1
- package/dist/vite/action-types-plugin.js +59 -0
- package/docs/content/creating-templates.mdx +1 -1
- package/docs/content/getting-started-actions.mdx +38 -189
- package/docs/content/getting-started-database.mdx +80 -164
- package/docs/content/getting-started-pages.mdx +44 -155
- package/docs/content/getting-started.mdx +26 -106
- package/docs/content/skills-guide.mdx +13 -0
- package/package.json +6 -2
- package/src/templates/chat/.agents/skills/turn-into-app/SKILL.md +296 -0
- package/src/templates/chat/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
- package/src/templates/chat/.agents/skills/turn-into-skill/SKILL.md +112 -0
- package/src/templates/chat/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
- package/src/templates/default/.agents/skills/turn-into-app/SKILL.md +296 -0
- package/src/templates/default/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
- package/src/templates/default/.agents/skills/turn-into-skill/SKILL.md +112 -0
- package/src/templates/default/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
- package/src/templates/headless/.agents/skills/turn-into-app/SKILL.md +296 -0
- package/src/templates/headless/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
- package/src/templates/headless/.agents/skills/turn-into-skill/SKILL.md +112 -0
- package/src/templates/headless/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
- package/src/templates/workspace-core/.agents/skills/adding-workspace-apps/SKILL.md +6 -0
- package/src/templates/workspace-core/.agents/skills/performance/SKILL.md +51 -0
- package/src/templates/workspace-core/.agents/skills/turn-into-app/SKILL.md +296 -0
- package/src/templates/workspace-core/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
- package/src/templates/workspace-core/.agents/skills/turn-into-skill/SKILL.md +112 -0
- package/src/templates/workspace-core/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
- package/src/templates/workspace-root/AGENTS.md +7 -0
|
@@ -79,6 +79,12 @@ import {
|
|
|
79
79
|
type PauseTransitionQueue,
|
|
80
80
|
} from "./pause-transition";
|
|
81
81
|
import { reconcileProcessingBackup } from "./processing-backup-recovery";
|
|
82
|
+
import {
|
|
83
|
+
guardRecordingStart,
|
|
84
|
+
RECORDING_START_TIMEOUT_MS,
|
|
85
|
+
RecordingStartCancelledError,
|
|
86
|
+
RecordingStartTimeoutError,
|
|
87
|
+
} from "./recording-start-guard";
|
|
82
88
|
import { buildCaptureTitle, type CaptureTitleResult } from "./recording-title";
|
|
83
89
|
import { prepareRewindRecordingStart } from "./rewind-recording-start";
|
|
84
90
|
import { singleFlight } from "./single-flight";
|
|
@@ -151,6 +157,20 @@ const NO_SPEECH_TRANSCRIPT_FAILURE =
|
|
|
151
157
|
const TRANSCRIPTION_START_FAILURE =
|
|
152
158
|
"macOS Speech recognition could not start for this recording. Check Speech Recognition, System Audio, and Microphone permissions, then retry transcription.";
|
|
153
159
|
|
|
160
|
+
function throwIfRecordingStartAborted(signal?: AbortSignal): void {
|
|
161
|
+
if (signal?.aborted) throw new RecordingStartCancelledError();
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function stopMediaStream(stream: MediaStream | null | undefined): void {
|
|
165
|
+
stream?.getTracks().forEach((track) => {
|
|
166
|
+
try {
|
|
167
|
+
track.stop();
|
|
168
|
+
} catch {
|
|
169
|
+
// coercion-ok: stopping an already-ended capture track is best-effort cleanup.
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
|
|
154
174
|
function isMacPlatform(): boolean {
|
|
155
175
|
if (typeof navigator === "undefined") return false;
|
|
156
176
|
const platform =
|
|
@@ -212,6 +232,8 @@ export interface StartParams {
|
|
|
212
232
|
cameraOn: boolean;
|
|
213
233
|
/** Record + transcribe system/desktop audio. Default true. */
|
|
214
234
|
systemAudioOn?: boolean;
|
|
235
|
+
/** Cancels or bounds the pre-record capture setup. */
|
|
236
|
+
signal?: AbortSignal;
|
|
215
237
|
localRecordingMode?: LocalRecordingMode;
|
|
216
238
|
/**
|
|
217
239
|
* Pre-acquired camera stream owned by the popover's session effect. The
|
|
@@ -1507,6 +1529,7 @@ async function createServerRecording(
|
|
|
1507
1529
|
requestStreaming?: boolean;
|
|
1508
1530
|
streamingUploadClient?: StreamingUploadClient;
|
|
1509
1531
|
visibility?: "public" | "private";
|
|
1532
|
+
signal?: AbortSignal;
|
|
1510
1533
|
},
|
|
1511
1534
|
) {
|
|
1512
1535
|
const url = `${serverUrl.replace(/\/+$/, "")}/_agent-native/actions/create-recording`;
|
|
@@ -1526,6 +1549,7 @@ async function createServerRecording(
|
|
|
1526
1549
|
// requests without Allow-Credentials — and dev auth is bypassed, so
|
|
1527
1550
|
// cookies aren't needed.
|
|
1528
1551
|
credentials: "include",
|
|
1552
|
+
signal: options?.signal,
|
|
1529
1553
|
body: JSON.stringify({
|
|
1530
1554
|
hasCamera,
|
|
1531
1555
|
hasAudio,
|
|
@@ -2087,7 +2111,10 @@ async function selectRegionForRecording(): Promise<RegionCaptureRect> {
|
|
|
2087
2111
|
}
|
|
2088
2112
|
}
|
|
2089
2113
|
|
|
2090
|
-
async function prepareCountdownEventWaiter(
|
|
2114
|
+
async function prepareCountdownEventWaiter(
|
|
2115
|
+
timeoutMs = 4000,
|
|
2116
|
+
signal?: AbortSignal,
|
|
2117
|
+
): Promise<{
|
|
2091
2118
|
event: Promise<string>;
|
|
2092
2119
|
cleanup: () => void;
|
|
2093
2120
|
}> {
|
|
@@ -2117,6 +2144,7 @@ async function prepareCountdownEventWaiter(timeoutMs = 4000): Promise<{
|
|
|
2117
2144
|
|
|
2118
2145
|
const cleanup = () => {
|
|
2119
2146
|
window.removeEventListener("keydown", onKeyDown);
|
|
2147
|
+
signal?.removeEventListener("abort", onAbort);
|
|
2120
2148
|
if (timer) {
|
|
2121
2149
|
clearTimeout(timer);
|
|
2122
2150
|
timer = null;
|
|
@@ -2139,6 +2167,12 @@ async function prepareCountdownEventWaiter(timeoutMs = 4000): Promise<{
|
|
|
2139
2167
|
resolveEvent(cause);
|
|
2140
2168
|
}
|
|
2141
2169
|
};
|
|
2170
|
+
const onAbort = () => finish("cancel", "abort");
|
|
2171
|
+
if (signal?.aborted) {
|
|
2172
|
+
onAbort();
|
|
2173
|
+
} else {
|
|
2174
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
2175
|
+
}
|
|
2142
2176
|
|
|
2143
2177
|
// Await both registrations before the countdown can become visible. A fast
|
|
2144
2178
|
// Return previously closed the overlay before these asynchronous listeners
|
|
@@ -2190,14 +2224,19 @@ async function showRegionRecordBorder(region: RegionCaptureRect | null) {
|
|
|
2190
2224
|
});
|
|
2191
2225
|
}
|
|
2192
2226
|
|
|
2193
|
-
async function runRecordingCountdown(
|
|
2227
|
+
async function runRecordingCountdown(
|
|
2228
|
+
wantsScreen: boolean,
|
|
2229
|
+
signal?: AbortSignal,
|
|
2230
|
+
) {
|
|
2194
2231
|
// The recording-start chime is intentionally NOT played here. It fires from
|
|
2195
2232
|
// `audioCue.playBeforeCapture()` at the real capture-start (right before the
|
|
2196
2233
|
// recorder/native capture is kicked off) so the beep lines up with the moment
|
|
2197
2234
|
// recording actually begins — not one second early on the countdown's "1".
|
|
2198
2235
|
const countdown = await prepareCountdownEventWaiter(
|
|
2199
2236
|
COUNTDOWN_EVENT_TIMEOUT_MS,
|
|
2237
|
+
signal,
|
|
2200
2238
|
);
|
|
2239
|
+
throwIfRecordingStartAborted(signal);
|
|
2201
2240
|
await showRegionGuidesForRecording(wantsScreen);
|
|
2202
2241
|
let countdownGeneration: number;
|
|
2203
2242
|
try {
|
|
@@ -2504,6 +2543,7 @@ async function tryStartRewindFullscreenRecording(
|
|
|
2504
2543
|
includeSystemAudio,
|
|
2505
2544
|
{ voiceProcessing: false },
|
|
2506
2545
|
).catch((err) => {
|
|
2546
|
+
if (params.signal?.aborted) throw err;
|
|
2507
2547
|
console.warn("[clips-recorder] rewind transcription start failed:", err);
|
|
2508
2548
|
return null;
|
|
2509
2549
|
});
|
|
@@ -2518,7 +2558,9 @@ async function tryStartRewindFullscreenRecording(
|
|
|
2518
2558
|
void saveTranscriptFailure(TRANSCRIPTION_START_FAILURE);
|
|
2519
2559
|
}
|
|
2520
2560
|
};
|
|
2521
|
-
await invoke("show_preparing")
|
|
2561
|
+
await guardRecordingStart(invoke("show_preparing"), {
|
|
2562
|
+
signal: params.signal,
|
|
2563
|
+
});
|
|
2522
2564
|
const recordingPromise = localOnly
|
|
2523
2565
|
? Promise.resolve<{ id: string; uploadMode: UploadMode }>({
|
|
2524
2566
|
id: folderName,
|
|
@@ -2542,6 +2584,7 @@ async function tryStartRewindFullscreenRecording(
|
|
|
2542
2584
|
// fail with 409 after the local encode has completed.
|
|
2543
2585
|
requestStreaming: true,
|
|
2544
2586
|
streamingUploadClient: "desktop-native",
|
|
2587
|
+
signal: params.signal,
|
|
2545
2588
|
},
|
|
2546
2589
|
);
|
|
2547
2590
|
})();
|
|
@@ -2549,18 +2592,22 @@ async function tryStartRewindFullscreenRecording(
|
|
|
2549
2592
|
const recording = await prepareRewindRecordingStart({
|
|
2550
2593
|
async prepare() {
|
|
2551
2594
|
const preparedRecording = await recordingPromise;
|
|
2595
|
+
throwIfRecordingStartAborted(params.signal);
|
|
2552
2596
|
id = preparedRecording.id;
|
|
2553
2597
|
uploadMode = preparedRecording.uploadMode ?? "buffered";
|
|
2554
|
-
await
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2598
|
+
await guardRecordingStart(
|
|
2599
|
+
invoke<RewindClipBackendStatus>("rewind_clip_prepare", {
|
|
2600
|
+
artifactLabel: id,
|
|
2601
|
+
serverUrl: localOnly ? null : params.serverUrl,
|
|
2602
|
+
recordingId: localOnly ? null : id,
|
|
2603
|
+
authToken: localOnly ? null : (params.authToken ?? ""),
|
|
2604
|
+
cookie: localOnly ? null : (params.cookie ?? ""),
|
|
2605
|
+
includeMic,
|
|
2606
|
+
includeSystemAudio,
|
|
2607
|
+
hasCamera: wantsCamera,
|
|
2608
|
+
}),
|
|
2609
|
+
{ signal: params.signal },
|
|
2610
|
+
);
|
|
2564
2611
|
return preparedRecording;
|
|
2565
2612
|
},
|
|
2566
2613
|
async countdown() {
|
|
@@ -2569,14 +2616,17 @@ async function tryStartRewindFullscreenRecording(
|
|
|
2569
2616
|
// Overlap whisper startup with the countdown so the capture boundary
|
|
2570
2617
|
// never waits on it, but have both settled before activation.
|
|
2571
2618
|
await Promise.all([
|
|
2572
|
-
runRecordingCountdown(true),
|
|
2619
|
+
runRecordingCountdown(true, params.signal),
|
|
2573
2620
|
startRewindTranscription(),
|
|
2574
2621
|
]);
|
|
2575
2622
|
console.log("[rewind-latency] countdown completed");
|
|
2576
2623
|
},
|
|
2577
2624
|
async activate(preparedRecording) {
|
|
2578
2625
|
const activationStarted = performance.now();
|
|
2579
|
-
await
|
|
2626
|
+
await guardRecordingStart(
|
|
2627
|
+
invoke<RewindClipBackendStatus>("rewind_clip_start"),
|
|
2628
|
+
{ signal: params.signal },
|
|
2629
|
+
);
|
|
2580
2630
|
console.log(
|
|
2581
2631
|
`[rewind-latency] countdown completion to start acknowledgement ${Math.round(performance.now() - activationStarted)}ms`,
|
|
2582
2632
|
);
|
|
@@ -2913,8 +2963,13 @@ async function tryStartRewindFullscreenRecording(
|
|
|
2913
2963
|
console.error("[clips-recorder] Rewind handle.cancel() threw:", error);
|
|
2914
2964
|
});
|
|
2915
2965
|
}),
|
|
2966
|
+
listen("clips:toolbar-ready", () => {
|
|
2967
|
+
emit("clips:toolbar-enabled", !stopped).catch(() => {});
|
|
2968
|
+
emitState();
|
|
2969
|
+
}),
|
|
2916
2970
|
]);
|
|
2917
2971
|
tickHandle = window.setInterval(emitState, 500);
|
|
2972
|
+
emit("clips:toolbar-sync").catch(() => {});
|
|
2918
2973
|
emit("clips:toolbar-enabled", true).catch(() => {});
|
|
2919
2974
|
emitState();
|
|
2920
2975
|
return handle;
|
|
@@ -2965,14 +3020,28 @@ async function startNativeFullscreenRecording(
|
|
|
2965
3020
|
};
|
|
2966
3021
|
const startNativeTranscriptionBeforeRecording = async () => {
|
|
2967
3022
|
if (localOnly || !canTranscribeLocally || transcriptionCapture) return;
|
|
2968
|
-
|
|
3023
|
+
throwIfRecordingStartAborted(params.signal);
|
|
3024
|
+
transcriptionCapture = await guardRecordingStart(
|
|
3025
|
+
startTranscriptionCapture(
|
|
3026
|
+
{
|
|
3027
|
+
deviceId: params.micId,
|
|
3028
|
+
label: micDeviceLabel,
|
|
3029
|
+
},
|
|
3030
|
+
wantsSystemAudio,
|
|
3031
|
+
{ voiceProcessing: false },
|
|
3032
|
+
),
|
|
2969
3033
|
{
|
|
2970
|
-
|
|
2971
|
-
|
|
3034
|
+
signal: params.signal,
|
|
3035
|
+
onLateResolve: (capture) => {
|
|
3036
|
+
void capture?.cancel().catch(() => {});
|
|
3037
|
+
},
|
|
2972
3038
|
},
|
|
2973
|
-
wantsSystemAudio,
|
|
2974
|
-
{ voiceProcessing: false },
|
|
2975
3039
|
);
|
|
3040
|
+
if (params.signal?.aborted) {
|
|
3041
|
+
await transcriptionCapture?.cancel().catch(() => {});
|
|
3042
|
+
transcriptionCapture = null;
|
|
3043
|
+
throw new RecordingStartCancelledError();
|
|
3044
|
+
}
|
|
2976
3045
|
if (
|
|
2977
3046
|
canTranscribeLocally &&
|
|
2978
3047
|
!transcriptionCapture &&
|
|
@@ -2983,6 +3052,7 @@ async function startNativeFullscreenRecording(
|
|
|
2983
3052
|
};
|
|
2984
3053
|
|
|
2985
3054
|
try {
|
|
3055
|
+
throwIfRecordingStartAborted(params.signal);
|
|
2986
3056
|
await invoke("park_popover_offscreen").catch(() => {});
|
|
2987
3057
|
emit("clips:popover-visible", false).catch(() => {});
|
|
2988
3058
|
|
|
@@ -3033,22 +3103,34 @@ async function startNativeFullscreenRecording(
|
|
|
3033
3103
|
// current device name.
|
|
3034
3104
|
if (wantsAudio && params.micId) {
|
|
3035
3105
|
try {
|
|
3036
|
-
const probe = await
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
3106
|
+
const probe = await guardRecordingStart(
|
|
3107
|
+
navigator.mediaDevices.getUserMedia({
|
|
3108
|
+
audio: {
|
|
3109
|
+
deviceId: { exact: params.micId },
|
|
3110
|
+
echoCancellation: false,
|
|
3111
|
+
noiseSuppression: false,
|
|
3112
|
+
autoGainControl: false,
|
|
3113
|
+
},
|
|
3114
|
+
video: false,
|
|
3115
|
+
}),
|
|
3116
|
+
{
|
|
3117
|
+
signal: params.signal,
|
|
3118
|
+
onLateResolve: stopMediaStream,
|
|
3042
3119
|
},
|
|
3043
|
-
|
|
3044
|
-
});
|
|
3120
|
+
);
|
|
3045
3121
|
const liveLabel = probe.getAudioTracks()[0]?.label?.trim();
|
|
3046
|
-
probe
|
|
3122
|
+
stopMediaStream(probe);
|
|
3047
3123
|
if (liveLabel) micDeviceLabel = liveLabel;
|
|
3048
3124
|
console.log(
|
|
3049
3125
|
`[clips-recorder] mic resolve: id=${params.micId} storedLabel=${JSON.stringify(params.micLabel ?? null)} liveLabel=${JSON.stringify(liveLabel ?? null)} -> using=${JSON.stringify(micDeviceLabel)}`,
|
|
3050
3126
|
);
|
|
3051
3127
|
} catch (probeErr) {
|
|
3128
|
+
if (
|
|
3129
|
+
params.signal?.aborted ||
|
|
3130
|
+
probeErr instanceof RecordingStartTimeoutError
|
|
3131
|
+
) {
|
|
3132
|
+
throw probeErr;
|
|
3133
|
+
}
|
|
3052
3134
|
// Probe failed (rotated/stale deviceId, device unplugged, or denied) —
|
|
3053
3135
|
// fall back to the stored label, which the Rust side name-matches.
|
|
3054
3136
|
console.warn(
|
|
@@ -3070,17 +3152,26 @@ async function startNativeFullscreenRecording(
|
|
|
3070
3152
|
// attach the recording output at the exact start moment. No-op when SCK is
|
|
3071
3153
|
// unavailable — `begin` then does a normal immediate start.
|
|
3072
3154
|
const warmMic = (recordingId: string) =>
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3155
|
+
guardRecordingStart(
|
|
3156
|
+
invoke("native_fullscreen_recording_warm", {
|
|
3157
|
+
recordingId,
|
|
3158
|
+
...captureAudioParams,
|
|
3159
|
+
}),
|
|
3160
|
+
{ signal: params.signal },
|
|
3161
|
+
).catch((err) => {
|
|
3162
|
+
if (
|
|
3163
|
+
params.signal?.aborted ||
|
|
3164
|
+
err instanceof RecordingStartTimeoutError
|
|
3165
|
+
) {
|
|
3166
|
+
throw err;
|
|
3167
|
+
}
|
|
3077
3168
|
console.warn("[clips-recorder] mic warm failed:", err);
|
|
3078
3169
|
});
|
|
3079
3170
|
const clickStartedAt = Date.now();
|
|
3080
3171
|
if (localOnly) {
|
|
3081
3172
|
// Local recordings have no create-recording round-trip; still overlap
|
|
3082
3173
|
// Whisper startup with countdown + deferred SCK warm.
|
|
3083
|
-
const countdownPromise = runRecordingCountdown(true);
|
|
3174
|
+
const countdownPromise = runRecordingCountdown(true, params.signal);
|
|
3084
3175
|
id = localFolderName;
|
|
3085
3176
|
const warmPromise = (async () => {
|
|
3086
3177
|
const warmStartedAt = Date.now();
|
|
@@ -3105,7 +3196,7 @@ async function startNativeFullscreenRecording(
|
|
|
3105
3196
|
mode: params.mode,
|
|
3106
3197
|
source: params.source,
|
|
3107
3198
|
});
|
|
3108
|
-
const countdownPromise = runRecordingCountdown(true);
|
|
3199
|
+
const countdownPromise = runRecordingCountdown(true, params.signal);
|
|
3109
3200
|
const recordingPromise = (async () => {
|
|
3110
3201
|
const captureTitle = await captureTitlePromise;
|
|
3111
3202
|
const createStartedAt = Date.now();
|
|
@@ -3119,6 +3210,7 @@ async function startNativeFullscreenRecording(
|
|
|
3119
3210
|
mimeType: NATIVE_FULLSCREEN_MIME_TYPE,
|
|
3120
3211
|
requestStreaming: true,
|
|
3121
3212
|
streamingUploadClient: "desktop-native",
|
|
3213
|
+
signal: params.signal,
|
|
3122
3214
|
},
|
|
3123
3215
|
);
|
|
3124
3216
|
} finally {
|
|
@@ -3174,19 +3266,23 @@ async function startNativeFullscreenRecording(
|
|
|
3174
3266
|
}
|
|
3175
3267
|
|
|
3176
3268
|
await audioCue.playBeforeCapture();
|
|
3269
|
+
throwIfRecordingStartAborted(params.signal);
|
|
3177
3270
|
// Phase 2: attach the recording output now that the mic is warm (or do a
|
|
3178
3271
|
// normal immediate start if warming was skipped/failed). Transcription has
|
|
3179
3272
|
// already been awaited above so AVAudioEngine won't reconfigure mid-write.
|
|
3180
3273
|
const beginStartedAt = Date.now();
|
|
3181
|
-
await
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3274
|
+
await guardRecordingStart(
|
|
3275
|
+
invoke("native_fullscreen_recording_begin", {
|
|
3276
|
+
recordingId: id,
|
|
3277
|
+
...captureAudioParams,
|
|
3278
|
+
// Live-upload credentials are read on the Rust side from the shared
|
|
3279
|
+
// meetings-watcher session; only signal whether this is a local-only
|
|
3280
|
+
// recording (which never uploads to the server).
|
|
3281
|
+
localOnly,
|
|
3282
|
+
hasCamera: wantsCamera,
|
|
3283
|
+
}),
|
|
3284
|
+
{ signal: params.signal },
|
|
3285
|
+
);
|
|
3190
3286
|
console.log(
|
|
3191
3287
|
`[clips-recorder] native begin durationMs=${Date.now() - beginStartedAt} clickToLiveMs=${Date.now() - clickStartedAt}`,
|
|
3192
3288
|
);
|
|
@@ -3705,10 +3801,15 @@ async function startNativeFullscreenRecording(
|
|
|
3705
3801
|
console.error("[clips-recorder] native handle.cancel() threw:", err);
|
|
3706
3802
|
});
|
|
3707
3803
|
}),
|
|
3804
|
+
listen("clips:toolbar-ready", () => {
|
|
3805
|
+
emit("clips:toolbar-enabled", !stopped).catch(() => {});
|
|
3806
|
+
emitState();
|
|
3807
|
+
}),
|
|
3708
3808
|
]);
|
|
3709
3809
|
stateUnlistens = toolbarUnlistens;
|
|
3710
3810
|
tickHandle = setInterval(emitState, 500);
|
|
3711
3811
|
startSegmentRotator();
|
|
3812
|
+
emit("clips:toolbar-sync").catch(() => {});
|
|
3712
3813
|
emit("clips:toolbar-enabled", true).catch(() => {});
|
|
3713
3814
|
emitState();
|
|
3714
3815
|
|
|
@@ -3816,8 +3917,28 @@ function bubbleSizeRatioForName(size: string | null | undefined): number {
|
|
|
3816
3917
|
export async function startRecording(
|
|
3817
3918
|
params: StartParams,
|
|
3818
3919
|
): Promise<RecorderHandle> {
|
|
3920
|
+
const startController = new AbortController();
|
|
3921
|
+
const cancelStartup = () => {
|
|
3922
|
+
startController.abort();
|
|
3923
|
+
void emit("clips:countdown-cancel").catch(() => {});
|
|
3924
|
+
void invoke("native_fullscreen_recording_cancel").catch(() => {});
|
|
3925
|
+
void invoke("hide_recording_chrome").catch(() => {});
|
|
3926
|
+
};
|
|
3927
|
+
const startPromise = startRecordingInner({
|
|
3928
|
+
...params,
|
|
3929
|
+
signal: startController.signal,
|
|
3930
|
+
});
|
|
3819
3931
|
try {
|
|
3820
|
-
return await
|
|
3932
|
+
return await guardRecordingStart(startPromise, {
|
|
3933
|
+
signal: params.signal,
|
|
3934
|
+
timeoutMs: RECORDING_START_TIMEOUT_MS,
|
|
3935
|
+
onCancel: cancelStartup,
|
|
3936
|
+
onLateResolve: (handle) => {
|
|
3937
|
+
void handle.cancel().catch((err) => {
|
|
3938
|
+
console.warn("[clips-recorder] late start cleanup failed:", err);
|
|
3939
|
+
});
|
|
3940
|
+
},
|
|
3941
|
+
});
|
|
3821
3942
|
} catch (err) {
|
|
3822
3943
|
await invoke("hide_recording_chrome").catch(() => {});
|
|
3823
3944
|
const e = err as { name?: string; message?: string } | null;
|
|
@@ -4028,21 +4149,72 @@ async function startRecordingInner(
|
|
|
4028
4149
|
? getAudioStreamWithFallback(params.micId, params.micLabel)
|
|
4029
4150
|
: null;
|
|
4030
4151
|
|
|
4152
|
+
// getDisplayMedia can remain pending in a long-lived WebKit tray webview.
|
|
4153
|
+
// Track every stream that does arrive so cancel/timeout can stop it even
|
|
4154
|
+
// when another capture promise never settles.
|
|
4155
|
+
const pendingCaptureStreams = new Set<MediaStream>();
|
|
4156
|
+
const trackCapturePromise = (
|
|
4157
|
+
promise: Promise<MediaStream> | null,
|
|
4158
|
+
): Promise<MediaStream> | null => {
|
|
4159
|
+
if (!promise) return null;
|
|
4160
|
+
return promise.then((stream) => {
|
|
4161
|
+
if (params.signal?.aborted) {
|
|
4162
|
+
stopMediaStream(stream);
|
|
4163
|
+
} else {
|
|
4164
|
+
pendingCaptureStreams.add(stream);
|
|
4165
|
+
}
|
|
4166
|
+
return stream;
|
|
4167
|
+
});
|
|
4168
|
+
};
|
|
4169
|
+
const trackedDisplayStreamPromise = trackCapturePromise(displayStreamPromise);
|
|
4170
|
+
const trackedCameraStreamPromise = trackCapturePromise(
|
|
4171
|
+
bubbleCameraStreamPromise,
|
|
4172
|
+
);
|
|
4173
|
+
const trackedAudioStreamPromise = trackCapturePromise(audioStreamPromise);
|
|
4174
|
+
const pendingCaptureAbort = () => {
|
|
4175
|
+
pendingCaptureStreams.forEach(stopMediaStream);
|
|
4176
|
+
pendingCaptureStreams.clear();
|
|
4177
|
+
};
|
|
4178
|
+
params.signal?.addEventListener("abort", pendingCaptureAbort, {
|
|
4179
|
+
once: true,
|
|
4180
|
+
});
|
|
4181
|
+
const clearPendingCaptureTracking = () => {
|
|
4182
|
+
params.signal?.removeEventListener("abort", pendingCaptureAbort);
|
|
4183
|
+
pendingCaptureStreams.clear();
|
|
4184
|
+
};
|
|
4185
|
+
let captureStartCleaned = false;
|
|
4186
|
+
const cleanupUnstartedCapture = () => {
|
|
4187
|
+
if (captureStartCleaned) return;
|
|
4188
|
+
captureStartCleaned = true;
|
|
4189
|
+
pendingCaptureAbort();
|
|
4190
|
+
clearPendingCaptureTracking();
|
|
4191
|
+
streamCleanups.forEach((cleanup) => cleanup());
|
|
4192
|
+
};
|
|
4193
|
+
const trackedCapturePromises = [
|
|
4194
|
+
trackedDisplayStreamPromise,
|
|
4195
|
+
trackedCameraStreamPromise,
|
|
4196
|
+
trackedAudioStreamPromise,
|
|
4197
|
+
];
|
|
4198
|
+
|
|
4031
4199
|
let captureSuspension: RewindCaptureSuspensionLease;
|
|
4032
4200
|
try {
|
|
4033
|
-
captureSuspension = await suspensionPromise
|
|
4201
|
+
captureSuspension = await guardRecordingStart(suspensionPromise, {
|
|
4202
|
+
signal: params.signal,
|
|
4203
|
+
onLateResolve: (lease) => {
|
|
4204
|
+
if (!lease.leaseId) return;
|
|
4205
|
+
void invoke("rewind_capture_suspension_release", {
|
|
4206
|
+
leaseId: lease.leaseId,
|
|
4207
|
+
}).catch(() => {});
|
|
4208
|
+
},
|
|
4209
|
+
});
|
|
4034
4210
|
} catch (err) {
|
|
4035
4211
|
// A picker may already have resolved while IPC was in flight. Tear down
|
|
4036
4212
|
// every possible stream before surfacing the fail-closed suspension error.
|
|
4037
|
-
|
|
4038
|
-
|
|
4039
|
-
|
|
4040
|
-
|
|
4041
|
-
|
|
4042
|
-
streams.forEach((result) => {
|
|
4043
|
-
if (result.status === "fulfilled") {
|
|
4044
|
-
result.value?.getTracks().forEach((track) => track.stop());
|
|
4045
|
-
}
|
|
4213
|
+
cleanupUnstartedCapture();
|
|
4214
|
+
void Promise.allSettled(trackedCapturePromises).then((streams) => {
|
|
4215
|
+
streams.forEach((result) => {
|
|
4216
|
+
if (result.status === "fulfilled") stopMediaStream(result.value);
|
|
4217
|
+
});
|
|
4046
4218
|
});
|
|
4047
4219
|
throw err;
|
|
4048
4220
|
}
|
|
@@ -4055,11 +4227,10 @@ async function startRecordingInner(
|
|
|
4055
4227
|
// track that DID resolve, then re-throw the original error so the caller's
|
|
4056
4228
|
// catch still sees `NotAllowedError` / `AbortError` as before.
|
|
4057
4229
|
console.log("[clips-recorder] allSettled IN — streams dispatched");
|
|
4058
|
-
const settled = await
|
|
4059
|
-
|
|
4060
|
-
|
|
4061
|
-
|
|
4062
|
-
]);
|
|
4230
|
+
const settled = await guardRecordingStart(
|
|
4231
|
+
Promise.allSettled(trackedCapturePromises),
|
|
4232
|
+
{ signal: params.signal },
|
|
4233
|
+
);
|
|
4063
4234
|
console.log(
|
|
4064
4235
|
"[clips-recorder] allSettled OUT — settled statuses:",
|
|
4065
4236
|
settled.map((s) => s.status),
|
|
@@ -4235,7 +4406,10 @@ async function startRecordingInner(
|
|
|
4235
4406
|
combined,
|
|
4236
4407
|
});
|
|
4237
4408
|
|
|
4238
|
-
const countdownPromise = runRecordingCountdown(
|
|
4409
|
+
const countdownPromise = runRecordingCountdown(
|
|
4410
|
+
wantsScreen,
|
|
4411
|
+
params.signal,
|
|
4412
|
+
);
|
|
4239
4413
|
const localExportPromise = prepareLocalRecordingExport(targets);
|
|
4240
4414
|
let localExport: Awaited<ReturnType<typeof prepareLocalRecordingExport>>;
|
|
4241
4415
|
try {
|
|
@@ -4244,13 +4418,7 @@ async function startRecordingInner(
|
|
|
4244
4418
|
localExportPromise,
|
|
4245
4419
|
]);
|
|
4246
4420
|
} catch (err) {
|
|
4247
|
-
|
|
4248
|
-
stream?.getTracks().forEach((track) => track.stop()),
|
|
4249
|
-
);
|
|
4250
|
-
streamCleanups.forEach((cleanup) => cleanup());
|
|
4251
|
-
if (!popoverOwnsCamera) {
|
|
4252
|
-
bubbleCameraStream?.getTracks().forEach((track) => track.stop());
|
|
4253
|
-
}
|
|
4421
|
+
cleanupUnstartedCapture();
|
|
4254
4422
|
throw err;
|
|
4255
4423
|
}
|
|
4256
4424
|
|
|
@@ -4268,7 +4436,10 @@ async function startRecordingInner(
|
|
|
4268
4436
|
function emitState(paused: boolean) {
|
|
4269
4437
|
const now = Date.now();
|
|
4270
4438
|
const pausedNowMs = paused && pausedAt ? now - pausedAt : 0;
|
|
4271
|
-
const elapsedMs =
|
|
4439
|
+
const elapsedMs =
|
|
4440
|
+
startedAt > 0
|
|
4441
|
+
? now - startedAt - accumulatedPauseMs - pausedNowMs
|
|
4442
|
+
: 0;
|
|
4272
4443
|
emit("clips:recorder-state", {
|
|
4273
4444
|
paused,
|
|
4274
4445
|
elapsedMs,
|
|
@@ -4299,8 +4470,15 @@ async function startRecordingInner(
|
|
|
4299
4470
|
console.error("[clips-recorder] local handle.cancel() threw:", err);
|
|
4300
4471
|
});
|
|
4301
4472
|
}),
|
|
4473
|
+
listen("clips:toolbar-ready", () => {
|
|
4474
|
+
emit("clips:toolbar-enabled", startedAt > 0 && !stopped).catch(
|
|
4475
|
+
() => {},
|
|
4476
|
+
);
|
|
4477
|
+
emitState(pausedAt != null);
|
|
4478
|
+
}),
|
|
4302
4479
|
]);
|
|
4303
4480
|
stateUnlistens = toolbarUnlistens;
|
|
4481
|
+
emit("clips:toolbar-sync").catch(() => {});
|
|
4304
4482
|
|
|
4305
4483
|
await showRegionGuidesForRecording(wantsScreen);
|
|
4306
4484
|
await audioCue.playBeforeCapture();
|
|
@@ -4413,7 +4591,12 @@ async function startRecordingInner(
|
|
|
4413
4591
|
},
|
|
4414
4592
|
};
|
|
4415
4593
|
|
|
4416
|
-
|
|
4594
|
+
const wrappedHandle = recorderWithCaptureSuspension(
|
|
4595
|
+
handle,
|
|
4596
|
+
releaseCaptureSuspension,
|
|
4597
|
+
);
|
|
4598
|
+
clearPendingCaptureTracking();
|
|
4599
|
+
return wrappedHandle;
|
|
4417
4600
|
}
|
|
4418
4601
|
|
|
4419
4602
|
const uploadPrimaryVideo = createUploadOptimizedVideoStream(primaryVideo);
|
|
@@ -4443,14 +4626,18 @@ async function startRecordingInner(
|
|
|
4443
4626
|
console.log(
|
|
4444
4627
|
"[clips-recorder] invoking show_countdown + createServerRecording",
|
|
4445
4628
|
);
|
|
4446
|
-
const countdownPromise = runRecordingCountdown(wantsScreen);
|
|
4629
|
+
const countdownPromise = runRecordingCountdown(wantsScreen, params.signal);
|
|
4447
4630
|
console.time("[clips-recorder] createServerRecording duration");
|
|
4448
4631
|
const recordingPromise = createServerRecording(
|
|
4449
4632
|
params.serverUrl,
|
|
4450
4633
|
wantsCamera,
|
|
4451
4634
|
recordingAudio.tracks.length > 0,
|
|
4452
4635
|
captureTitle,
|
|
4453
|
-
{
|
|
4636
|
+
{
|
|
4637
|
+
mimeType: mimeType || "video/webm",
|
|
4638
|
+
requestStreaming: true,
|
|
4639
|
+
signal: params.signal,
|
|
4640
|
+
},
|
|
4454
4641
|
).finally(() => {
|
|
4455
4642
|
console.timeEnd("[clips-recorder] createServerRecording duration");
|
|
4456
4643
|
});
|
|
@@ -4664,7 +4851,8 @@ async function startRecordingInner(
|
|
|
4664
4851
|
function emitState(paused: boolean) {
|
|
4665
4852
|
const now = Date.now();
|
|
4666
4853
|
const pausedNowMs = paused && pausedAt ? now - pausedAt : 0;
|
|
4667
|
-
const elapsedMs =
|
|
4854
|
+
const elapsedMs =
|
|
4855
|
+
startedAt > 0 ? now - startedAt - accumulatedPauseMs - pausedNowMs : 0;
|
|
4668
4856
|
emit("clips:recorder-state", {
|
|
4669
4857
|
paused,
|
|
4670
4858
|
elapsedMs,
|
|
@@ -4716,8 +4904,15 @@ async function startRecordingInner(
|
|
|
4716
4904
|
console.error("[clips-recorder] handle.cancel() threw:", err);
|
|
4717
4905
|
});
|
|
4718
4906
|
}),
|
|
4907
|
+
listen("clips:toolbar-ready", () => {
|
|
4908
|
+
emit("clips:toolbar-enabled", startedAt > 0 && !stopped).catch(
|
|
4909
|
+
() => {},
|
|
4910
|
+
);
|
|
4911
|
+
emitState(pausedAt != null);
|
|
4912
|
+
}),
|
|
4719
4913
|
]);
|
|
4720
4914
|
stateUnlistens = toolbarUnlistens;
|
|
4915
|
+
emit("clips:toolbar-sync").catch(() => {});
|
|
4721
4916
|
|
|
4722
4917
|
await showRegionGuidesForRecording(wantsScreen);
|
|
4723
4918
|
await audioCue.playBeforeCapture();
|
|
@@ -5228,8 +5423,14 @@ async function startRecordingInner(
|
|
|
5228
5423
|
},
|
|
5229
5424
|
};
|
|
5230
5425
|
|
|
5231
|
-
|
|
5426
|
+
const wrappedHandle = recorderWithCaptureSuspension(
|
|
5427
|
+
handle,
|
|
5428
|
+
releaseCaptureSuspension,
|
|
5429
|
+
);
|
|
5430
|
+
clearPendingCaptureTracking();
|
|
5431
|
+
return wrappedHandle;
|
|
5232
5432
|
} catch (err) {
|
|
5433
|
+
cleanupUnstartedCapture();
|
|
5233
5434
|
await releaseCaptureSuspension();
|
|
5234
5435
|
throw err;
|
|
5235
5436
|
}
|